Compare commits

...

2 Commits

Author SHA1 Message Date
237bb02a8c feat: add shape transforms 2026-03-11 14:11:03 +00:00
Beriff
562cbc12da feat: handle drawing 2026-03-11 21:04:11 +07:00
7 changed files with 177 additions and 89 deletions

View File

@@ -131,6 +131,10 @@ begin_click_dispatch:
vektor_polyline_add_point(state->selectedShape->primitive.polyline,
pos);
vektor_shapes_update_bbox(state->shapeBuffer);
// polyline's handle count is not fixed, so we have to add them manually
vektor_shape_add_handle(state->selectedShape, pos);
} else if (state->selectedTool == VektorPolygonTool) {
// create new polygon shape if none is selected
if (state->selectedShape == NULL) {
@@ -155,6 +159,10 @@ begin_click_dispatch:
vektor_polygon_add_point(state->selectedShape->primitive.polygon, pos);
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);
} else if (state->selectedTool == VektorCircleTool) {
VektorCircle* circle = vektor_circle_new();
@@ -174,6 +182,10 @@ begin_click_dispatch:
vektor_circle_set_radius(&state->selectedShape->primitive.circle, 0.1f);
vektor_shapes_update_bbox(state->shapeBuffer);
vektor_circle_create_handles(&state->selectedShape->primitive.circle,
&state->selectedShape->handles,
&state->selectedShape->handleCount);
} else if (state->selectedTool == VektorRectangleTool) {
VektorRectangle* rect = vektor_rectangle_new();
@@ -193,6 +205,10 @@ begin_click_dispatch:
pos);
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 = NULL;
vektor_shapes_update_bbox(state->shapeBuffer);
} else if (state->selectedTool == VektorSelectionTool) {
@@ -207,6 +223,9 @@ begin_click_dispatch:
// was clicked outside any shapes - reset selection
state->selectedShape = NULL;
}
if (state->selectedShape != NULL)
g_print("%zu\n", state->selectedShape->handleCount);
}
void vektor_appstate_canvas_drag_begin(GtkGestureDrag* gesture, gdouble x,
@@ -219,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);
@@ -234,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) {

View File

@@ -1,5 +1,6 @@
#include "primitives.h"
#include "glib.h"
#include "src/core/matrix.h"
#include "src/core/vector.h"
#include <assert.h>
#include <math.h>
@@ -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));
@@ -147,8 +146,7 @@ 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_add(prim.circle.center, vec2_fromfloat(prim.circle.radius))};
}
VektorBBox vektor_primitive_get_bbox(VektorPrimitive prim) {
@@ -178,82 +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) {
switch (shape->primitive.kind) {
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;
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;
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;
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;
}
}
// ------ 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;
}
@@ -261,8 +272,9 @@ 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;
}
@@ -274,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];
@@ -288,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);
@@ -297,29 +309,42 @@ 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) {
switch (shape->primitive.kind) {
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;
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;
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;
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;
}
}
void vektor_shape_add_handle(VektorShape* shape, V2 handle) {
// could be optimised with capacity property
// 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[shape->handleCount++] = handle;
}
// ------ BBOX METHODS ------
bool vektor_bbox_isinside(VektorBBox bbox, V2 point) {
@@ -331,17 +356,32 @@ 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))};
}
// ------ SHAPE METHODS ------
VektorShape vektor_shape_new(VektorPrimitive prim, VektorStyle style,
int z_index) {
return (VektorShape){.primitive = prim,
VektorShape shape = (VektorShape){.primitive = 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.
*/
vektor_shape_create_handles(&shape);
return shape;
}
void vektor_shapes_update_bbox(VektorShapeBuffer* buffer) {

View File

@@ -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);
@@ -93,19 +96,29 @@ VektorBBox vektor_rectangle_get_bbox(VektorPrimitive prim);
VektorBBox vektor_primitive_get_bbox(VektorPrimitive prim);
bool vektor_bbox_isinside(VektorBBox bbox, V2 point);
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 {

View File

@@ -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 <math.h>
@@ -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);

View File

@@ -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}; }

View File

@@ -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(

View File

@@ -143,11 +143,25 @@ static gboolean render(GtkGLArea* a, GdkGLContext* ctx,
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);
VektorShape* selectedShape = *renderInfo->selectedShape;
// create handle quads if a shape is selected
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));
}
shape_vertex_count = vb.count;
// create selection quad if a shape is selected
VektorBBox bbox = vektor_primitive_get_bbox(selectedShape->primitive);
// expand it a little so it is not inset
bbox = vektor_bbox_expand(bbox, 0.03f);
vektor_vb_add_quad(&vb, bbox.min, bbox.max,
vektor_color_new(255, 255, 255, 255));
@@ -180,6 +194,7 @@ static gboolean render(GtkGLArea* a, GdkGLContext* ctx,
// re-fetch bbox (we know a shape is selected)
VektorBBox bbox = vektor_primitive_get_bbox(
(*(renderInfo->selectedShape))->primitive);
bbox = vektor_bbox_expand(bbox, 0.03f);
glUseProgram(selection_shader_program);