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

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

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);
@@ -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 {

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