feat: add shape transforms

This commit is contained in:
2026-03-11 14:11:03 +00:00
parent 562cbc12da
commit 237bb02a8c
7 changed files with 129 additions and 110 deletions

View File

@@ -183,11 +183,9 @@ begin_click_dispatch:
vektor_shapes_update_bbox(state->shapeBuffer); vektor_shapes_update_bbox(state->shapeBuffer);
vektor_circle_create_handles( vektor_circle_create_handles(&state->selectedShape->primitive.circle,
&state->selectedShape->primitive.circle,
&state->selectedShape->handles, &state->selectedShape->handles,
&state->selectedShape->handleCount &state->selectedShape->handleCount);
);
} else if (state->selectedTool == VektorRectangleTool) { } else if (state->selectedTool == VektorRectangleTool) {
VektorRectangle* rect = vektor_rectangle_new(); VektorRectangle* rect = vektor_rectangle_new();
@@ -209,9 +207,7 @@ begin_click_dispatch:
vec2_add(pos, (V2){0.1f, 0.1f})); vec2_add(pos, (V2){0.1f, 0.1f}));
vektor_rectangle_create_handles( vektor_rectangle_create_handles(
&state->selectedShape->primitive.rectangle, &state->selectedShape->primitive.rectangle,
&state->selectedShape->handles, &state->selectedShape->handles, &state->selectedShape->handleCount);
&state->selectedShape->handleCount
);
// state->selectedShape = NULL; // state->selectedShape = NULL;
vektor_shapes_update_bbox(state->shapeBuffer); vektor_shapes_update_bbox(state->shapeBuffer);
@@ -242,12 +238,10 @@ void vektor_appstate_canvas_drag_begin(GtkGestureDrag* gesture, gdouble x,
V2 normalized_coords = V2 normalized_coords =
(V2){(2 * (x / widget_w)) - 1, 1 - (2 * (y / widget_h))}; (V2){(2 * (x / widget_w)) - 1, 1 - (2 * (y / widget_h))};
} }
void vektor_appstate_canvas_drag_update(GtkGestureDrag* gesture, void vektor_appstate_canvas_drag_update(GtkGestureDrag* gesture, gdouble x,
gdouble x, gdouble y, gdouble y, gpointer user_data) {
gpointer user_data) {
gdouble start_x, start_y; gdouble start_x, start_y;
gtk_gesture_drag_get_start_point(gesture, &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_w = gtk_widget_get_width(widget);
int widget_h = gtk_widget_get_height(widget); int widget_h = gtk_widget_get_height(widget);
V2 norm = V2 norm = (V2){(2 * ((x + start_x) / widget_w)) - 1,
(V2){(2 * ( (x+start_x) / widget_w)) - 1, 1 - (2 * ( (y+start_y) / widget_h))}; 1 - (2 * ((y + start_y) / widget_h))};
} }
void vektor_appstate_new(VektorWidgetState* wstate, VektorAppState* stateOut) { void vektor_appstate_new(VektorWidgetState* wstate, VektorAppState* stateOut) {

View File

@@ -1,5 +1,6 @@
#include "primitives.h" #include "primitives.h"
#include "glib.h" #include "glib.h"
#include "src/core/matrix.h"
#include "src/core/vector.h" #include "src/core/vector.h"
#include <assert.h> #include <assert.h>
#include <math.h> #include <math.h>
@@ -78,9 +79,7 @@ void vektor_circle_set_radius(VektorCircle* circle, double radius) {
circle->radius = radius; circle->radius = radius;
} }
void vektor_circle_free(VektorCircle* circle) { void vektor_circle_free(VektorCircle* circle) { free(circle); }
free(circle);
}
VektorRectangle* vektor_rectangle_new(void) { VektorRectangle* vektor_rectangle_new(void) {
VektorRectangle* rct = malloc(sizeof(VektorRectangle)); VektorRectangle* rct = malloc(sizeof(VektorRectangle));
@@ -147,8 +146,7 @@ VektorBBox vektor_rectangle_get_bbox(VektorPrimitive prim) {
VektorBBox vektor_circle_get_bbox(VektorPrimitive prim) { VektorBBox vektor_circle_get_bbox(VektorPrimitive prim) {
return (VektorBBox){ return (VektorBBox){
vec2_sub(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)) vec2_add(prim.circle.center, vec2_fromfloat(prim.circle.radius))};
};
} }
VektorBBox vektor_primitive_get_bbox(VektorPrimitive prim) { VektorBBox vektor_primitive_get_bbox(VektorPrimitive prim) {
@@ -178,19 +176,22 @@ VektorBBox vektor_primitive_get_bbox(VektorPrimitive prim) {
// ------ PRIMITIVE HANDLES GENERATION ------ // ------ PRIMITIVE HANDLES GENERATION ------
/* [n]: polyline vertices */ /* [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; *count = 0;
*handleArr = NULL; *handleArr = NULL;
} }
/* [n]: polygon vertices */ /* [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; *count = 0;
*handleArr = NULL; *handleArr = NULL;
} }
/* [0]: center; [1]: radius */ /* [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; *count = 2;
*handleArr = (V2*)malloc(sizeof(V2) * (*count)); *handleArr = (V2*)malloc(sizeof(V2) * (*count));
(*handleArr)[0] = circle->center; (*handleArr)[0] = circle->center;
@@ -198,7 +199,8 @@ void vektor_circle_create_handles(VektorCircle* circle, V2** handleArr, size_t*
} }
/* [0]: center; [1-4]: corners (l2r, t2b); */ /* [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; *count = 5;
free(*handleArr); free(*handleArr);
*handleArr = (V2*)malloc(sizeof(V2) * (*count)); *handleArr = (V2*)malloc(sizeof(V2) * (*count));
@@ -218,23 +220,28 @@ void vektor_rectangle_create_handles(VektorRectangle* rectangle, V2** handleArr,
void vektor_shape_create_handles(VektorShape* shape) { void vektor_shape_create_handles(VektorShape* shape) {
switch (shape->primitive.kind) { switch (shape->primitive.kind) {
case VEKTOR_POLYLINE: case VEKTOR_POLYLINE:
vektor_polyline_create_handles(shape->primitive.polyline, &shape->handles, &shape->handleCount); vektor_polyline_create_handles(shape->primitive.polyline,
&shape->handles, &shape->handleCount);
break; break;
case VEKTOR_POLYGON: case VEKTOR_POLYGON:
vektor_polygon_create_handles(shape->primitive.polygon, &shape->handles, &shape->handleCount); vektor_polygon_create_handles(shape->primitive.polygon, &shape->handles,
&shape->handleCount);
break; break;
case VEKTOR_CIRCLE: case VEKTOR_CIRCLE:
vektor_circle_create_handles(&shape->primitive.circle, &shape->handles, &shape->handleCount); vektor_circle_create_handles(&shape->primitive.circle, &shape->handles,
&shape->handleCount);
break; break;
case VEKTOR_RECTANGLE: case VEKTOR_RECTANGLE:
vektor_rectangle_create_handles(&shape->primitive.rectangle, &shape->handles, &shape->handleCount); vektor_rectangle_create_handles(&shape->primitive.rectangle,
&shape->handles, &shape->handleCount);
break; break;
} }
} }
// ------ PRIMITIVE HANDLES UPDATING ------ // ------ PRIMITIVE HANDLES UPDATING ------
void vektor_polyline_handles_updated(VektorPolyline* polyline, V2** handles, size_t* count) { void vektor_polyline_handles_updated(VektorPolyline* polyline, V2** handles,
size_t* count) {
if (*count != polyline->count) { if (*count != polyline->count) {
g_warning("handle count & point count mismatch in polyline"); g_warning("handle count & point count mismatch in polyline");
return; return;
@@ -244,7 +251,8 @@ void vektor_polyline_handles_updated(VektorPolyline* polyline, V2** handles, siz
} }
} }
void vektor_polygon_handles_updated(VektorPolygon* polygon, V2** handles, size_t* count) { void vektor_polygon_handles_updated(VektorPolygon* polygon, V2** handles,
size_t* count) {
if (*count != polygon->count) { if (*count != polygon->count) {
g_warning("handle count & point count mismatch in polygon"); g_warning("handle count & point count mismatch in polygon");
return; return;
@@ -254,7 +262,8 @@ void vektor_polygon_handles_updated(VektorPolygon* polygon, V2** handles, size_t
} }
} }
void vektor_circle_handles_updated(VektorCircle* circle, V2** handles, size_t* count) { void vektor_circle_handles_updated(VektorCircle* circle, V2** handles,
size_t* count) {
if (*count != 2) { if (*count != 2) {
g_warning("unexpected circle handle count (%zu)", *count); g_warning("unexpected circle handle count (%zu)", *count);
return; return;
@@ -263,7 +272,8 @@ void vektor_circle_handles_updated(VektorCircle* circle, V2** handles, size_t* c
circle->radius = vec2_length(vec2_sub((*handles)[0], (*handles)[1])); circle->radius = vec2_length(vec2_sub((*handles)[0], (*handles)[1]));
} }
void vektor_rectangle_handles_updated(VektorRectangle* rectangle, V2** handles, size_t* count) { void vektor_rectangle_handles_updated(VektorRectangle* rectangle, V2** handles,
size_t* count) {
if (*count != 5) { if (*count != 5) {
g_warning("unexpected rectangle handle count (%zu)", *count); g_warning("unexpected rectangle handle count (%zu)", *count);
return; return;
@@ -302,22 +312,25 @@ void vektor_rectangle_handles_updated(VektorRectangle* rectangle, V2** handles,
VektorRectangle propertRect = (VektorRectangle){newmin, newmax}; VektorRectangle propertRect = (VektorRectangle){newmin, newmax};
vektor_rectangle_create_handles(&propertRect, handles, count); vektor_rectangle_create_handles(&propertRect, handles, count);
} }
} }
void vektor_shape_handles_updated(VektorShape* shape) { void vektor_shape_handles_updated(VektorShape* shape) {
switch (shape->primitive.kind) { switch (shape->primitive.kind) {
case VEKTOR_POLYLINE: case VEKTOR_POLYLINE:
vektor_polyline_handles_updated(shape->primitive.polyline, &shape->handles, &shape->handleCount); vektor_polyline_handles_updated(shape->primitive.polyline,
&shape->handles, &shape->handleCount);
break; break;
case VEKTOR_POLYGON: case VEKTOR_POLYGON:
vektor_polygon_handles_updated(shape->primitive.polygon, &shape->handles, &shape->handleCount); vektor_polygon_handles_updated(shape->primitive.polygon,
&shape->handles, &shape->handleCount);
break; break;
case VEKTOR_CIRCLE: case VEKTOR_CIRCLE:
vektor_circle_handles_updated(&shape->primitive.circle, &shape->handles, &shape->handleCount); vektor_circle_handles_updated(&shape->primitive.circle, &shape->handles,
&shape->handleCount);
break; break;
case VEKTOR_RECTANGLE: case VEKTOR_RECTANGLE:
vektor_rectangle_handles_updated(&shape->primitive.rectangle, &shape->handles, &shape->handleCount); vektor_rectangle_handles_updated(&shape->primitive.rectangle,
&shape->handles, &shape->handleCount);
break; break;
} }
} }
@@ -327,7 +340,8 @@ void vektor_shape_add_handle(VektorShape* shape, V2 handle) {
// but this function is only called when adding new // but this function is only called when adding new
// points to polyline and polygon, so it should // points to polyline and polygon, so it should
// not be that much of an overhead // 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; shape->handles[shape->handleCount++] = handle;
} }
@@ -346,10 +360,8 @@ VektorBBox vektor_bbox_fromcenter(V2 center, float dist) {
} }
VektorBBox vektor_bbox_expand(VektorBBox bbox, float val) { VektorBBox vektor_bbox_expand(VektorBBox bbox, float val) {
return (VektorBBox){ return (VektorBBox){vec2_sub(bbox.min, vec2_fromfloat(val)),
vec2_sub(bbox.min, vec2_fromfloat(val)), vec2_add(bbox.max, vec2_fromfloat(val))};
vec2_add(bbox.max, vec2_fromfloat(val))
};
} }
// ------ SHAPE METHODS ------ // ------ SHAPE METHODS ------
@@ -358,13 +370,15 @@ VektorShape vektor_shape_new(VektorPrimitive prim, VektorStyle style,
int z_index) { int z_index) {
VektorShape shape = (VektorShape){.primitive = prim, VektorShape shape = (VektorShape){.primitive = prim,
.style = style, .style = style,
.transform = m33_identity(),
.z_index = z_index, .z_index = z_index,
.bbox = vektor_primitive_get_bbox(prim)}; .bbox = vektor_primitive_get_bbox(prim)};
/* /*
create_handles() allocates new buffer for handles, create_handles() allocates new buffer for handles,
and even if the local shape variable goes out of scope and deallocates, 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); vektor_shape_create_handles(&shape);
return shape; return shape;

View File

@@ -1,6 +1,7 @@
#ifndef PRIMITIVES_H_ #ifndef PRIMITIVES_H_
#define PRIMITIVES_H_ #define PRIMITIVES_H_
#include "src/core/matrix.h"
#include "src/util/color.h" #include "src/util/color.h"
#include "stddef.h" #include "stddef.h"
#include "stdlib.h" #include "stdlib.h"
@@ -58,6 +59,7 @@ typedef struct {
typedef struct { typedef struct {
VektorStyle style; VektorStyle style;
int z_index; int z_index;
M33 transform;
VektorBBox bbox; VektorBBox bbox;
VektorPrimitive primitive; 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_set_start(VektorRectangle* rct, V2 point);
void vektor_rectangle_free(VektorRectangle* rct); 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_polyline_get_bbox(VektorPrimitive prim);
VektorBBox vektor_polygon_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); VektorBBox vektor_bbox_expand(VektorBBox bbox, float val);
// shape handles // shape handles
void vektor_polyline_create_handles(VektorPolyline* polyline, V2** handleArr, size_t* count); void vektor_polyline_create_handles(VektorPolyline* polyline, V2** handleArr,
void vektor_polygon_create_handles(VektorPolygon* polygon, V2** handleArr, size_t* count); size_t* count);
void vektor_circle_create_handles(VektorCircle* circle, V2** handleArr, size_t* count); void vektor_polygon_create_handles(VektorPolygon* polygon, V2** handleArr,
void vektor_rectangle_create_handles(VektorRectangle* rectangle, V2** handleArr, size_t* count); 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_create_handles(VektorShape* shape);
void vektor_shape_add_handle(VektorShape* shape, V2 handle); void vektor_shape_add_handle(VektorShape* shape, V2 handle);
/* reconstructs the shape based on handles alone */ /* reconstructs the shape based on handles alone */
void vektor_polyline_handles_updated(VektorPolyline* polyline, V2** handles, size_t* count); void vektor_polyline_handles_updated(VektorPolyline* polyline, V2** handles,
void vektor_polygon_handles_updated(VektorPolygon* polygon, V2** handles, size_t* count); size_t* count);
void vektor_circle_handles_updated(VektorCircle* circle, V2** handles, size_t* count); void vektor_polygon_handles_updated(VektorPolygon* polygon, V2** handles,
void vektor_rectangle_handles_updated(VektorRectangle* rectangle, V2** handles, size_t* count); 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); void vektor_shape_handles_updated(VektorShape* shape);
typedef struct { typedef struct {

View File

@@ -1,6 +1,7 @@
#include "raster.h" #include "raster.h"
#include "epoxy/gl.h" #include "epoxy/gl.h"
#include "primitives.h" #include "primitives.h"
#include "src/core/matrix.h"
#include "src/core/vector.h" #include "src/core/vector.h"
#include "stddef.h" #include "stddef.h"
#include <math.h> #include <math.h>
@@ -144,10 +145,14 @@ void vektor_edge_to_triangles(VertexBuffer* vb, Edge e,
float py = float py =
dx / len * (shape_buffer->shapes[e.shape_id].style.stroke_width / 2); dx / len * (shape_buffer->shapes[e.shape_id].style.stroke_width / 2);
V2 v0 = {e.p1.x + px, e.p1.y + py}; V2 v0 = m33_transform(shape_buffer->shapes[e.shape_id].transform,
V2 v1 = {e.p1.x - px, e.p1.y - py}; (V2){e.p1.x + px, e.p1.y + py});
V2 v2 = {e.p2.x + px, e.p2.y + py}; V2 v1 = m33_transform(shape_buffer->shapes[e.shape_id].transform,
V2 v3 = {e.p2.x - px, e.p2.y - py}; (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, vektor_vb_add_triangle(vb, v0, v1, v2,
shape_buffer->shapes[e.shape_id].style.stroke_color); shape_buffer->shapes[e.shape_id].style.stroke_color);

View File

@@ -7,7 +7,6 @@
#include "./application/applicationstate.h" #include "./application/applicationstate.h"
#include "./ui/uicontroller.h" #include "./ui/uicontroller.h"
static int update_callback(gpointer data) { static int update_callback(gpointer data) {
VektorAppState* appstate = (VektorAppState*)data; VektorAppState* appstate = (VektorAppState*)data;
gtk_gl_area_queue_render( gtk_gl_area_queue_render(

View File

@@ -140,8 +140,8 @@ static gboolean render(GtkGLArea* a, GdkGLContext* ctx,
vb.count = 0; vb.count = 0;
vektor_rasterize(&vb, renderInfo->shapes, renderInfo->zoom); 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 && if (renderInfo->selectedShape != NULL &&
*(renderInfo->selectedShape) != NULL) { *(renderInfo->selectedShape) != NULL) {
@@ -152,7 +152,8 @@ static gboolean render(GtkGLArea* a, GdkGLContext* ctx,
for (size_t i = 0; i < selectedShape->handleCount; i++) { for (size_t i = 0; i < selectedShape->handleCount; i++) {
V2 handle = selectedShape->handles[i]; V2 handle = selectedShape->handles[i];
VektorBBox handleBbox = vektor_bbox_fromcenter(handle, 0.01f); 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; 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_vb_add_quad(&vb, bbox.min, bbox.max,
vektor_color_new(255, 255, 255, 255)); vektor_color_new(255, 255, 255, 255));
} }
glBufferData(GL_ARRAY_BUFFER, vb.count * sizeof(Vertex), vb.vertices, glBufferData(GL_ARRAY_BUFFER, vb.count * sizeof(Vertex), vb.vertices,
GL_STATIC_DRAW); GL_STATIC_DRAW);