diff --git a/shaders/selection.frag.glsl b/shaders/selection.frag.glsl new file mode 100644 index 0000000..d236dbf --- /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 = 1.8; + + float distance_along = (vPos.x + vPos.y) * 20.0; + + float t = mod(distance_along * total, 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 ba36c81..78dff57 100644 --- a/src/application/applicationstate.c +++ b/src/application/applicationstate.c @@ -21,8 +21,6 @@ static void appstate_set_tool(GtkButton* button, gpointer user_data) { button_tool_set_data* data = (button_tool_set_data*)user_data; data->state->selectedTool = data->tool; - g_print("%d", data->tool); - // setting tool makes the sub-tools menu to close gtk_revealer_set_reveal_child(data->revealer, FALSE); @@ -71,7 +69,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}); @@ -203,7 +200,10 @@ void vektor_appstate_new(VektorWidgetState* wstate, VektorAppState* stateOut) { 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; + vektor_canvas_init(wstate, stateOut->canvas, renderInfo); // link all the buttons g_signal_connect(G_OBJECT(wstate->workspaceButtonLinetool), "clicked", diff --git a/src/core/primitives.c b/src/core/primitives.c index 497f2b4..31a83fd 100644 --- a/src/core/primitives.c +++ b/src/core/primitives.c @@ -91,15 +91,23 @@ VektorBBox vektor_polyline_get_bbox(VektorPrimitive prim) { } VektorBBox vektor_polygon_get_bbox(VektorPrimitive prim) { - float min_x, max_x, min_y, max_y; - for (size_t i = 0; i < prim.polygon->count; i++) { + 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}}; } diff --git a/src/core/raster.c b/src/core/raster.c index 7c7066b..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 @@ -71,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); @@ -81,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; @@ -99,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 6bbbb3b..6864b43 100644 --- a/src/core/raster.h +++ b/src/core/raster.h @@ -38,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/ui/vektorcanvas.c b/src/ui/vektorcanvas.c index e0d72c7..4cf5851 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,72 @@ 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 + ); + + g_print("min: %f %f max: %f %f\n", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y); + + 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() / 1000000.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); // ← this was missing/broken + glUniform2f(shader_selection_uMaxLoc, bbox.max.x, bbox.max.y); // ← this was missing/broken + 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 +237,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 +252,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..18f0371 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,15 @@ typedef struct VektorCanvas { int height; } VektorCanvas; +typedef struct VektorCanvasRenderInfo { + 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);