diff --git a/shaders/selection.frag.glsl b/shaders/selection.frag.glsl index be04a82..83ce5dd 100644 --- a/shaders/selection.frag.glsl +++ b/shaders/selection.frag.glsl @@ -5,6 +5,7 @@ in vec2 vPos; out vec4 FragColor; uniform float uTime; +uniform float uScale; uniform vec4 uColor1; uniform vec4 uColor2; uniform vec2 uMin; @@ -12,7 +13,7 @@ uniform vec2 uMax; void main() { - float borderWidth = 0.008; + float borderWidth = 0.008 / uScale; float distX = min(vPos.x - uMin.x, uMax.x - vPos.x); diff --git a/src/application/applicationstate.c b/src/application/applicationstate.c index 4df7752..314ebc5 100644 --- a/src/application/applicationstate.c +++ b/src/application/applicationstate.c @@ -96,9 +96,9 @@ static void canvas_onclick(GtkGestureClick* gesture, int n_press, double x, vektor_appstate_canvas_click(state, normalized_coords.x, normalized_coords.y); - // technically there are cases when a click would not result in change of the geometry - // but this is more concise then writing it inside that function - // a bunch of times and burder future click dispatches with + // technically there are cases when a click would not result in change of + // the geometry but this is more concise then writing it inside that + // function a bunch of times and burder future click dispatches with // handling this signal vektor_canvas_geometry_changed(state->renderInfo); } @@ -243,10 +243,9 @@ void vektor_appstate_canvas_drag_begin(GtkGestureDrag* gesture, gdouble x, int widget_w = gtk_widget_get_width(widget); int widget_h = gtk_widget_get_height(widget); - V2 position = - (V2){(2 * (x / widget_w)) - 1, 1 - (2 * (y / widget_h))}; - position = - m33_transform(m33_inverse(state->renderInfo->canvasMat), (V2){position.x, position.y}); + V2 position = (V2){(2 * (x / widget_w)) - 1, 1 - (2 * (y / widget_h))}; + position = m33_transform(m33_inverse(state->renderInfo->canvasMat), + (V2){position.x, position.y}); if(state->selectedShape != NULL) { VektorShapeNode* selectedShape = state->selectedShape; @@ -262,7 +261,6 @@ void vektor_appstate_canvas_drag_begin(GtkGestureDrag* gesture, gdouble x, break; } } - } } @@ -282,9 +280,9 @@ void vektor_appstate_canvas_drag_update(GtkGestureDrag* gesture, gdouble x, int widget_h = gtk_widget_get_height(widget); V2 position = (V2){(2 * ((x + start_x) / widget_w)) - 1, - 1 - (2 * ((y + start_y) / widget_h))}; - position = - m33_transform(m33_inverse(state->renderInfo->canvasMat), (V2){position.x, position.y}); + 1 - (2 * ((y + start_y) / widget_h))}; + position = m33_transform(m33_inverse(state->renderInfo->canvasMat), + (V2){position.x, position.y}); // drag handle if selected if(state->selectedShape != NULL && state->heldHandleIndex != -1) { @@ -295,12 +293,12 @@ void vektor_appstate_canvas_drag_update(GtkGestureDrag* gesture, gdouble x, } void vektor_appstate_canvas_drag_end(GtkGestureDrag* gesture, gdouble x, - gdouble y, gpointer user_data) { + gdouble y, gpointer user_data) { VektorAppState* state = (VektorAppState*)user_data; // if we were dragging a handle - if(state->selectedShape != NULL && state->heldHandleIndex != -1) { + if (state->selectedShape != NULL && state->heldHandleIndex != -1) { state->heldHandleIndex = -1; // ...then remove handle drag flag vektor_canvas_geometry_changed(state->renderInfo); } diff --git a/src/core/primitives.c b/src/core/primitives.c index 5ae1a03..2744bf0 100644 --- a/src/core/primitives.c +++ b/src/core/primitives.c @@ -238,7 +238,6 @@ void vektor_shape_create_handles(VektorShape* shape) { // ------ AUXILIARY HANDLE METHODS ------ - void vektor_shape_add_handle(VektorShape* shape, V2 handle) { // could be optimised with capacity property // but this function is only called when adding new @@ -284,66 +283,64 @@ void vektor_circle_handles_updated(VektorCircle* circle, V2** handles, return; } - if(*heldHandleIndex == 0) { // dragging center + if (*heldHandleIndex == 0) { // dragging center V2 translation = vec2_sub((*handles)[0], circle->center); circle->center = (*handles)[0]; (*handles)[1] = vec2_add(translation, (*handles)[1]); } else { circle->radius = vec2_length(vec2_sub((*handles)[0], (*handles)[1])); } - - } // this shi is big because it dynamically handles handle remapping when // rectangle enters an invalid state (end < start) // creating the illusion of an invertable rect, while also keeping it // valid at all times -void vektor_rectangle_handles_updated(VektorRectangle* rectangle, V2** handles, size_t* count, int* heldHandleIndex) { +void vektor_rectangle_handles_updated(VektorRectangle* rectangle, V2** handles, + size_t* count, int* heldHandleIndex) { if (*count != 5) { g_warning("unexpected rectangle handle count (%zu)", *count); return; } V2 start = rectangle->start; - V2 end = rectangle->end; + V2 end = rectangle->end; - switch (*heldHandleIndex) + switch (*heldHandleIndex) { + case 0: // center drag { - case 0: // center drag - { - V2 oldCenter = vec2_scale(vec2_add(start, end), 0.5f); - V2 newCenter = (*handles)[0]; + V2 oldCenter = vec2_scale(vec2_add(start, end), 0.5f); + V2 newCenter = (*handles)[0]; - V2 translation = vec2_sub(newCenter, oldCenter); + V2 translation = vec2_sub(newCenter, oldCenter); - start = vec2_add(start, translation); - end = vec2_add(end, translation); - break; - } + start = vec2_add(start, translation); + end = vec2_add(end, translation); + break; + } - case 1: // top-left - start.x = (*handles)[1].x; - end.y = (*handles)[1].y; - break; + case 1: // top-left + start.x = (*handles)[1].x; + end.y = (*handles)[1].y; + break; - case 2: // top-right - end.x = (*handles)[2].x; - end.y = (*handles)[2].y; - break; + case 2: // top-right + end.x = (*handles)[2].x; + end.y = (*handles)[2].y; + break; - case 3: // bottom-left - start.x = (*handles)[3].x; - start.y = (*handles)[3].y; - break; + case 3: // bottom-left + start.x = (*handles)[3].x; + start.y = (*handles)[3].y; + break; - case 4: // bottom-right - end.x = (*handles)[4].x; - start.y = (*handles)[4].y; - break; + case 4: // bottom-right + end.x = (*handles)[4].x; + start.y = (*handles)[4].y; + break; - default: - return; + default: + return; } // Store raw values before normalization @@ -362,31 +359,44 @@ void vektor_rectangle_handles_updated(VektorRectangle* rectangle, V2** handles, bool flipY = raw_min_y > raw_max_y; // Remap handle if we crossed axes - if (*heldHandleIndex != 0) - { + if (*heldHandleIndex != 0) { if (flipX) { switch (*heldHandleIndex) { - case 1: *heldHandleIndex = 2; break; - case 2: *heldHandleIndex = 1; break; - case 3: *heldHandleIndex = 4; break; - case 4: *heldHandleIndex = 3; break; + case 1: + *heldHandleIndex = 2; + break; + case 2: + *heldHandleIndex = 1; + break; + case 3: + *heldHandleIndex = 4; + break; + case 4: + *heldHandleIndex = 3; + break; } } if (flipY) { switch (*heldHandleIndex) { - case 1: *heldHandleIndex = 3; break; - case 3: *heldHandleIndex = 1; break; - case 2: *heldHandleIndex = 4; break; - case 4: *heldHandleIndex = 2; break; + case 1: + *heldHandleIndex = 3; + break; + case 3: + *heldHandleIndex = 1; + break; + case 2: + *heldHandleIndex = 4; + break; + case 4: + *heldHandleIndex = 2; + break; } } } - VektorRectangle properRect = { - .start = {min_x, min_y}, - .end = {max_x, max_y} - }; + VektorRectangle properRect = {.start = {min_x, min_y}, + .end = {max_x, max_y}}; vektor_rectangle_set_start(rectangle, properRect.start); vektor_rectangle_set_end(rectangle, properRect.end); @@ -399,11 +409,13 @@ void vektor_shape_handles_updated(VektorShape* shape, int* heldHandleIndex) { switch (shape->primitive.kind) { case VEKTOR_POLYLINE: vektor_polyline_handles_updated(shape->primitive.polyline, - &shape->handles, &shape->handleCount, heldHandleIndex); + &shape->handles, &shape->handleCount, + heldHandleIndex); break; case VEKTOR_POLYGON: vektor_polygon_handles_updated(shape->primitive.polygon, - &shape->handles, &shape->handleCount, heldHandleIndex); + &shape->handles, &shape->handleCount, + heldHandleIndex); break; case VEKTOR_CIRCLE: vektor_circle_handles_updated(&shape->primitive.circle, &shape->handles, @@ -411,7 +423,8 @@ void vektor_shape_handles_updated(VektorShape* shape, int* heldHandleIndex) { break; case VEKTOR_RECTANGLE: vektor_rectangle_handles_updated(&shape->primitive.rectangle, - &shape->handles, &shape->handleCount, heldHandleIndex); + &shape->handles, &shape->handleCount, + heldHandleIndex); break; } } diff --git a/src/core/primitives.h b/src/core/primitives.h index f73ddf2..32e919d 100644 --- a/src/core/primitives.h +++ b/src/core/primitives.h @@ -109,7 +109,6 @@ void vektor_rectangle_create_handles(VektorRectangle* rectangle, V2** handleArr, size_t* count); void vektor_shape_create_handles(VektorShape* shape); - void vektor_shape_add_handle(VektorShape* shape, V2 handle); VektorBBox vektor_shape_get_handle_bbox(V2 handle); diff --git a/src/core/raster.c b/src/core/raster.c index d91f741..8d0950c 100644 --- a/src/core/raster.c +++ b/src/core/raster.c @@ -1,5 +1,6 @@ #include "raster.h" #include "epoxy/gl.h" +#include "glib.h" #include "primitives.h" #include "src/core/matrix.h" #include "src/core/modifier.h" @@ -39,8 +40,8 @@ void vektor_polygon_tessellate(EdgeBuffer* buffer, VektorPolygon* polygon, void vektor_circle_tessellate(EdgeBuffer* buffer, VektorCircle* circle, size_t j, double scale) { - double err = 0.000025; - size_t res = PI * sqrt((scale * circle->radius) / (2 * err)); + double err = 0.0025; + size_t res = MIN(PI * sqrt((scale * circle->radius) / (2 * err)), 20); for (size_t i = 0; i < res; i++) { double theta1 = (2 * PI * i) / res; double theta2 = (2 * PI * (i + 1)) / res; @@ -69,27 +70,33 @@ void vektor_rectangle_tessellate(EdgeBuffer* buffer, VektorRectangle* rct, vektor_edgebuffer_add_edge(buffer, left); } -void vektor_vb_rasterize(VertexBuffer* vb, VektorShapeNodeBuffer* nodes, +void vektor_vb_rasterize(VertexBuffer* vb, VektorShapeNodeBuffer* nodebuf, double scale) { - EdgeBuffer edges = {0}; - for (size_t i = 0; i < nodes->count; i++) { - VektorPrimitive* p = &nodes->nodes[i].base.primitive; + for (size_t i = 0; i < nodebuf->count; i++) { + EdgeBuffer edges = {0}; + VektorPrimitive* p = &nodebuf->nodes[i].base.primitive; + VektorStyle style = nodebuf->nodes[i].base.style; + M33 transform = nodebuf->nodes[i].base.transform; switch (p->kind) { case VEKTOR_POLYLINE: vektor_polyline_tessellate(&edges, p->polyline, i, scale); + vektor_edges_to_triangles(vb, &edges, &transform, style, FALSE); break; case VEKTOR_POLYGON: vektor_polygon_tessellate(&edges, p->polygon, i, scale); + vektor_edges_to_triangles(vb, &edges, &transform, style, TRUE); break; case VEKTOR_CIRCLE: vektor_circle_tessellate(&edges, &p->circle, i, scale); + vektor_edges_to_triangles(vb, &edges, &transform, style, TRUE); break; case VEKTOR_RECTANGLE: vektor_rectangle_tessellate(&edges, &p->rectangle, i, scale); + vektor_edges_to_triangles(vb, &edges, &transform, style, TRUE); break; default: @@ -133,37 +140,91 @@ void vektor_vb_add_quad(VertexBuffer* vb, V2 a, V2 b, VektorColor color) { vektor_vb_add_triangle(vb, tl, br, tr, color); } -void vektor_edge_to_triangles(VertexBuffer* vb, Edge e, - VektorShapeNodeBuffer* node_buffer) { - float dx = e.p2.x - e.p1.x; - float dy = e.p2.y - e.p1.y; - float len = sqrtf(dx * dx + dy * dy); - if (len == 0) - return; +Edge edge_transform(const Edge* e, const M33* t) { + Edge out = *e; + out.p1 = m33_transform(*t, e->p1); + out.p2 = m33_transform(*t, e->p2); + return out; +} - float px = - -dy / len * (node_buffer->nodes[e.shape_id].base.style.stroke_width / 2); - float py = - dx / len * (node_buffer->nodes[e.shape_id].base.style.stroke_width / 2); - - V2 v0 = m33_transform(node_buffer->nodes[e.shape_id].base.transform, - (V2){e.p1.x + px, e.p1.y + py}); - V2 v1 = m33_transform(node_buffer->nodes[e.shape_id].base.transform, - (V2){e.p1.x - px, e.p1.y - py}); - V2 v2 = m33_transform(node_buffer->nodes[e.shape_id].base.transform, - (V2){e.p2.x + px, e.p2.y + py}); - V2 v3 = m33_transform(node_buffer->nodes[e.shape_id].base.transform, - (V2){e.p2.x - px, e.p2.y - py}); - - vektor_vb_add_triangle(vb, v0, v1, v2, - node_buffer->nodes[e.shape_id].base.style.stroke_color); - vektor_vb_add_triangle(vb, v2, v1, v3, - node_buffer->nodes[e.shape_id].base.style.stroke_color); +V2 line_intersection(V2 p, V2 r, V2 q, V2 s) { + float t = vec2_cross(vec2_sub(q, p), s) / vec2_cross(r, s); + return vec2_add(p, vec2_scale(r, t)); } void vektor_edges_to_triangles(VertexBuffer* vb, EdgeBuffer* edges, - VektorShapeNodeBuffer* node_buffer) { + M33* transform, VektorStyle style, bool closed) { + if (!edges || edges->count < 1) + return; + + float hw = style.stroke_width * 0.5f; + for (size_t i = 0; i < edges->count; i++) { - vektor_edge_to_triangles(vb, edges->edges[i], node_buffer); + Edge e = edge_transform(&edges->edges[i], transform); + + V2 d = vec2_normalize(vec2_sub(e.p2, e.p1)); + V2 n = vec2_perp(d); + V2 off = vec2_scale(n, hw); + + V2 v0 = vec2_add(e.p1, off); + V2 v1 = vec2_sub(e.p1, off); + V2 v2 = vec2_add(e.p2, off); + V2 v3 = vec2_sub(e.p2, off); + + vektor_vb_add_triangle(vb, v0, v1, v2, style.stroke_color); + vektor_vb_add_triangle(vb, v2, v1, v3, style.stroke_color); + } + + size_t limit = closed ? edges->count : edges->count - 1; + + for (size_t i = 0; i < limit; i++) { + Edge e1 = edge_transform(&edges->edges[i], transform); + Edge e2 = + edge_transform(&edges->edges[(i + 1) % edges->count], transform); + + V2 corner = e1.p2; + + V2 d1 = vec2_normalize(vec2_sub(e1.p2, e1.p1)); + V2 d2 = vec2_normalize(vec2_sub(e2.p2, e2.p1)); + + V2 n1 = vec2_perp(d1); + V2 n2 = vec2_perp(d2); + + V2 off1 = vec2_scale(n1, hw); + V2 off2 = vec2_scale(n2, hw); + + V2 v10 = vec2_add(e1.p1, off1); + V2 v11 = vec2_sub(e1.p1, off1); + V2 v12 = vec2_add(e1.p2, off1); + V2 v13 = vec2_sub(e1.p2, off1); + + V2 v20 = vec2_add(e2.p1, off2); + V2 v21 = vec2_sub(e2.p1, off2); + V2 v22 = vec2_add(e2.p2, off2); + V2 v23 = vec2_sub(e2.p2, off2); + + V2 outer_miter = line_intersection(vec2_add(corner, off1), d1, + vec2_add(corner, off2), d2); + + V2 inner_miter = line_intersection(vec2_sub(corner, off1), d1, + vec2_sub(corner, off2), d2); + + V2 mo = vec2_sub(outer_miter, corner); + V2 mi = vec2_sub(inner_miter, corner); + + V2 vo1 = line_intersection(corner, vec2_normalize(vec2_perp(mo)), + vec2_add(corner, off1), d1); + V2 vo2 = line_intersection(corner, + vec2_negate(vec2_normalize(vec2_perp(mo))), + vec2_add(corner, off2), d2); + + V2 vi1 = line_intersection(corner, vec2_normalize(vec2_perp(mi)), + vec2_sub(corner, off1), d1); + V2 vi2 = line_intersection(corner, + vec2_negate(vec2_normalize(vec2_perp(mi))), + vec2_sub(corner, off2), d2); + + vektor_vb_add_triangle(vb, vo1, outer_miter, vo2, style.stroke_color); + vektor_vb_add_triangle(vb, vi1, inner_miter, vi2, style.stroke_color); } } \ No newline at end of file diff --git a/src/core/raster.h b/src/core/raster.h index ca0c37c..09266bc 100644 --- a/src/core/raster.h +++ b/src/core/raster.h @@ -51,8 +51,8 @@ void vektor_vb_add_quad(VertexBuffer* vb, V2 v0, V2 v1, VektorColor color); void vektor_edge_to_triangles(VertexBuffer* vb, Edge e, VektorShapeNodeBuffer* node_buffer); void vektor_edges_to_triangles(VertexBuffer* vb, EdgeBuffer* edges, - VektorShapeNodeBuffer* node_buffer) ; -void vektor_vb_rasterize(VertexBuffer* vb, VektorShapeNodeBuffer* shapes, + M33* transform, VektorStyle style, bool closed); +void vektor_rasterize(VertexBuffer* vb, VektorShapeBuffer* shapes, double scale); #endif // RASTER_H_ diff --git a/src/core/vector.h b/src/core/vector.h index 354e8c4..6413db3 100644 --- a/src/core/vector.h +++ b/src/core/vector.h @@ -20,6 +20,10 @@ static inline V3 vec2_vector(const V2 v) { return (V3){v.x, v.y, 0}; } static inline V3 vec2_point(const V2 v) { return (V3){v.x, v.y, 1}; } +static inline V2 vec2_perp(const V2 v) { return (V2){-v.y, v.x}; } + +static inline V2 vec2_negate(const V2 v) { return (V2){-v.x, -v.y}; } + static inline V2 vec2_add(const V2 v1, const V2 v2) { return (V2){v1.x + v2.x, v1.y + v2.y}; } diff --git a/src/ui/vektorcanvas.c b/src/ui/vektorcanvas.c index 0c207b4..50ef9ce 100644 --- a/src/ui/vektorcanvas.c +++ b/src/ui/vektorcanvas.c @@ -38,6 +38,7 @@ static GLuint shader_standard_uProjMatrixLoc; static GLuint shader_selection_uProjMatrixLoc; static GLuint shader_selection_uTimeLoc; +static GLuint shader_selection_uScaleLoc; static GLuint shader_selection_uC1Loc; static GLuint shader_selection_uC2Loc; static GLuint shader_selection_uMinLoc; @@ -103,6 +104,8 @@ static void init_shader(void) { glGetUniformLocation(selection_shader_program, "uProjection"); shader_selection_uTimeLoc = glGetUniformLocation(selection_shader_program, "uTime"); + shader_selection_uScaleLoc = + glGetUniformLocation(selection_shader_program, "uScale"); shader_selection_uC1Loc = glGetUniformLocation(selection_shader_program, "uColor1"); shader_selection_uC2Loc = @@ -169,9 +172,7 @@ void vektor_canvas_geometry_changed(VektorCanvasRenderInfo* renderInfo) { static gboolean render(GtkGLArea* a, GdkGLContext* ctx, VektorCanvasRenderInfo* renderInfo) { - //vektor_canvas_geometry_changed(renderInfo); - - + // vektor_canvas_geometry_changed(renderInfo); glBufferData(GL_ARRAY_BUFFER, vb.count * sizeof(Vertex), vb.vertices, GL_STATIC_DRAW); @@ -186,7 +187,7 @@ static gboolean render(GtkGLArea* a, GdkGLContext* ctx, 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); @@ -207,6 +208,7 @@ static gboolean render(GtkGLArea* a, GdkGLContext* ctx, glUniformMatrix4fv(shader_selection_uProjMatrixLoc, 1, GL_FALSE, renderInfo->canvasTransform); glUniform1f(shader_selection_uTimeLoc, time); + glUniform1f(shader_selection_uScaleLoc, renderInfo->zoom); 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, 0);