From 237bb02a8c8e1e1995c6b05a1ff679c638f66095 Mon Sep 17 00:00:00 2001 From: Froxwin <56168224+Froxwin@users.noreply.github.com> Date: Wed, 11 Mar 2026 14:11:03 +0000 Subject: [PATCH] feat: add shape transforms --- src/application/applicationstate.c | 29 +++--- src/core/primitives.c | 150 ++++++++++++++++------------- src/core/primitives.h | 31 ++++-- src/core/raster.c | 13 ++- src/core/vector.h | 2 +- src/main.c | 1 - src/ui/vektorcanvas.c | 13 +-- 7 files changed, 129 insertions(+), 110 deletions(-) diff --git a/src/application/applicationstate.c b/src/application/applicationstate.c index 52e2f50..8a16544 100644 --- a/src/application/applicationstate.c +++ b/src/application/applicationstate.c @@ -161,7 +161,7 @@ begin_click_dispatch: vektor_shapes_update_bbox(state->shapeBuffer); // polygon's handle count is not fixed, so we have to add them manually - vektor_shape_add_handle(state->selectedShape, pos); + vektor_shape_add_handle(state->selectedShape, pos); } else if (state->selectedTool == VektorCircleTool) { @@ -183,11 +183,9 @@ begin_click_dispatch: vektor_shapes_update_bbox(state->shapeBuffer); - vektor_circle_create_handles( - &state->selectedShape->primitive.circle, - &state->selectedShape->handles, - &state->selectedShape->handleCount - ); + vektor_circle_create_handles(&state->selectedShape->primitive.circle, + &state->selectedShape->handles, + &state->selectedShape->handleCount); } else if (state->selectedTool == VektorRectangleTool) { VektorRectangle* rect = vektor_rectangle_new(); @@ -208,10 +206,8 @@ begin_click_dispatch: vektor_rectangle_set_end(&state->selectedShape->primitive.rectangle, vec2_add(pos, (V2){0.1f, 0.1f})); vektor_rectangle_create_handles( - &state->selectedShape->primitive.rectangle, - &state->selectedShape->handles, - &state->selectedShape->handleCount - ); + &state->selectedShape->primitive.rectangle, + &state->selectedShape->handles, &state->selectedShape->handleCount); // state->selectedShape = NULL; vektor_shapes_update_bbox(state->shapeBuffer); @@ -228,7 +224,7 @@ begin_click_dispatch: state->selectedShape = NULL; } - if(state->selectedShape != NULL) + if (state->selectedShape != NULL) g_print("%zu\n", state->selectedShape->handleCount); } @@ -242,12 +238,10 @@ void vektor_appstate_canvas_drag_begin(GtkGestureDrag* gesture, gdouble x, V2 normalized_coords = (V2){(2 * (x / widget_w)) - 1, 1 - (2 * (y / widget_h))}; - } -void vektor_appstate_canvas_drag_update(GtkGestureDrag* gesture, - gdouble x, gdouble y, - gpointer user_data) { +void vektor_appstate_canvas_drag_update(GtkGestureDrag* gesture, gdouble x, + gdouble y, gpointer user_data) { gdouble start_x, start_y; gtk_gesture_drag_get_start_point(gesture, &start_x, &start_y); @@ -257,9 +251,8 @@ void vektor_appstate_canvas_drag_update(GtkGestureDrag* gesture, int widget_w = gtk_widget_get_width(widget); int widget_h = gtk_widget_get_height(widget); - V2 norm = - (V2){(2 * ( (x+start_x) / widget_w)) - 1, 1 - (2 * ( (y+start_y) / widget_h))}; - + V2 norm = (V2){(2 * ((x + start_x) / widget_w)) - 1, + 1 - (2 * ((y + start_y) / widget_h))}; } void vektor_appstate_new(VektorWidgetState* wstate, VektorAppState* stateOut) { diff --git a/src/core/primitives.c b/src/core/primitives.c index f2fa931..fe667bf 100644 --- a/src/core/primitives.c +++ b/src/core/primitives.c @@ -1,5 +1,6 @@ #include "primitives.h" #include "glib.h" +#include "src/core/matrix.h" #include "src/core/vector.h" #include #include @@ -78,9 +79,7 @@ void vektor_circle_set_radius(VektorCircle* circle, double radius) { circle->radius = radius; } -void vektor_circle_free(VektorCircle* circle) { - free(circle); -} +void vektor_circle_free(VektorCircle* circle) { free(circle); } VektorRectangle* vektor_rectangle_new(void) { VektorRectangle* rct = malloc(sizeof(VektorRectangle)); @@ -146,9 +145,8 @@ VektorBBox vektor_rectangle_get_bbox(VektorPrimitive prim) { VektorBBox vektor_circle_get_bbox(VektorPrimitive prim) { return (VektorBBox){ - vec2_sub(prim.circle.center, vec2_fromfloat(prim.circle.radius)), - vec2_add(prim.circle.center, vec2_fromfloat(prim.circle.radius)) - }; + vec2_sub(prim.circle.center, vec2_fromfloat(prim.circle.radius)), + vec2_add(prim.circle.center, vec2_fromfloat(prim.circle.radius))}; } VektorBBox vektor_primitive_get_bbox(VektorPrimitive prim) { @@ -178,84 +176,95 @@ VektorBBox vektor_primitive_get_bbox(VektorPrimitive prim) { // ------ PRIMITIVE HANDLES GENERATION ------ /* [n]: polyline vertices */ -void vektor_polyline_create_handles(VektorPolyline* polyline, V2** handleArr, size_t* count) { +void vektor_polyline_create_handles(VektorPolyline* polyline, V2** handleArr, + size_t* count) { *count = 0; *handleArr = NULL; } /* [n]: polygon vertices */ -void vektor_polygon_create_handles(VektorPolygon* polygon, V2** handleArr, size_t* count) { +void vektor_polygon_create_handles(VektorPolygon* polygon, V2** handleArr, + size_t* count) { *count = 0; *handleArr = NULL; } /* [0]: center; [1]: radius */ -void vektor_circle_create_handles(VektorCircle* circle, V2** handleArr, size_t* count) { +void vektor_circle_create_handles(VektorCircle* circle, V2** handleArr, + size_t* count) { *count = 2; - *handleArr = (V2*)malloc(sizeof(V2)*(*count)); + *handleArr = (V2*)malloc(sizeof(V2) * (*count)); (*handleArr)[0] = circle->center; (*handleArr)[1] = (V2){circle->radius + circle->center.x, circle->center.y}; } /* [0]: center; [1-4]: corners (l2r, t2b); */ -void vektor_rectangle_create_handles(VektorRectangle* rectangle, V2** handleArr, size_t* count) { +void vektor_rectangle_create_handles(VektorRectangle* rectangle, V2** handleArr, + size_t* count) { *count = 5; free(*handleArr); - *handleArr = (V2*)malloc(sizeof(V2)*(*count)); - + *handleArr = (V2*)malloc(sizeof(V2) * (*count)); + V2 halfdist = vec2_scale(vec2_sub(rectangle->end, rectangle->start), 0.5f); V2 center = vec2_add(rectangle->start, halfdist); g_print("boobs: %f %f\n", rectangle->start.x, rectangle->start.y); g_print("pussy: %f %f\n", rectangle->end.x, rectangle->end.y); (*handleArr)[0] = center; - (*handleArr)[1] = vec2_add( center, vec2_mul(halfdist, (V2){-1.0f, 1.0f}) ); - (*handleArr)[2] = vec2_add( center, halfdist); - (*handleArr)[3] = vec2_add( center, vec2_mul(halfdist, (V2){-1.0f, -1.0f}) ); - (*handleArr)[4] = vec2_add( center, vec2_mul(halfdist, (V2){1.0f, -1.0f}) ); + (*handleArr)[1] = vec2_add(center, vec2_mul(halfdist, (V2){-1.0f, 1.0f})); + (*handleArr)[2] = vec2_add(center, halfdist); + (*handleArr)[3] = vec2_add(center, vec2_mul(halfdist, (V2){-1.0f, -1.0f})); + (*handleArr)[4] = vec2_add(center, vec2_mul(halfdist, (V2){1.0f, -1.0f})); } void vektor_shape_create_handles(VektorShape* shape) { - switch(shape->primitive.kind) { - case VEKTOR_POLYLINE: - vektor_polyline_create_handles(shape->primitive.polyline, &shape->handles, &shape->handleCount); - break; - case VEKTOR_POLYGON: - vektor_polygon_create_handles(shape->primitive.polygon, &shape->handles, &shape->handleCount); - break; - case VEKTOR_CIRCLE: - vektor_circle_create_handles(&shape->primitive.circle, &shape->handles, &shape->handleCount); - break; - case VEKTOR_RECTANGLE: - vektor_rectangle_create_handles(&shape->primitive.rectangle, &shape->handles, &shape->handleCount); - break; + switch (shape->primitive.kind) { + case VEKTOR_POLYLINE: + vektor_polyline_create_handles(shape->primitive.polyline, + &shape->handles, &shape->handleCount); + break; + case VEKTOR_POLYGON: + vektor_polygon_create_handles(shape->primitive.polygon, &shape->handles, + &shape->handleCount); + break; + case VEKTOR_CIRCLE: + vektor_circle_create_handles(&shape->primitive.circle, &shape->handles, + &shape->handleCount); + break; + case VEKTOR_RECTANGLE: + vektor_rectangle_create_handles(&shape->primitive.rectangle, + &shape->handles, &shape->handleCount); + break; } } // ------ PRIMITIVE HANDLES UPDATING ------ -void vektor_polyline_handles_updated(VektorPolyline* polyline, V2** handles, size_t* count) { - if(*count != polyline->count) { +void vektor_polyline_handles_updated(VektorPolyline* polyline, V2** handles, + size_t* count) { + if (*count != polyline->count) { g_warning("handle count & point count mismatch in polyline"); return; } - for(size_t i = 0; i < *count; i++) { + for (size_t i = 0; i < *count; i++) { polyline->points[i] = (*handles)[i]; } } -void vektor_polygon_handles_updated(VektorPolygon* polygon, V2** handles, size_t* count) { - if(*count != polygon->count) { +void vektor_polygon_handles_updated(VektorPolygon* polygon, V2** handles, + size_t* count) { + if (*count != polygon->count) { g_warning("handle count & point count mismatch in polygon"); return; } - for(size_t i = 0; i < *count; i++) { + for (size_t i = 0; i < *count; i++) { polygon->points[i] = (*handles)[i]; } } -void vektor_circle_handles_updated(VektorCircle* circle, V2** handles, size_t* count) { - if(*count != 2) { +void vektor_circle_handles_updated(VektorCircle* circle, V2** handles, + size_t* count) { + if (*count != 2) { g_warning("unexpected circle handle count (%zu)", *count); return; } @@ -263,12 +272,13 @@ void vektor_circle_handles_updated(VektorCircle* circle, V2** handles, size_t* c circle->radius = vec2_length(vec2_sub((*handles)[0], (*handles)[1])); } -void vektor_rectangle_handles_updated(VektorRectangle* rectangle, V2** handles, size_t* count) { - if(*count != 5) { +void vektor_rectangle_handles_updated(VektorRectangle* rectangle, V2** handles, + size_t* count) { + if (*count != 5) { g_warning("unexpected rectangle handle count (%zu)", *count); return; } - + // get rectangle center V2 halfdist = vec2_scale(vec2_sub(rectangle->end, rectangle->start), 0.5f); V2 rectcenter = vec2_add(rectangle->start, halfdist); @@ -276,7 +286,7 @@ void vektor_rectangle_handles_updated(VektorRectangle* rectangle, V2** handles, // center according to handles V2 center = (*handles)[0]; - if(vec2_equals(center, rectcenter)) { // corner handles were changed + if (vec2_equals(center, rectcenter)) { // corner handles were changed V2 p1 = (*handles)[1]; V2 p2 = (*handles)[2]; V2 p3 = (*handles)[3]; @@ -290,7 +300,7 @@ void vektor_rectangle_handles_updated(VektorRectangle* rectangle, V2** handles, V2 min = (V2){min_x, min_y}; V2 max = (V2){max_x, max_y}; - VektorRectangle propertRect = (VektorRectangle){min,max}; + VektorRectangle propertRect = (VektorRectangle){min, max}; // overwrite handles array (create_handles() frees the passed one) vektor_rectangle_create_handles(&propertRect, handles, count); @@ -299,26 +309,29 @@ void vektor_rectangle_handles_updated(VektorRectangle* rectangle, V2** handles, V2 newmax = vec2_add((*handles)[2], translation); V2 newmin = vec2_add((*handles)[3], translation); - VektorRectangle propertRect = (VektorRectangle){newmin,newmax}; + VektorRectangle propertRect = (VektorRectangle){newmin, newmax}; vektor_rectangle_create_handles(&propertRect, handles, count); } - } void vektor_shape_handles_updated(VektorShape* shape) { - switch(shape->primitive.kind) { - case VEKTOR_POLYLINE: - vektor_polyline_handles_updated(shape->primitive.polyline, &shape->handles, &shape->handleCount); - break; - case VEKTOR_POLYGON: - vektor_polygon_handles_updated(shape->primitive.polygon, &shape->handles, &shape->handleCount); - break; - case VEKTOR_CIRCLE: - vektor_circle_handles_updated(&shape->primitive.circle, &shape->handles, &shape->handleCount); - break; - case VEKTOR_RECTANGLE: - vektor_rectangle_handles_updated(&shape->primitive.rectangle, &shape->handles, &shape->handleCount); - break; + switch (shape->primitive.kind) { + case VEKTOR_POLYLINE: + vektor_polyline_handles_updated(shape->primitive.polyline, + &shape->handles, &shape->handleCount); + break; + case VEKTOR_POLYGON: + vektor_polygon_handles_updated(shape->primitive.polygon, + &shape->handles, &shape->handleCount); + break; + case VEKTOR_CIRCLE: + vektor_circle_handles_updated(&shape->primitive.circle, &shape->handles, + &shape->handleCount); + break; + case VEKTOR_RECTANGLE: + vektor_rectangle_handles_updated(&shape->primitive.rectangle, + &shape->handles, &shape->handleCount); + break; } } @@ -327,7 +340,8 @@ void vektor_shape_add_handle(VektorShape* shape, V2 handle) { // but this function is only called when adding new // points to polyline and polygon, so it should // not be that much of an overhead - shape->handles = realloc(shape->handles, sizeof(V2) * shape->handleCount + 1); + shape->handles = + realloc(shape->handles, sizeof(V2) * shape->handleCount + 1); shape->handles[shape->handleCount++] = handle; } @@ -342,14 +356,12 @@ VektorBBox vektor_bbox_fromcenter(V2 center, float dist) { V2 v2dist = vec2_fromfloat(dist); V2 min = vec2_sub(center, v2dist); V2 max = vec2_add(center, v2dist); - return (VektorBBox){min,max}; + return (VektorBBox){min, max}; } VektorBBox vektor_bbox_expand(VektorBBox bbox, float val) { - return (VektorBBox){ - vec2_sub(bbox.min, vec2_fromfloat(val)), - vec2_add(bbox.max, vec2_fromfloat(val)) - }; + return (VektorBBox){vec2_sub(bbox.min, vec2_fromfloat(val)), + vec2_add(bbox.max, vec2_fromfloat(val))}; } // ------ SHAPE METHODS ------ @@ -357,14 +369,16 @@ VektorBBox vektor_bbox_expand(VektorBBox bbox, float val) { VektorShape vektor_shape_new(VektorPrimitive prim, VektorStyle style, int z_index) { VektorShape shape = (VektorShape){.primitive = prim, - .style = style, - .z_index = z_index, - .bbox = vektor_primitive_get_bbox(prim)}; + .style = style, + .transform = m33_identity(), + .z_index = z_index, + .bbox = vektor_primitive_get_bbox(prim)}; /* create_handles() allocates new buffer for handles, and even if the local shape variable goes out of scope and deallocates, - the passed value's pointer to an array of handles remains valid in the passed copy. + the passed value's pointer to an array of handles remains valid in the + passed copy. */ vektor_shape_create_handles(&shape); return shape; diff --git a/src/core/primitives.h b/src/core/primitives.h index ef75e79..dbd0d86 100644 --- a/src/core/primitives.h +++ b/src/core/primitives.h @@ -1,6 +1,7 @@ #ifndef PRIMITIVES_H_ #define PRIMITIVES_H_ +#include "src/core/matrix.h" #include "src/util/color.h" #include "stddef.h" #include "stdlib.h" @@ -58,6 +59,7 @@ typedef struct { typedef struct { VektorStyle style; int z_index; + M33 transform; VektorBBox bbox; VektorPrimitive primitive; @@ -83,7 +85,8 @@ void vektor_rectangle_set_end(VektorRectangle* rct, V2 point); void vektor_rectangle_set_start(VektorRectangle* rct, V2 point); void vektor_rectangle_free(VektorRectangle* rct); -VektorShape vektor_shape_new(VektorPrimitive prim, VektorStyle style, int z_index); +VektorShape vektor_shape_new(VektorPrimitive prim, VektorStyle style, + int z_index); VektorBBox vektor_polyline_get_bbox(VektorPrimitive prim); VektorBBox vektor_polygon_get_bbox(VektorPrimitive prim); @@ -96,18 +99,26 @@ VektorBBox vektor_bbox_fromcenter(V2 center, float dist); VektorBBox vektor_bbox_expand(VektorBBox bbox, float val); // shape handles -void vektor_polyline_create_handles(VektorPolyline* polyline, V2** handleArr, size_t* count); -void vektor_polygon_create_handles(VektorPolygon* polygon, V2** handleArr, size_t* count); -void vektor_circle_create_handles(VektorCircle* circle, V2** handleArr, size_t* count); -void vektor_rectangle_create_handles(VektorRectangle* rectangle, V2** handleArr, size_t* count); +void vektor_polyline_create_handles(VektorPolyline* polyline, V2** handleArr, + size_t* count); +void vektor_polygon_create_handles(VektorPolygon* polygon, V2** handleArr, + size_t* count); +void vektor_circle_create_handles(VektorCircle* circle, V2** handleArr, + size_t* count); +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); - /* reconstructs the shape based on handles alone */ -void vektor_polyline_handles_updated(VektorPolyline* polyline, V2** handles, size_t* count); -void vektor_polygon_handles_updated(VektorPolygon* polygon, V2** handles, size_t* count); -void vektor_circle_handles_updated(VektorCircle* circle, V2** handles, size_t* count); -void vektor_rectangle_handles_updated(VektorRectangle* rectangle, V2** handles, size_t* count); +/* reconstructs the shape based on handles alone */ +void vektor_polyline_handles_updated(VektorPolyline* polyline, V2** handles, + size_t* count); +void vektor_polygon_handles_updated(VektorPolygon* polygon, V2** handles, + size_t* count); +void vektor_circle_handles_updated(VektorCircle* circle, V2** handles, + size_t* count); +void vektor_rectangle_handles_updated(VektorRectangle* rectangle, V2** handles, + size_t* count); void vektor_shape_handles_updated(VektorShape* shape); typedef struct { diff --git a/src/core/raster.c b/src/core/raster.c index ae32298..dca16ba 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/matrix.h" #include "src/core/vector.h" #include "stddef.h" #include @@ -144,10 +145,14 @@ void vektor_edge_to_triangles(VertexBuffer* vb, Edge e, float py = dx / len * (shape_buffer->shapes[e.shape_id].style.stroke_width / 2); - V2 v0 = {e.p1.x + px, e.p1.y + py}; - V2 v1 = {e.p1.x - px, e.p1.y - py}; - V2 v2 = {e.p2.x + px, e.p2.y + py}; - V2 v3 = {e.p2.x - px, e.p2.y - py}; + V2 v0 = m33_transform(shape_buffer->shapes[e.shape_id].transform, + (V2){e.p1.x + px, e.p1.y + py}); + V2 v1 = m33_transform(shape_buffer->shapes[e.shape_id].transform, + (V2){e.p1.x - px, e.p1.y - py}); + V2 v2 = m33_transform(shape_buffer->shapes[e.shape_id].transform, + (V2){e.p2.x + px, e.p2.y + py}); + V2 v3 = m33_transform(shape_buffer->shapes[e.shape_id].transform, + (V2){e.p2.x - px, e.p2.y - py}); vektor_vb_add_triangle(vb, v0, v1, v2, shape_buffer->shapes[e.shape_id].style.stroke_color); diff --git a/src/core/vector.h b/src/core/vector.h index 84f45f2..354e8c4 100644 --- a/src/core/vector.h +++ b/src/core/vector.h @@ -14,7 +14,7 @@ typedef struct { double z; } V3; -static inline V2 vec2_fromfloat(const float f) { return (V2){f,f}; } +static inline V2 vec2_fromfloat(const float f) { return (V2){f, f}; } static inline V3 vec2_vector(const V2 v) { return (V3){v.x, v.y, 0}; } diff --git a/src/main.c b/src/main.c index 9220d99..1d35cdc 100644 --- a/src/main.c +++ b/src/main.c @@ -7,7 +7,6 @@ #include "./application/applicationstate.h" #include "./ui/uicontroller.h" - static int update_callback(gpointer data) { VektorAppState* appstate = (VektorAppState*)data; gtk_gl_area_queue_render( diff --git a/src/ui/vektorcanvas.c b/src/ui/vektorcanvas.c index d962a52..41b6b93 100644 --- a/src/ui/vektorcanvas.c +++ b/src/ui/vektorcanvas.c @@ -140,19 +140,20 @@ static gboolean render(GtkGLArea* a, GdkGLContext* ctx, vb.count = 0; vektor_rasterize(&vb, renderInfo->shapes, renderInfo->zoom); - size_t shape_vertex_count = vb.count; // remember how many vertices belong to shapes + size_t shape_vertex_count = + vb.count; // remember how many vertices belong to shapes - if (renderInfo->selectedShape != NULL && *(renderInfo->selectedShape) != NULL) { VektorShape* selectedShape = *renderInfo->selectedShape; // create handle quads if a shape is selected - for(size_t i = 0; i < selectedShape->handleCount; i++) { + for (size_t i = 0; i < selectedShape->handleCount; i++) { V2 handle = selectedShape->handles[i]; VektorBBox handleBbox = vektor_bbox_fromcenter(handle, 0.01f); - vektor_vb_add_quad(&vb, handleBbox.min, handleBbox.max, vektor_color_new(255, 255, 255, 255)); + vektor_vb_add_quad(&vb, handleBbox.min, handleBbox.max, + vektor_color_new(255, 255, 255, 255)); } shape_vertex_count = vb.count; @@ -164,12 +165,8 @@ static gboolean render(GtkGLArea* a, GdkGLContext* ctx, 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);