diff --git a/icons/hicolor/scalable/actions/tool-select-symbolic.svg b/icons/hicolor/scalable/actions/tool-select-symbolic.svg new file mode 100644 index 0000000..8755b1d --- /dev/null +++ b/icons/hicolor/scalable/actions/tool-select-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/shaders/selection.frag.glsl b/shaders/selection.frag.glsl new file mode 100644 index 0000000..be04a82 --- /dev/null +++ b/shaders/selection.frag.glsl @@ -0,0 +1,39 @@ +#version 320 es +precision mediump float; + +in vec2 vPos; +out vec4 FragColor; + +uniform float uTime; +uniform vec4 uColor1; +uniform vec4 uColor2; +uniform vec2 uMin; +uniform vec2 uMax; + +void main() +{ + float borderWidth = 0.008; + + + float distX = min(vPos.x - uMin.x, uMax.x - vPos.x); + float distY = min(vPos.y - uMin.y, uMax.y - vPos.y); + float dist = min(distX, distY); + + if (dist > borderWidth) + discard; + + float dash_length = 0.025; + float gap_length = 0.015; + float total = dash_length + gap_length; + + float speed = 0.3; + + float distance_along = (vPos.x + vPos.y) * 20.0; + + float t = mod( distance_along * total + uTime * speed, total); + + if (t < dash_length) + FragColor = uColor2; + else + FragColor = uColor1; +} \ No newline at end of file diff --git a/shaders/triangle.vert.glsl b/shaders/triangle.vert.glsl index 369e4ab..0e55851 100644 --- a/shaders/triangle.vert.glsl +++ b/shaders/triangle.vert.glsl @@ -7,9 +7,11 @@ layout (location = 1) in vec4 aColor; uniform mat4 uProjection; out vec4 vColor; +out vec2 vPos; void main() { gl_Position = uProjection * vec4(aPos, 0.0, 1.0); + vPos = aPos; vColor = aColor; } diff --git a/src/application/applicationstate.c b/src/application/applicationstate.c index 228dd86..3993b0d 100644 --- a/src/application/applicationstate.c +++ b/src/application/applicationstate.c @@ -22,10 +22,16 @@ static void appstate_set_tool(GtkButton* button, gpointer user_data) { data->state->selectedTool = data->tool; // setting tool makes the sub-tools menu to close - gtk_revealer_set_reveal_child(data->revealer, FALSE); + // (ADD NEW REVEALERS HERE) + gtk_revealer_set_reveal_child( + data->state->widgetState->workspaceRevealerShapes, + FALSE + ); // setting tool also resets selected shape - data->state->selectedShape = NULL; + // NOTE: isn't needed anymore, as you would + // want to be able to select & edit existing shapes + //data->state->selectedShape = NULL; } static void appstate_reveal_subtools(GtkButton* button, gpointer user_data) { @@ -56,8 +62,8 @@ static void appstate_on_color_change(VektorColorWheel* wheel, gtk_editable_set_text(GTK_EDITABLE(appstate->widgetState->sidepanelEntryB), str_b); - gtk_gl_area_queue_render( - GTK_GL_AREA(appstate->widgetState->workspaceCanvas)); + /*gtk_gl_area_queue_render( + GTK_GL_AREA(appstate->widgetState->workspaceCanvas));*/ } static void appstate_on_entry_update(GtkEntry* entry, gpointer user_data) { @@ -69,7 +75,6 @@ static void appstate_on_entry_update(GtkEntry* entry, gpointer user_data) { unsigned char b = (unsigned char)atoi( gtk_editable_get_text(GTK_EDITABLE(widgetState->sidepanelEntryB))); - g_print("%d", r); vektor_color_wheel_set_color( VEKTOR_COLOR_WHEEL(widgetState->workspaceColorPicker), (VektorColor){.r = r, .g = g, .b = b}); @@ -94,7 +99,8 @@ static void canvas_onclick(GtkGestureClick* gesture, int n_press, double x, vektor_appstate_canvas_click(state, normalized_coords.x, normalized_coords.y); - gtk_gl_area_queue_render(GTK_GL_AREA(widget)); + + //gtk_gl_area_queue_render(GTK_GL_AREA(widget)); } void vektor_appstate_canvas_click(VektorAppState* state, double x, double y) { @@ -152,6 +158,43 @@ begin_click_dispatch: vektor_polygon_add_point(state->selectedShape->primitive.polygon, pos); vektor_shapes_update_bbox(state->shapeBuffer); } + else if (state->selectedTool == VektorRectangleTool) { + + VektorRectangle* rect = vektor_rectangle_new(); + VektorPrimitive rectPrimitive = + (VektorPrimitive){.kind = VEKTOR_RECTANGLE, .rectangle = *rect}; + VektorStyle style = (VektorStyle){ + .stroke_color = state->currentColor, .stroke_width = 0.01}; + vektor_shapebuffer_add_shape( + state->shapeBuffer, vektor_shape_new(rectPrimitive, style, 0)); + + state->selectedShape = + &(state->shapeBuffer->shapes[state->shapeBuffer->count - 1]); + + vektor_rectangle_free(rect); + + + vektor_rectangle_set_start(&state->selectedShape->primitive.rectangle, pos); + vektor_rectangle_set_end( + &state->selectedShape->primitive.rectangle, + vec2_add(pos, (V2){0.1f,0.1f}) + ); + //state->selectedShape = NULL; + vektor_shapes_update_bbox(state->shapeBuffer); + } + else if (state->selectedTool == VektorSelectionTool) { + for(size_t i = 0; i < state->shapeBuffer->count; i++) { + VektorBBox bbox = + vektor_primitive_get_bbox(state->shapeBuffer->shapes[i].primitive); + if(vektor_bbox_isinside(bbox, pos)) { + state->selectedShape = &(state->shapeBuffer->shapes[i]); + g_print("%d", state->selectedShape == NULL); + return; + } + } + // was clicked outside any shapes - reset selection + state->selectedShape = NULL; + } } void vektor_appstate_new(VektorWidgetState* wstate, VektorAppState* stateOut) { @@ -165,24 +208,41 @@ void vektor_appstate_new(VektorWidgetState* wstate, VektorAppState* stateOut) { data_polygontool->tool = VektorPolygonTool; data_polygontool->revealer = wstate->workspaceRevealerShapes; + button_tool_set_data* data_rectangletool = malloc(sizeof(button_tool_set_data)); + data_rectangletool->state = stateOut; + data_rectangletool->tool = VektorRectangleTool; + data_rectangletool->revealer = wstate->workspaceRevealerShapes; + + button_tool_set_data* data_selecttool = malloc(sizeof(button_tool_set_data)); + data_selecttool->state = stateOut; + data_selecttool->tool = VektorSelectionTool; + // populate appstate + stateOut->startupTime = g_get_monotonic_time(); + stateOut->shapeBuffer = malloc(sizeof(VektorShapeBuffer)); *stateOut->shapeBuffer = (VektorShapeBuffer){0}; stateOut->canvas = malloc(sizeof(VektorCanvas)); stateOut->widgetState = wstate; stateOut->currentColor = vektor_color_solid(0, 0, 0); stateOut->selectedShape = NULL; - vektor_canvas_init(wstate, stateOut->canvas, stateOut->shapeBuffer); + VektorCanvasRenderInfo* renderInfo = malloc(sizeof(VektorCanvasRenderInfo)); + renderInfo->selectedShape = &(stateOut->selectedShape); + renderInfo->shapes = stateOut->shapeBuffer; + renderInfo->startupTime = stateOut->startupTime; + vektor_canvas_init(wstate, stateOut->canvas, renderInfo); // link all the buttons - g_signal_connect(G_OBJECT(wstate->workspaceButtonLinetool), "clicked", + g_signal_connect(G_OBJECT(wstate->workspaceButtonLineTool), "clicked", G_CALLBACK(appstate_set_tool), data_linetool); - g_signal_connect(G_OBJECT(wstate->workspaceButtonRecttool), "clicked", + g_signal_connect(G_OBJECT(wstate->workspaceButtonRectTool), "clicked", + G_CALLBACK(appstate_set_tool), data_rectangletool); + g_signal_connect(G_OBJECT(wstate->workspaceButtonCircleTool), "clicked", G_CALLBACK(appstate_set_tool), data_linetool); - g_signal_connect(G_OBJECT(wstate->workspaceButtonCircletool), "clicked", - G_CALLBACK(appstate_set_tool), data_linetool); - g_signal_connect(G_OBJECT(wstate->workspaceButtonPolygontool), "clicked", + g_signal_connect(G_OBJECT(wstate->workspaceButtonPolygonTool), "clicked", G_CALLBACK(appstate_set_tool), data_polygontool); + g_signal_connect(G_OBJECT(wstate->workspaceButtonSelectionTool), "clicked", + G_CALLBACK(appstate_set_tool), data_selecttool); // hook subtool revealers to their master buttons g_signal_connect(G_OBJECT(wstate->workspaceButtonMasterShapes), "clicked", diff --git a/src/application/applicationstate.h b/src/application/applicationstate.h index a822662..c4d3a08 100644 --- a/src/application/applicationstate.h +++ b/src/application/applicationstate.h @@ -6,12 +6,16 @@ #include "../ui/vektorcanvas.h" #include "src/core/raster.h" -typedef enum VektorAppTool { +typedef enum VektorAppTool { + VektorSelectionTool, VektorLineTool, VektorPolygonTool, + VektorRectangleTool } VektorAppTool; typedef struct VektorAppState { + gint64 startupTime; + VektorWidgetState* widgetState; VektorAppTool selectedTool; diff --git a/src/core/primitives.c b/src/core/primitives.c index f72453d..08185ab 100644 --- a/src/core/primitives.c +++ b/src/core/primitives.c @@ -49,6 +49,24 @@ void vektor_polygon_free(VektorPolygon* pg) { free(pg); } +VektorRectangle* vektor_rectangle_new(void) { + VektorRectangle* rct = malloc(sizeof(VektorRectangle)); + rct->start = (V2){.x = 0, .y = 0}; + rct->end = (V2){.x = 0, .y = 0}; + return rct; +} + +void vektor_rectangle_set_end(VektorRectangle* rct, V2 point) { + rct->end = point; +} +void vektor_rectangle_set_start(VektorRectangle* rct, V2 point) { + rct->start = point; +} + +void vektor_rectangle_free(VektorRectangle* rct) { + free(rct); +} + void vektor_shapebuffer_add_shape(VektorShapeBuffer* buffer, VektorShape shape) { if (buffer->count >= buffer->capacity) { @@ -59,7 +77,7 @@ void vektor_shapebuffer_add_shape(VektorShapeBuffer* buffer, buffer->shapes[buffer->count++] = shape; } -VektorBBox polyline_mk_bbox(VektorPrimitive prim) { +VektorBBox vektor_polyline_get_bbox(VektorPrimitive prim) { float min_x, max_x, min_y, max_y; for (size_t i = 0; i < prim.polyline->count; i++) { V2 p = prim.polyline->points[i]; @@ -72,27 +90,43 @@ VektorBBox polyline_mk_bbox(VektorPrimitive prim) { return (VektorBBox){(V2){min_x, min_y}, (V2){max_x, max_y}}; } -VektorBBox polygon_mk_bbox(VektorPrimitive prim) { - float min_x, max_x, min_y, max_y; - for (size_t i = 0; i < prim.polygon->count; i++) { +VektorBBox vektor_polygon_get_bbox(VektorPrimitive prim) { + V2 first = prim.polygon->points[0]; + + float min_x = first.x; + float max_x = first.x; + float min_y = first.y; + float max_y = first.y; + + for (size_t i = 1; i < prim.polygon->count; i++) { V2 p = prim.polygon->points[i]; + min_x = fminf(min_x, p.x); min_y = fminf(min_y, p.y); - max_x = fminf(max_x, p.x); - max_y = fminf(max_y, p.y); + max_x = fmaxf(max_x, p.x); + max_y = fmaxf(max_y, p.y); } + return (VektorBBox){(V2){min_x, min_y}, (V2){max_x, max_y}}; } -VektorBBox vektor_mk_bbox(VektorPrimitive prim) { +VektorBBox vektor_rectangle_get_bbox(VektorPrimitive prim) { + return (VektorBBox){prim.rectangle.start, prim.rectangle.end}; +} + +VektorBBox vektor_primitive_get_bbox(VektorPrimitive prim) { switch (prim.kind) { case VEKTOR_POLYLINE: - return polyline_mk_bbox(prim); + return vektor_polyline_get_bbox(prim); break; case VEKTOR_POLYGON: - return polygon_mk_bbox(prim); + return vektor_polygon_get_bbox(prim); + break; + + case VEKTOR_RECTANGLE: + return vektor_rectangle_get_bbox(prim); break; default: @@ -101,13 +135,18 @@ VektorBBox vektor_mk_bbox(VektorPrimitive prim) { } } +bool vektor_bbox_isinside(VektorBBox bbox, V2 point) { + return point.x >= bbox.min.x && point.y >= bbox.min.y + && point.x <= bbox.max.x && point.y <= bbox.max.y; +} + VektorShape vektor_shape_new(VektorPrimitive prim, VektorStyle style, int z_index) { - return (VektorShape){.primitive = prim, .style = style, .z_index = z_index, .bbox=vektor_mk_bbox(prim)}; + return (VektorShape){.primitive = prim, .style = style, .z_index = z_index, .bbox=vektor_primitive_get_bbox(prim)}; } void vektor_shapes_update_bbox(VektorShapeBuffer* buffer) { for (size_t i = 0; i < buffer->count; i++) { - buffer->shapes[i].bbox = vektor_mk_bbox(buffer->shapes[i].primitive); + buffer->shapes[i].bbox = vektor_primitive_get_bbox(buffer->shapes[i].primitive); } } \ No newline at end of file diff --git a/src/core/primitives.h b/src/core/primitives.h index cf5734b..7897116 100644 --- a/src/core/primitives.h +++ b/src/core/primitives.h @@ -23,10 +23,16 @@ typedef struct { double radius; } VektorCircle; +typedef struct { + V2 start; + V2 end; +} VektorRectangle; + typedef enum { VEKTOR_POLYLINE, VEKTOR_POLYGON, - VEKTOR_CIRCLE + VEKTOR_CIRCLE, + VEKTOR_RECTANGLE } VektorPrimitiveKind; typedef struct { @@ -35,6 +41,7 @@ typedef struct { VektorPolyline* polyline; VektorPolygon* polygon; VektorCircle circle; + VektorRectangle rectangle; }; } VektorPrimitive; @@ -46,6 +53,11 @@ VektorPolygon* vektor_polygon_new(void); void vektor_polygon_add_point(VektorPolygon* pl, V2 point); void vektor_polygon_free(VektorPolygon* pl); +VektorRectangle* vektor_rectangle_new(void); +void vektor_rectangle_set_end(VektorRectangle* rct, V2 point); +void vektor_rectangle_set_start(VektorRectangle* rct, V2 point); +void vektor_rectangle_free(VektorRectangle* rct); + typedef struct { VektorColor stroke_color; float stroke_width; @@ -63,10 +75,12 @@ typedef struct { VektorPrimitive primitive; } VektorShape; -VektorBBox polyline_mk_bbox(VektorPrimitive prim); -VektorBBox polygon_mk_bbox(VektorPrimitive prim); +VektorBBox vektor_polyline_get_bbox(VektorPrimitive prim); +VektorBBox vektor_polygon_get_bbox(VektorPrimitive prim); +VektorBBox vektor_rectangle_get_bbox(VektorPrimitive prim); -VektorBBox vektor_mk_bbox(VektorPrimitive prim); +VektorBBox vektor_primitive_get_bbox(VektorPrimitive prim); +bool vektor_bbox_isinside(VektorBBox bbox, V2 point); VektorShape vektor_shape_new(VektorPrimitive prim, VektorStyle style, int z_index); diff --git a/src/core/raster.c b/src/core/raster.c index f7f8a15..02d746d 100644 --- a/src/core/raster.c +++ b/src/core/raster.c @@ -1,6 +1,7 @@ #include "raster.h" #include "epoxy/gl.h" #include "primitives.h" +#include "src/core/vector.h" #include "stddef.h" #include @@ -12,7 +13,7 @@ void vektor_edgebuffer_add_edge(EdgeBuffer* buffer, Edge edge) { buffer->edges[buffer->count++] = edge; } -void vektor_polyline_flatten(EdgeBuffer* buffer, VektorPolyline* line, +void vektor_polyline_tessellate(EdgeBuffer* buffer, VektorPolyline* line, size_t j) { for (size_t i = 0; i + 1 < line->count; i++) { vektor_edgebuffer_add_edge( @@ -20,7 +21,7 @@ void vektor_polyline_flatten(EdgeBuffer* buffer, VektorPolyline* line, } } -void vektor_polygon_flatten(EdgeBuffer* buffer, VektorPolygon* polygon, +void vektor_polygon_tessellate(EdgeBuffer* buffer, VektorPolygon* polygon, size_t j) { for (size_t i = 0; i + 1 < polygon->count; i++) { vektor_edgebuffer_add_edge( @@ -30,6 +31,20 @@ void vektor_polygon_flatten(EdgeBuffer* buffer, VektorPolygon* polygon, buffer, (Edge){polygon->points[polygon->count - 1], polygon->points[0], 0, j}); } +void vektor_rectangle_tessellate(EdgeBuffer* buffer, VektorRectangle* rct, size_t j) { + if (vec2_equals(rct->end, rct->start)) {return;} + + Edge top = (Edge){rct->start, (V2){rct->end.x, rct->start.y}, 0, j }; + Edge right = (Edge){(V2){rct->end.x, rct->start.y}, rct->end, 0, j}; + Edge bottom = (Edge){(V2){rct->start.x, rct->end.y}, rct->end, 0, j}; + Edge left = (Edge){rct->start, (V2){rct->start.x, rct->end.y}, 0, j }; + + vektor_edgebuffer_add_edge(buffer, top); + vektor_edgebuffer_add_edge(buffer, right); + vektor_edgebuffer_add_edge(buffer, bottom); + vektor_edgebuffer_add_edge(buffer, left); +} + void vektor_rasterize(VertexBuffer* vb, VektorShapeBuffer* shapes) { EdgeBuffer edges = {0}; for (size_t i = 0; i < shapes->count; i++) { @@ -37,11 +52,15 @@ void vektor_rasterize(VertexBuffer* vb, VektorShapeBuffer* shapes) { switch (p->kind) { case VEKTOR_POLYLINE: - vektor_polyline_flatten(&edges, p->polyline, i); + vektor_polyline_tessellate(&edges, p->polyline, i); break; case VEKTOR_POLYGON: - vektor_polygon_flatten(&edges, p->polygon, i); + vektor_polygon_tessellate(&edges, p->polygon, i); + break; + + case VEKTOR_RECTANGLE: + vektor_rectangle_tessellate(&edges, &p->rectangle, i); break; default: @@ -53,7 +72,7 @@ void vektor_rasterize(VertexBuffer* vb, VektorShapeBuffer* shapes) { vektor_edges_to_triangles(vb, &edges, shapes); } -void vb_add_triangle(VertexBuffer* vb, V2 v0, V2 v1, V2 v2, VektorColor color) { +void vektor_vb_add_triangle(VertexBuffer* vb, V2 v0, V2 v1, V2 v2, VektorColor color) { if (vb->count + 3 >= vb->capacity) { vb->capacity = vb->capacity ? vb->capacity * 2 : 8; vb->vertices = realloc(vb->vertices, sizeof(Vertex) * vb->capacity); @@ -63,6 +82,22 @@ void vb_add_triangle(VertexBuffer* vb, V2 v0, V2 v1, V2 v2, VektorColor color) { vb->vertices[vb->count++] = (Vertex){v2, color}; } +void vektor_vb_add_quad(VertexBuffer* vb, V2 a, V2 b, VektorColor color) { + + float minx = fminf(a.x, b.x); + float maxx = fmaxf(a.x, b.x); + float miny = fminf(a.y, b.y); + float maxy = fmaxf(a.y, b.y); + + V2 tl = {minx, miny}; + V2 bl = {minx, maxy}; + V2 br = {maxx, maxy}; + V2 tr = {maxx, miny}; + + vektor_vb_add_triangle(vb, tl, bl, br, color); + vektor_vb_add_triangle(vb, tl, br, tr, color); +} + void vektor_edge_to_triangles(VertexBuffer* vb, Edge e, VektorShapeBuffer* shape_buffer) { float dx = e.p2.x - e.p1.x; @@ -81,9 +116,9 @@ void vektor_edge_to_triangles(VertexBuffer* vb, Edge e, V2 v2 = {e.p2.x + px, e.p2.y + py}; V2 v3 = {e.p2.x - px, e.p2.y - py}; - vb_add_triangle(vb, v0, v1, v2, + vektor_vb_add_triangle(vb, v0, v1, v2, shape_buffer->shapes[e.shape_id].style.stroke_color); - vb_add_triangle(vb, v2, v1, v3, + vektor_vb_add_triangle(vb, v2, v1, v3, shape_buffer->shapes[e.shape_id].style.stroke_color); } diff --git a/src/core/raster.h b/src/core/raster.h index 56f70a7..6864b43 100644 --- a/src/core/raster.h +++ b/src/core/raster.h @@ -23,8 +23,9 @@ typedef struct { void vektor_edgebuffer_add_edge(EdgeBuffer* edges, Edge edge); -void vektor_polyline_flatten(EdgeBuffer* edges, VektorPolyline* line, size_t i); -void vektor_polygon_flatten(EdgeBuffer* buffer, VektorPolygon* line, size_t i); +void vektor_polyline_tessellate(EdgeBuffer* edges, VektorPolyline* line, size_t i); +void vektor_polygon_tessellate(EdgeBuffer* buffer, VektorPolygon* polygon, size_t i); +void vektor_rectangle_tessellate(EdgeBuffer* buffer, VektorRectangle* rct, size_t i); typedef struct { V2 coords; @@ -37,7 +38,9 @@ typedef struct { size_t capacity; } VertexBuffer; -void vb_add_triangle(VertexBuffer* vb, V2 v0, V2 v1, V2 v2, VektorColor color); +void vektor_vb_add_triangle(VertexBuffer* vb, V2 v0, V2 v1, V2 v2, VektorColor color); +void vektor_vb_add_quad(VertexBuffer* vb, V2 v0, V2 v1, VektorColor color); + void vektor_edge_to_triangles(VertexBuffer* vb, Edge e, VektorShapeBuffer* shape_buffer); void vektor_edges_to_triangles(VertexBuffer* vb, EdgeBuffer* edges, diff --git a/src/core/vector.h b/src/core/vector.h index d483711..2cfea10 100644 --- a/src/core/vector.h +++ b/src/core/vector.h @@ -22,6 +22,10 @@ static inline V2 vec2_add(const V2 v1, const V2 v2) { return (V2){v1.x + v2.x, v1.y + v2.y}; } +static inline bool vec2_equals(const V2 v1, const V2 v2) { + return (bool)(v1.x == v2.x && v1.y == v2.x); +} + static inline V2 vec2_sub(const V2 v1, const V2 v2) { return (V2){v1.x - v2.x, v1.y - v2.y}; } diff --git a/src/main.c b/src/main.c index 67d942e..7488bcc 100644 --- a/src/main.c +++ b/src/main.c @@ -1,3 +1,4 @@ +#include "glib.h" #include "gtk/gtk.h" #include "src/application/applicationstate.h" #include "src/core/primitives.h" @@ -14,6 +15,13 @@ static void on_map(GtkWidget* window, gpointer user_data) { vektor_uictrl_map((VektorWidgetState*)user_data); } +static int update_callback(gpointer data) { + VektorAppState* appstate = (VektorAppState*)data; + gtk_gl_area_queue_render( + GTK_GL_AREA(appstate->widgetState->workspaceCanvas)); + return G_SOURCE_CONTINUE; +} + static void activate(GtkApplication* app, gpointer user_data) { VektorWidgetState* widget_state = @@ -25,6 +33,8 @@ static void activate(GtkApplication* app, gpointer user_data) { g_signal_connect(widget_state->window, "map", G_CALLBACK(on_map), widget_state); + g_timeout_add(1, update_callback, app_state); + gtk_window_present(widget_state->window); } diff --git a/src/ui/uicontroller.c b/src/ui/uicontroller.c index 5f0a07d..3b076d9 100644 --- a/src/ui/uicontroller.c +++ b/src/ui/uicontroller.c @@ -50,14 +50,16 @@ void vektor_uictrl_init(GtkApplication* app, VektorWidgetState* stateOut) { GTK_BUTTON(gtk_builder_get_object(builder, "button_shapetools")); stateOut->workspaceRevealerShapes = GTK_REVEALER(gtk_builder_get_object(builder, "shape_revealer")); - stateOut->workspaceButtonLinetool = + stateOut->workspaceButtonLineTool = GTK_BUTTON(gtk_builder_get_object(builder, "button_linetool")); - stateOut->workspaceButtonRecttool = + stateOut->workspaceButtonRectTool = GTK_BUTTON(gtk_builder_get_object(builder, "button_rectangletool")); - stateOut->workspaceButtonCircletool = + stateOut->workspaceButtonCircleTool = GTK_BUTTON(gtk_builder_get_object(builder, "button_circletool")); - stateOut->workspaceButtonPolygontool = + stateOut->workspaceButtonPolygonTool = GTK_BUTTON(gtk_builder_get_object(builder, "button_polygontool")); + stateOut->workspaceButtonSelectionTool = + GTK_BUTTON(gtk_builder_get_object(builder, "button_selecttool")); stateOut->workspaceColorPicker = VEKTOR_COLOR_WHEEL(gtk_builder_get_object(builder, "color_picker")); diff --git a/src/ui/uicontroller.h b/src/ui/uicontroller.h index bd2a247..90b99b7 100644 --- a/src/ui/uicontroller.h +++ b/src/ui/uicontroller.h @@ -17,10 +17,11 @@ typedef struct VektorWidgetState { GtkButton* workspaceButtonMasterShapes; GtkRevealer* workspaceRevealerShapes; - GtkButton* workspaceButtonLinetool; - GtkButton* workspaceButtonRecttool; - GtkButton* workspaceButtonCircletool; - GtkButton* workspaceButtonPolygontool; + GtkButton* workspaceButtonLineTool; + GtkButton* workspaceButtonRectTool; + GtkButton* workspaceButtonCircleTool; + GtkButton* workspaceButtonPolygonTool; + GtkButton* workspaceButtonSelectionTool; VektorColorWheel* workspaceColorPicker; diff --git a/src/ui/vektorcanvas.c b/src/ui/vektorcanvas.c index e0d72c7..c0020a9 100644 --- a/src/ui/vektorcanvas.c +++ b/src/ui/vektorcanvas.c @@ -1,8 +1,10 @@ #include "epoxy/gl.h" +#include "glib.h" #include "gtk/gtk.h" #include "../core/raster.h" #include "src/core/primitives.h" +#include "src/util/color.h" #include "uicontroller.h" #include "vektorcanvas.h" #include @@ -27,7 +29,19 @@ char* read_file(const char* path) { return buffer; } -static GLuint shader_program; +static GLuint standard_shader_program; +static GLuint selection_shader_program; + +// shader uniforms +static GLuint shader_standard_uProjMatrixLoc; + +static GLuint shader_selection_uProjMatrixLoc; +static GLuint shader_selection_uTimeLoc; +static GLuint shader_selection_uC1Loc; +static GLuint shader_selection_uC2Loc; +static GLuint shader_selection_uMinLoc; +static GLuint shader_selection_uMaxLoc; + static GLuint vao; VertexBuffer vb; @@ -46,17 +60,11 @@ static GLuint compile_shader(GLenum type, const char* src) { return shader; } -static void init_shader(void) { - char* vert_src = read_file("./shaders/triangle.vert.glsl"); - char* frag_src = read_file("./shaders/triangle.frag.glsl"); +static GLuint create_shader_program(char* frag, char* vert) { + GLuint vertex = compile_shader(GL_VERTEX_SHADER, vert); + GLuint fragment = compile_shader(GL_FRAGMENT_SHADER, frag); - if (!vert_src || !frag_src) - g_error("Failed to load shader files"); - - GLuint vertex = compile_shader(GL_VERTEX_SHADER, vert_src); - GLuint fragment = compile_shader(GL_FRAGMENT_SHADER, frag_src); - - shader_program = glCreateProgram(); + GLuint shader_program = glCreateProgram(); glAttachShader(shader_program, vertex); glAttachShader(shader_program, fragment); glLinkProgram(shader_program); @@ -71,6 +79,32 @@ static void init_shader(void) { glDeleteShader(vertex); glDeleteShader(fragment); + + return shader_program; +} + +static void init_shader(void) { + char* vert_src = read_file("./shaders/triangle.vert.glsl"); + char* frag_src = read_file("./shaders/triangle.frag.glsl"); + char* selection_frag_src = read_file("./shaders/selection.frag.glsl"); + + if (!vert_src || !frag_src) + g_error("Failed to load shader files"); + + standard_shader_program = create_shader_program(frag_src, vert_src); + selection_shader_program = create_shader_program(selection_frag_src, vert_src); + + shader_standard_uProjMatrixLoc = glGetUniformLocation(standard_shader_program, "uProjection"); + shader_selection_uProjMatrixLoc = glGetUniformLocation(selection_shader_program, "uProjection"); + shader_selection_uTimeLoc = glGetUniformLocation(selection_shader_program, "uTime"); + shader_selection_uC1Loc = glGetUniformLocation(selection_shader_program, "uColor1"); + shader_selection_uC2Loc = glGetUniformLocation(selection_shader_program, "uColor2"); + + shader_selection_uMinLoc = glGetUniformLocation(selection_shader_program, "uMin"); + shader_selection_uMaxLoc = glGetUniformLocation(selection_shader_program, "uMax"); + + if (shader_selection_uMinLoc == -1 || shader_selection_uMaxLoc == -1) + g_warning("Selection shader: uMin/uMax uniform not found in shader!"); } static void init_geometry(void) { @@ -92,32 +126,70 @@ static void init_geometry(void) { glBindVertexArray(0); } -static gboolean render(GtkGLArea* area, GdkGLContext* context, - VektorShapeBuffer* prims) { - +static gboolean render(GtkGLArea* a, GdkGLContext* ctx, VektorCanvasRenderInfo* renderInfo) { vb.count = 0; - vektor_rasterize(&vb, prims); + + vektor_rasterize(&vb, renderInfo->shapes); + size_t shape_vertex_count = vb.count; // remember how many vertices belong to shapes + + // create selection quad if a shape is selected + if(renderInfo->selectedShape != NULL && + *(renderInfo->selectedShape) != NULL) { + VektorBBox bbox = vektor_primitive_get_bbox( + (*(renderInfo->selectedShape))->primitive + ); + + vektor_vb_add_quad( + &vb, + bbox.min, + bbox.max, + vektor_color_new(255,255,255,255) + ); + } glBufferData(GL_ARRAY_BUFFER, vb.count * sizeof(Vertex), vb.vertices, GL_STATIC_DRAW); - glUseProgram(shader_program); - GLuint uProjectionLoc = glGetUniformLocation(shader_program, "uProjection"); + // PASS 1 - draw shape vertices + glUseProgram(standard_shader_program); + float projectionMatrix[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; - glUniformMatrix4fv(uProjectionLoc, 1, GL_FALSE, projectionMatrix); + glUniformMatrix4fv(shader_standard_uProjMatrixLoc, 1, GL_FALSE, projectionMatrix); glBindVertexArray(vao); glDisable(GL_CULL_FACE); - // glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); - glDrawArrays(GL_TRIANGLES, 0, vb.count); - GLenum err = glGetError(); - //printf("OpenGL error: %x\n", err); - // glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); + glDrawArrays(GL_TRIANGLES, 0, shape_vertex_count); + + // PASS 2 - draw selection quads + if (vb.count > shape_vertex_count) { + float time = (g_get_monotonic_time() - renderInfo->startupTime) / 10000000.0f; + + // re-fetch bbox (we know a shape is selected) + VektorBBox bbox = vektor_primitive_get_bbox( + (*(renderInfo->selectedShape))->primitive + ); + + glUseProgram(selection_shader_program); + + float projectionMatrix[16] = {1, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 1}; + + glUniformMatrix4fv(shader_selection_uProjMatrixLoc, 1, GL_FALSE, projectionMatrix); + glUniform1f(shader_selection_uTimeLoc, time); + glUniform2f(shader_selection_uMinLoc, bbox.min.x, bbox.min.y); + glUniform2f(shader_selection_uMaxLoc, bbox.max.x, bbox.max.y); + glUniform4f(shader_selection_uC1Loc, 0, 0, 0, 1); + glUniform4f(shader_selection_uC2Loc, 0.46, 0.46, 1, 1); + + glDrawArrays(GL_TRIANGLES, shape_vertex_count, vb.count - shape_vertex_count); + } + glBindVertexArray(0); glUseProgram(0); @@ -163,7 +235,7 @@ static void realize(GtkGLArea* area, gpointer user_data) { } void vektor_canvas_init(VektorWidgetState* state, VektorCanvas* canvasOut, - VektorShapeBuffer* prims) { + VektorCanvasRenderInfo* renderInfo) { canvasOut->canvasWidget = state->workspaceCanvas; canvasOut->width = VKTR_CANVAS_WIDTH; canvasOut->height = VKTR_CANVAS_HEIGHT; @@ -178,5 +250,5 @@ void vektor_canvas_init(VektorWidgetState* state, VektorCanvas* canvasOut, g_signal_connect(canvasOut->canvasWidget, "realize", G_CALLBACK(realize), NULL); g_signal_connect(canvasOut->canvasWidget, "render", G_CALLBACK(render), - prims); + renderInfo); } diff --git a/src/ui/vektorcanvas.h b/src/ui/vektorcanvas.h index eeb706d..374341e 100644 --- a/src/ui/vektorcanvas.h +++ b/src/ui/vektorcanvas.h @@ -4,8 +4,10 @@ #include "../core/raster.h" #include "../util/color.h" #include "gtk/gtk.h" +#include "src/core/primitives.h" #include "uicontroller.h" + typedef struct VektorCanvas { GtkGLArea* canvasWidget; @@ -18,8 +20,16 @@ typedef struct VektorCanvas { int height; } VektorCanvas; +typedef struct VektorCanvasRenderInfo { + gint64 startupTime; + VektorShapeBuffer* shapes; + + // a pointer to appstate->selectedShape + VektorShape** selectedShape; +} VektorCanvasRenderInfo; + void vektor_canvas_init(VektorWidgetState* state, VektorCanvas* canvasOut, - VektorShapeBuffer* shapes); + VektorCanvasRenderInfo* renderInfo); // void vektor_canvas_update(VektorCanvas* canvas); // void vektor_canvas_fill(VektorCanvas* canvas, VektorColor color); // void vektor_canvas_drawfrom(VektorFramebuffer* fb, VektorCanvas* canvas); diff --git a/ui/main.ui b/ui/main.ui index b254381..0e19809 100644 --- a/ui/main.ui +++ b/ui/main.ui @@ -65,6 +65,14 @@ + + + + tool-select-symbolic + start + + +