Compare commits

...

6 Commits

Author SHA1 Message Date
Beriff
90eec8d301 chore: add modifier pipeline 2026-03-15 16:30:49 +07:00
Beriff
e9d1e5c47e fix(minor): link geometry_updated signal to zooming 2026-03-14 19:41:57 +07:00
31b2196976 fix: add bevel edge case to miter join 2026-03-14 12:31:03 +00:00
Beriff
bd586cda6a fix: selection quad bug 2026-03-14 18:49:52 +07:00
Beriff
ae58a60be9 Merge branch 'main' of https://bundleofsticks.store/git/Frox/Vektor 2026-03-14 18:49:02 +07:00
Beriff
43b6d284dd chore: refactor to use shape nodes 2026-03-14 18:25:02 +07:00
15 changed files with 317 additions and 76 deletions

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 22 22">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#444444; } .ColorScheme-Highlight { color:#4285f4; } .ColorScheme-NeutralText { color:#ff9800; } .ColorScheme-PositiveText { color:#4caf50; } .ColorScheme-NegativeText { color:#f44336; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 14.574219 1.0058594 C 13.520146 0.87298937 10.770478 2.75775 8.0605469 5.46875 C 6.8520776 6.67795 5.8032796 7.8729 5 9 C 5.9414561 9.29995 6.7002076 10.0582 7 11 C 8.1266713 10.19649 9.3243336 9.1522594 10.533203 7.9433594 C 13.607725 4.8675594 15.546263 1.8205187 14.863281 1.1367188 C 14.793083 1.0660188 14.697306 1.0216494 14.574219 1.0058594 z M 4.5 10.330078 L 4.5 10.332031 C 1.0001889 11.270271 3.6248533 13.4865 1 15 C 4.4998111 15 6.25 13.248178 6.25 12.080078 C 6.25 11.497798 6.3093545 10.426978 4.5 10.330078 z" transform="translate(3 3)"/>
</svg>

After

Width:  |  Height:  |  Size: 1005 B

View File

@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" version="1.1">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#444444; } .ColorScheme-Highlight { color:#4285f4; } .ColorScheme-NeutralText { color:#ff9800; } .ColorScheme-PositiveText { color:#4caf50; } .ColorScheme-NegativeText { color:#f44336; }
</style>
</defs>
<g transform="translate(3,3)">
<path style="fill:currentColor" class="ColorScheme-Text" d="M 12.778,1.2222 C 12.778,1.2222 12.278,0.72224 11.778,1.2222 L 10,3 13,6 14.778,4.2222 C 15.278,3.7222 14.778,3.2222 14.778,3.2222 Z M 9,4 1,12 V 15 H 4 L 12,7 Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 646 B

View File

@@ -25,6 +25,7 @@ src = files(
'src/core/matrix.c', 'src/core/matrix.c',
'src/core/primitives.c', 'src/core/primitives.c',
'src/core/raster.c', 'src/core/raster.c',
'src/core/modifier.c',
'src/ui/uicontroller.c', 'src/ui/uicontroller.c',
'src/ui/vektorcanvas.c', 'src/ui/vektorcanvas.c',
'src/ui/widgets/colorwheel.c', 'src/ui/widgets/colorwheel.c',

View File

@@ -1,4 +1,5 @@
#include "src/core/matrix.h" #include "src/core/matrix.h"
#include "src/core/modifier.h"
#include "src/ui/uicontroller.h" #include "src/ui/uicontroller.h"
#include "stdlib.h" #include "stdlib.h"
@@ -7,7 +8,7 @@
#include "gtk/gtk.h" #include "gtk/gtk.h"
#include "gtk/gtkrevealer.h" #include "gtk/gtkrevealer.h"
#include "src/core/primitives.h" #include "src/core/primitives.h"
#include "src/core/raster.h"
#include "src/ui/vektorcanvas.h" #include "src/ui/vektorcanvas.h"
#include "src/ui/widgets/colorwheel.h" #include "src/ui/widgets/colorwheel.h"
#include "src/util/color.h" #include "src/util/color.h"
@@ -46,7 +47,7 @@ static void appstate_on_color_change(VektorColorWheel* wheel,
appstate->currentColor = c; appstate->currentColor = c;
if (appstate->selectedShape != NULL) { if (appstate->selectedShape != NULL) {
appstate->selectedShape->style.stroke_color = c; appstate->selectedShape->base->style.stroke_color = c;
} }
// set entry fields under the color selector // set entry fields under the color selector
@@ -117,13 +118,15 @@ begin_click_dispatch:
VektorStyle style = (VektorStyle){ VektorStyle style = (VektorStyle){
.stroke_color = state->currentColor, .stroke_width = 0.01}; .stroke_color = state->currentColor, .stroke_width = 0.01};
vektor_shapebuffer_add_shape( vektor_shapenodebuf_add(state->shapeBuffer,
state->shapeBuffer, vektor_shape_new(linePrimitive, style, 0)); vektor_shapenode_new(vektor_shape_new(
linePrimitive, style, 0)));
state->selectedShape = state->selectedShape =
&(state->shapeBuffer->shapes[state->shapeBuffer->count - 1]); &(state->shapeBuffer->nodes[state->shapeBuffer->count - 1]);
} else if (state->selectedShape->primitive.kind != VEKTOR_POLYLINE) { } else if (state->selectedShape->base->primitive.kind !=
VEKTOR_POLYLINE) {
// selecting a tool resets the selection, so this condition // selecting a tool resets the selection, so this condition
// should not happen // should not happen
g_warning("Invalid selected primitive; polyline expected"); g_warning("Invalid selected primitive; polyline expected");
@@ -131,12 +134,13 @@ begin_click_dispatch:
goto begin_click_dispatch; // retry goto begin_click_dispatch; // retry
} }
vektor_polyline_add_point(state->selectedShape->primitive.polyline, vektor_polyline_add_point(state->selectedShape->base->primitive.polyline,
pos); pos);
vektor_shapes_update_bbox(state->shapeBuffer); state->selectedShape->base->bbox =
vektor_primitive_get_bbox(state->selectedShape->base->primitive);
// polyline's handle count is not fixed, so we have to add them manually // polyline'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->base, pos);
} else if (state->selectedTool == VektorPolygonTool) { } else if (state->selectedTool == VektorPolygonTool) {
// create new polygon shape if none is selected // create new polygon shape if none is selected
@@ -147,24 +151,27 @@ begin_click_dispatch:
(VektorPrimitive){.kind = VEKTOR_POLYGON, .polygon = polygon}; (VektorPrimitive){.kind = VEKTOR_POLYGON, .polygon = polygon};
VektorStyle style = (VektorStyle){ VektorStyle style = (VektorStyle){
.stroke_color = state->currentColor, .stroke_width = 0.01}; .stroke_color = state->currentColor, .stroke_width = 0.01};
vektor_shapebuffer_add_shape( vektor_shapenodebuf_add(state->shapeBuffer,
state->shapeBuffer, vektor_shapenode_new(vektor_shape_new(
vektor_shape_new(polygonPrimitive, style, 0)); polygonPrimitive, style, 0)));
state->selectedShape = state->selectedShape =
&(state->shapeBuffer->shapes[state->shapeBuffer->count - 1]); &(state->shapeBuffer->nodes[state->shapeBuffer->count - 1]);
} else if (state->selectedShape->primitive.kind != VEKTOR_POLYGON) { } else if (state->selectedShape->base->primitive.kind !=
VEKTOR_POLYGON) {
g_warning("Invalid selected primitive; polygon expected"); g_warning("Invalid selected primitive; polygon expected");
vektor_appstate_deselect_shape(state); vektor_appstate_deselect_shape(state);
goto begin_click_dispatch; // retry goto begin_click_dispatch; // retry
} }
vektor_polygon_add_point(state->selectedShape->primitive.polygon, pos); vektor_polygon_add_point(state->selectedShape->base->primitive.polygon,
vektor_shapes_update_bbox(state->shapeBuffer); pos);
state->selectedShape->base->bbox =
vektor_primitive_get_bbox(state->selectedShape->base->primitive);
// polygon's handle count is not fixed, so we have to add them manually // 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->base, pos);
} else if (state->selectedTool == VektorCircleTool) { } else if (state->selectedTool == VektorCircleTool) {
@@ -173,22 +180,27 @@ begin_click_dispatch:
(VektorPrimitive){.kind = VEKTOR_CIRCLE, .circle = *circle}; (VektorPrimitive){.kind = VEKTOR_CIRCLE, .circle = *circle};
VektorStyle style = (VektorStyle){.stroke_color = state->currentColor, VektorStyle style = (VektorStyle){.stroke_color = state->currentColor,
.stroke_width = 0.01}; .stroke_width = 0.01};
vektor_shapebuffer_add_shape( vektor_shapenodebuf_add(
state->shapeBuffer, vektor_shape_new(circlePrimitive, style, 0)); state->shapeBuffer,
vektor_shapenode_new(vektor_shape_new(circlePrimitive, style, 0)));
state->selectedShape = state->selectedShape =
&(state->shapeBuffer->shapes[state->shapeBuffer->count - 1]); &(state->shapeBuffer->nodes[state->shapeBuffer->count - 1]);
vektor_circle_free(circle); vektor_circle_free(circle);
vektor_circle_set_center(&state->selectedShape->primitive.circle, pos); vektor_circle_set_center(&state->selectedShape->base->primitive.circle,
vektor_circle_set_radius(&state->selectedShape->primitive.circle, 0.1f); pos);
vektor_circle_set_radius(&state->selectedShape->base->primitive.circle,
0.1f);
vektor_shapes_update_bbox(state->shapeBuffer); state->selectedShape->base->bbox =
vektor_primitive_get_bbox(state->selectedShape->base->primitive);
vektor_circle_create_handles(&state->selectedShape->primitive.circle, vektor_circle_create_handles(
&state->selectedShape->handles, &state->selectedShape->base->primitive.circle,
&state->selectedShape->handleCount); &state->selectedShape->base->handles,
&state->selectedShape->base->handleCount);
} else if (state->selectedTool == VektorRectangleTool) { } else if (state->selectedTool == VektorRectangleTool) {
VektorRectangle* rect = vektor_rectangle_new(); VektorRectangle* rect = vektor_rectangle_new();
@@ -196,35 +208,39 @@ begin_click_dispatch:
(VektorPrimitive){.kind = VEKTOR_RECTANGLE, .rectangle = *rect}; (VektorPrimitive){.kind = VEKTOR_RECTANGLE, .rectangle = *rect};
VektorStyle style = (VektorStyle){.stroke_color = state->currentColor, VektorStyle style = (VektorStyle){.stroke_color = state->currentColor,
.stroke_width = 0.01}; .stroke_width = 0.01};
vektor_shapebuffer_add_shape(state->shapeBuffer, vektor_shapenodebuf_add(
vektor_shape_new(rectPrimitive, style, 0)); state->shapeBuffer,
vektor_shapenode_new(vektor_shape_new(rectPrimitive, style, 0)));
state->selectedShape = state->selectedShape =
&(state->shapeBuffer->shapes[state->shapeBuffer->count - 1]); &(state->shapeBuffer->nodes[state->shapeBuffer->count - 1]);
vektor_rectangle_free(rect); vektor_rectangle_free(rect);
vektor_rectangle_set_start(&state->selectedShape->primitive.rectangle, vektor_rectangle_set_start(
pos); &state->selectedShape->base->primitive.rectangle, pos);
vektor_rectangle_set_end(&state->selectedShape->primitive.rectangle, vektor_rectangle_set_end(
vec2_add(pos, (V2){0.1f, 0.1f})); &state->selectedShape->base->primitive.rectangle,
vec2_add(pos, (V2){0.1f, 0.1f}));
vektor_rectangle_create_handles( vektor_rectangle_create_handles(
&state->selectedShape->primitive.rectangle, &state->selectedShape->base->primitive.rectangle,
&state->selectedShape->handles, &state->selectedShape->handleCount); &state->selectedShape->base->handles,
&state->selectedShape->base->handleCount);
state->selectedShape->base->bbox =
vektor_primitive_get_bbox(state->selectedShape->base->primitive);
// state->selectedShape = NULL;
vektor_shapes_update_bbox(state->shapeBuffer);
} else if (state->selectedTool == VektorSelectionTool) { } else if (state->selectedTool == VektorSelectionTool) {
for (size_t i = 0; i < state->shapeBuffer->count; i++) { for (size_t i = 0; i < state->shapeBuffer->count; i++) {
VektorBBox bbox = vektor_primitive_get_bbox( VektorBBox bbox = vektor_primitive_get_bbox(
state->shapeBuffer->shapes[i].primitive); state->shapeBuffer->nodes[i].base->primitive);
// expand the bbox a little so its not painful to // expand the bbox a little so its not painful to
// try to grab handles located on the border of said bbox // try to grab handles located on the border of said bbox
bbox = vektor_bbox_expand(bbox, 0.02); bbox = vektor_bbox_expand(bbox, 0.02);
if (vektor_bbox_isinside(bbox, pos)) { if (vektor_bbox_isinside(bbox, pos)) {
state->selectedShape = &(state->shapeBuffer->shapes[i]); state->selectedShape = &(state->shapeBuffer->nodes[i]);
return; return;
} }
} }
@@ -248,13 +264,13 @@ void vektor_appstate_canvas_drag_begin(GtkGestureDrag* gesture, gdouble x,
(V2){position.x, position.y}); (V2){position.x, position.y});
if (state->selectedShape != NULL) { if (state->selectedShape != NULL) {
VektorShape* selectedShape = state->selectedShape; VektorShapeNode* selectedShape = state->selectedShape;
// get selected shape's handles and check // get selected shape's handles and check
// if we click any of them // if we click any of them
for (size_t i = 0; i < selectedShape->handleCount; i++) { for (size_t i = 0; i < selectedShape->base->handleCount; i++) {
VektorBBox bbox = VektorBBox bbox =
vektor_shape_get_handle_bbox(selectedShape->handles[i]); vektor_shape_get_handle_bbox(selectedShape->base->handles[i]);
if (vektor_bbox_isinside(bbox, position)) { if (vektor_bbox_isinside(bbox, position)) {
// clicked inside handle // clicked inside handle
state->heldHandleIndex = i; state->heldHandleIndex = i;
@@ -287,8 +303,8 @@ void vektor_appstate_canvas_drag_update(GtkGestureDrag* gesture, gdouble x,
// drag handle if selected // drag handle if selected
if (state->selectedShape != NULL && state->heldHandleIndex != -1) { if (state->selectedShape != NULL && state->heldHandleIndex != -1) {
state->selectedShape->handles[state->heldHandleIndex] = position; state->selectedShape->base->handles[state->heldHandleIndex] = position;
vektor_shape_handles_updated(state->selectedShape, vektor_shape_handles_updated(state->selectedShape->base,
&state->heldHandleIndex); &state->heldHandleIndex);
vektor_canvas_geometry_changed(state->renderInfo); vektor_canvas_geometry_changed(state->renderInfo);
} }
@@ -337,8 +353,8 @@ void vektor_appstate_new(VektorWidgetState* wstate, VektorAppState* stateOut) {
// populate appstate // populate appstate
stateOut->startupTime = g_get_monotonic_time(); stateOut->startupTime = g_get_monotonic_time();
stateOut->shapeBuffer = malloc(sizeof(VektorShapeBuffer)); stateOut->shapeBuffer = malloc(sizeof(VektorShapeNodeBuffer));
*stateOut->shapeBuffer = (VektorShapeBuffer){0}; *stateOut->shapeBuffer = (VektorShapeNodeBuffer){0};
stateOut->canvas = malloc(sizeof(VektorCanvas)); stateOut->canvas = malloc(sizeof(VektorCanvas));
stateOut->widgetState = wstate; stateOut->widgetState = wstate;
stateOut->currentColor = vektor_color_solid(0, 0, 0); stateOut->currentColor = vektor_color_solid(0, 0, 0);

View File

@@ -4,7 +4,7 @@
#include "../core/primitives.h" #include "../core/primitives.h"
#include "../ui/uicontroller.h" #include "../ui/uicontroller.h"
#include "../ui/vektorcanvas.h" #include "../ui/vektorcanvas.h"
#include "src/core/raster.h" #include "src/core/modifier.h"
typedef enum VektorAppTool { typedef enum VektorAppTool {
VektorSelectionTool, VektorSelectionTool,
@@ -20,13 +20,13 @@ typedef struct VektorAppState {
VektorWidgetState* widgetState; VektorWidgetState* widgetState;
VektorAppTool selectedTool; VektorAppTool selectedTool;
VektorShape* selectedShape; VektorShapeNode* selectedShape;
int heldHandleIndex; int heldHandleIndex;
VektorColor currentColor; VektorColor currentColor;
// Logic space // Logic space
VektorShapeBuffer* shapeBuffer; VektorShapeNodeBuffer* shapeBuffer;
// View space // View space
VektorCanvas* canvas; VektorCanvas* canvas;
VektorCanvasRenderInfo* renderInfo; VektorCanvasRenderInfo* renderInfo;

83
src/core/modifier.c Normal file
View File

@@ -0,0 +1,83 @@
#include "modifier.h"
#include <glib.h>
VektorShapeNode vektor_shapenode_new(VektorShape* shape) {
VektorShapeNode node = (VektorShapeNode){
.base = shape,
.modifier_count = 0,
.evaluated = shape,
.base_dirty = true
};
return node;
}
void vektor_shapenode_free(VektorShapeNode* shapeNode) {
if(shapeNode->base == shapeNode->evaluated) {
free(shapeNode->base); // avoid double free()
} else {
free(shapeNode->base);
free(shapeNode->evaluated);
}
free(shapeNode->modifiers);
}
VektorShape* vektor_shapenode_get_evaluated(VektorShapeNode* shapeNode) {
return shapeNode->evaluated;
}
VektorShape vektor_modifier_apply(VektorModifier* mod, VektorShape* input) {
mod->cachedEvaluatedShape = mod->apply(mod, input);
return mod->cachedEvaluatedShape;
}
// lots of copies by value here, could be problematic
void vektor_shapenode_update(VektorShapeNode* shapeNode) {
// if the base is dirty, apply EVERY modifier
if(shapeNode->base_dirty) {
VektorShape* evaluated = shapeNode->base;
for(size_t i = 0; i < shapeNode->modifier_count; i++) {
*evaluated = vektor_modifier_apply(&shapeNode->modifiers[i], evaluated);
shapeNode->modifiers[i].dirty = false;
}
shapeNode->evaluated = evaluated;
shapeNode->base_dirty = false;
return;
}
// if the base is not dirty, start applying modifiers upstream
// starting from the first dirty
bool encountered_dirty = false;
for(size_t i = 0; i < shapeNode->modifier_count; i++) {
if(shapeNode->modifiers[i].dirty) { encountered_dirty = true; }
if(encountered_dirty) {
if(i == 0) {
vektor_modifier_apply(&shapeNode->modifiers[i], shapeNode->base);
} else {
vektor_modifier_apply(&shapeNode->modifiers[i], &shapeNode->modifiers[i - 1].cachedEvaluatedShape);
}
shapeNode->modifiers[i].dirty = false;
}
}
if (encountered_dirty) {
*shapeNode->evaluated = shapeNode->modifiers[shapeNode->modifier_count - 1].cachedEvaluatedShape;
}
}
void vektor_shapenodebuf_add(VektorShapeNodeBuffer* buffer,
VektorShapeNode node) {
if (buffer->count >= buffer->capacity) {
buffer->capacity = buffer->capacity ? buffer->capacity * 2 : 4;
buffer->nodes =
realloc(buffer->nodes, sizeof(VektorShapeNode) * buffer->capacity);
}
buffer->nodes[buffer->count++] = node;
if (buffer->count <= buffer->capacity / 4) {
buffer->capacity /= 2;
buffer->nodes =
realloc(buffer->nodes, sizeof(VektorShapeNode) * buffer->capacity);
}
}

53
src/core/modifier.h Normal file
View File

@@ -0,0 +1,53 @@
#ifndef VKTR_MODIFIER_H
#define VKTR_MODIFIER_H
#include "src/core/primitives.h"
typedef enum {
VEKTOR_MODIFIER_IDENTITY,
VEKTOR_MODIFIER_BEVEL
} VektorModifierType;
typedef struct VektorModifier {
VektorModifierType type;
bool enabled;
bool dirty;
void* parameters;
VektorShape (*apply)(struct VektorModifier* mod, VektorShape* input);
VektorShape cachedEvaluatedShape;
} VektorModifier;
typedef struct VektorShapeNode {
VektorShape* base;
VektorShape* evaluated;
VektorModifier* modifiers;
size_t modifier_count;
bool base_dirty;
} VektorShapeNode;
typedef struct VektorShapeNodeBuffer {
VektorShapeNode* nodes;
size_t count;
size_t capacity;
} VektorShapeNodeBuffer;
VektorShape vektor_modifier_apply(VektorModifier* mod, VektorShape* input);
VektorShapeNode vektor_shapenode_new(VektorShape* shape);
void vektor_shapenode_free(VektorShapeNode* shapeNode);
VektorShape* vektor_shapenode_get_evaluated(VektorShapeNode* shapeNode);
void vektor_shapenode_update(VektorShapeNode* shapeNode);
void vektor_shapenode_modifier_add(VektorShapeNode* shapeNode,
VektorModifier* mod);
void vektor_shapenode_modifier_remove(VektorShapeNode* shapeNode,
VektorModifier* mod);
void vektor_shapenode_free(VektorShapeNode* shapeNode);
void vektor_shapenodebuf_add(VektorShapeNodeBuffer* buffer,
VektorShapeNode node);
#endif

View File

@@ -0,0 +1,16 @@
#include "../modifier.h"
static VektorShape vektor_m_identity_apply(VektorModifier* m, VektorShape* input) {
return *input;
}
VektorModifier vektor_m_identity_new() {
return (VektorModifier) {
.type = VEKTOR_MODIFIER_IDENTITY,
.enabled = true,
.dirty = true,
.parameters = NULL,
.apply = vektor_m_identity_apply
};
}

View File

@@ -450,9 +450,10 @@ VektorBBox vektor_bbox_expand(VektorBBox bbox, float val) {
// ------ SHAPE METHODS ------ // ------ SHAPE METHODS ------
VektorShape vektor_shape_new(VektorPrimitive prim, VektorStyle style, VektorShape* vektor_shape_new(VektorPrimitive prim, VektorStyle style,
int z_index) { int z_index) {
VektorShape shape = (VektorShape){.primitive = prim, VektorShape* shape = malloc(sizeof(VektorShape));
*shape = (VektorShape){.primitive = prim,
.style = style, .style = style,
.transform = m33_identity(), .transform = m33_identity(),
.z_index = z_index, .z_index = z_index,
@@ -464,7 +465,7 @@ VektorShape vektor_shape_new(VektorPrimitive prim, VektorStyle style,
the passed value's pointer to an array of handles remains valid in the the passed value's pointer to an array of handles remains valid in the
passed copy. passed copy.
*/ */
vektor_shape_create_handles(&shape); vektor_shape_create_handles(shape);
return shape; return shape;
} }

View File

@@ -85,7 +85,7 @@ 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, VektorShape* vektor_shape_new(VektorPrimitive prim, VektorStyle style,
int z_index); int z_index);
VektorBBox vektor_polyline_get_bbox(VektorPrimitive prim); VektorBBox vektor_polyline_get_bbox(VektorPrimitive prim);

View File

@@ -3,6 +3,7 @@
#include "glib.h" #include "glib.h"
#include "primitives.h" #include "primitives.h"
#include "src/core/matrix.h" #include "src/core/matrix.h"
#include "src/core/modifier.h"
#include "src/core/vector.h" #include "src/core/vector.h"
#include "stddef.h" #include "stddef.h"
#include <math.h> #include <math.h>
@@ -40,7 +41,7 @@ void vektor_polygon_tessellate(EdgeBuffer* buffer, VektorPolygon* polygon,
void vektor_circle_tessellate(EdgeBuffer* buffer, VektorCircle* circle, void vektor_circle_tessellate(EdgeBuffer* buffer, VektorCircle* circle,
size_t j, double scale) { size_t j, double scale) {
double err = 0.0025; double err = 0.0025;
size_t res = MIN(PI * sqrt((scale * circle->radius) / (2 * err)), 20); size_t res = PI * sqrt((scale * circle->radius) / (2 * err));
for (size_t i = 0; i < res; i++) { for (size_t i = 0; i < res; i++) {
double theta1 = (2 * PI * i) / res; double theta1 = (2 * PI * i) / res;
double theta2 = (2 * PI * (i + 1)) / res; double theta2 = (2 * PI * (i + 1)) / res;
@@ -69,13 +70,17 @@ void vektor_rectangle_tessellate(EdgeBuffer* buffer, VektorRectangle* rct,
vektor_edgebuffer_add_edge(buffer, left); vektor_edgebuffer_add_edge(buffer, left);
} }
void vektor_rasterize(VertexBuffer* vb, VektorShapeBuffer* shapes, void vektor_vb_rasterize(VertexBuffer* vb, VektorShapeNodeBuffer* nodebuf,
double scale) { double scale) {
for (size_t i = 0; i < shapes->count; i++) { for (size_t i = 0; i < nodebuf->count; i++) {
EdgeBuffer edges = {0}; EdgeBuffer edges = {0};
VektorPrimitive* p = &shapes->shapes[i].primitive;
VektorStyle style = shapes->shapes[i].style; vektor_shapenode_update(&nodebuf->nodes[i]);
M33 transform = shapes->shapes[i].transform; VektorShape* currentShape = vektor_shapenode_get_evaluated(&nodebuf->nodes[i]);
VektorPrimitive* p = &currentShape->primitive;
VektorStyle style = currentShape->style;
M33 transform = currentShape->transform;
switch (p->kind) { switch (p->kind) {
case VEKTOR_POLYLINE: case VEKTOR_POLYLINE:
@@ -200,6 +205,24 @@ void vektor_edges_to_triangles(VertexBuffer* vb, EdgeBuffer* edges,
V2 v22 = vec2_add(e2.p2, off2); V2 v22 = vec2_add(e2.p2, off2);
V2 v23 = vec2_sub(e2.p2, off2); V2 v23 = vec2_sub(e2.p2, off2);
V2 outer1 = vec2_add(corner, off1);
V2 outer2 = vec2_add(corner, off2);
V2 inner1 = vec2_sub(corner, off1);
V2 inner2 = vec2_sub(corner, off2);
float cos_theta = vec2_dot(d1, d2);
float sin_half = sqrtf((1.0f - cos_theta) * 0.5f);
float miter_len = hw / sin_half;
if (miter_len > 4.0 * hw || miter_len < 1.05 * hw) {
vektor_vb_add_triangle(vb, outer1, corner, outer2,
style.stroke_color);
vektor_vb_add_triangle(vb, inner1, corner, inner2,
style.stroke_color);
continue;
}
V2 outer_miter = line_intersection(vec2_add(corner, off1), d1, V2 outer_miter = line_intersection(vec2_add(corner, off1), d1,
vec2_add(corner, off2), d2); vec2_add(corner, off2), d2);

View File

@@ -4,6 +4,7 @@
#include "primitives.h" #include "primitives.h"
#include "../util/color.h" #include "../util/color.h"
#include "src/core/modifier.h"
#include "stddef.h" #include "stddef.h"
#include "vector.h" #include "vector.h"
#include <stddef.h> #include <stddef.h>
@@ -47,9 +48,11 @@ void vektor_vb_add_triangle(VertexBuffer* vb, V2 v0, V2 v1, V2 v2,
VektorColor color); VektorColor color);
void vektor_vb_add_quad(VertexBuffer* vb, V2 v0, V2 v1, VektorColor color); 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, void vektor_edges_to_triangles(VertexBuffer* vb, EdgeBuffer* edges,
M33* transform, VektorStyle style, bool closed); M33* transform, VektorStyle style, bool closed);
void vektor_rasterize(VertexBuffer* vb, VektorShapeBuffer* shapes, void vektor_vb_rasterize(VertexBuffer* vb, VektorShapeNodeBuffer* shapes,
double scale); double scale);
#endif // RASTER_H_ #endif // RASTER_H_

View File

@@ -142,17 +142,17 @@ static void init_geometry(void) {
void vektor_canvas_geometry_changed(VektorCanvasRenderInfo* renderInfo) { void vektor_canvas_geometry_changed(VektorCanvasRenderInfo* renderInfo) {
vb.count = 0; vb.count = 0;
vektor_rasterize(&vb, renderInfo->shapes, renderInfo->zoom); vektor_vb_rasterize(&vb, renderInfo->shapes, renderInfo->zoom);
shape_vertex_count = vb.count; shape_vertex_count = vb.count;
if (renderInfo->selectedShape != NULL && if (renderInfo->selectedShape != NULL &&
*(renderInfo->selectedShape) != NULL) { *(renderInfo->selectedShape) != NULL) {
VektorShape* selectedShape = *renderInfo->selectedShape; VektorShapeNode* selectedShape = *renderInfo->selectedShape;
// create handle quads if a shape is selected // create handle quads if a shape is selected
for (size_t i = 0; i < selectedShape->handleCount; i++) { for (size_t i = 0; i < selectedShape->base->handleCount; i++) {
V2 handle = selectedShape->handles[i]; V2 handle = selectedShape->base->handles[i];
VektorBBox handleBbox = vektor_shape_get_handle_bbox(handle); VektorBBox handleBbox = vektor_shape_get_handle_bbox(handle);
vektor_vb_add_quad(&vb, handleBbox.min, handleBbox.max, vektor_vb_add_quad(&vb, handleBbox.min, handleBbox.max,
vektor_color_new(255, 255, 255, 255)); vektor_color_new(255, 255, 255, 255));
@@ -161,7 +161,8 @@ void vektor_canvas_geometry_changed(VektorCanvasRenderInfo* renderInfo) {
shape_vertex_count = vb.count; shape_vertex_count = vb.count;
// create selection quad if a shape is selected // create selection quad if a shape is selected
VektorBBox bbox = vektor_primitive_get_bbox(selectedShape->primitive); VektorBBox bbox =
vektor_primitive_get_bbox(selectedShape->base->primitive);
// expand it a little so it is not inset // expand it a little so it is not inset
bbox = vektor_bbox_expand(bbox, 0.03f); bbox = vektor_bbox_expand(bbox, 0.03f);
@@ -195,12 +196,13 @@ static gboolean render(GtkGLArea* a, GdkGLContext* ctx,
// PASS 2 - draw selection quads // PASS 2 - draw selection quads
if (vb.count > shape_vertex_count) { if (vb.count > shape_vertex_count) {
// g_print("vdelta: %zu\n", vb.count - shape_vertex_count);
float time = float time =
(g_get_monotonic_time() - renderInfo->startupTime) / 10000000.0f; (g_get_monotonic_time() - renderInfo->startupTime) / 10000000.0f;
// re-fetch bbox (we know a shape is selected) // re-fetch bbox (we know a shape is selected)
VektorBBox bbox = vektor_primitive_get_bbox( VektorBBox bbox = vektor_primitive_get_bbox(
(*(renderInfo->selectedShape))->primitive); (*(renderInfo->selectedShape))->base->primitive);
bbox = vektor_bbox_expand(bbox, 0.03f); bbox = vektor_bbox_expand(bbox, 0.03f);
glUseProgram(selection_shader_program); glUseProgram(selection_shader_program);
@@ -279,16 +281,15 @@ static void on_scroll(GtkEventControllerScroll* controller, double dx,
M33 mat = M33 mat =
m33_mul(m33_translate(s->panX, s->panY), m33_mul(m33_translate(s->panX, s->panY),
m33_mul(m33_rotate(s->rotation), m33_scale(s->zoom, s->zoom))); m33_mul(m33_rotate(s->rotation), m33_scale(s->zoom, s->zoom)));
// M33 mat = m33_mul(m33_mul(m33_scale(s->zoom, s->zoom),
// m33_translate(s->panX, s->panY)),
// m33_rotate(s->rotation));
m33_to_gl4(mat, s->canvasTransform); m33_to_gl4(mat, s->canvasTransform);
s->canvasMat = mat; s->canvasMat = mat;
GtkWidget* widget = GtkWidget* widget =
gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(controller)); gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(controller));
gtk_gl_area_queue_render(GTK_GL_AREA(widget));
// } vektor_canvas_geometry_changed(s);
} }
static void on_pan_begin(GtkGestureDrag* gesture, double start_x, static void on_pan_begin(GtkGestureDrag* gesture, double start_x,
@@ -360,7 +361,9 @@ static void on_pan_drag(GtkGestureDrag* gesture, double offset_x,
} }
GtkWidget* widget = GtkWidget* widget =
gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(gesture)); gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(gesture));
gtk_gl_area_queue_render(GTK_GL_AREA(widget));
//gtk_gl_area_queue_render(GTK_GL_AREA(widget));
vektor_canvas_geometry_changed(s);
} }
void vektor_canvas_init(VektorWidgetState* state, VektorCanvas* canvasOut, void vektor_canvas_init(VektorWidgetState* state, VektorCanvas* canvasOut,

View File

@@ -5,6 +5,7 @@
#include "../util/color.h" #include "../util/color.h"
#include "gtk/gtk.h" #include "gtk/gtk.h"
#include "src/core/matrix.h" #include "src/core/matrix.h"
#include "src/core/modifier.h"
#include "src/core/primitives.h" #include "src/core/primitives.h"
#include "uicontroller.h" #include "uicontroller.h"
@@ -22,10 +23,10 @@ typedef struct VektorCanvas {
typedef struct VektorCanvasRenderInfo { typedef struct VektorCanvasRenderInfo {
gint64 startupTime; gint64 startupTime;
VektorShapeBuffer* shapes; VektorShapeNodeBuffer* shapes;
// a pointer to appstate->selectedShape // a pointer to appstate->selectedShape
VektorShape** selectedShape; VektorShapeNode** selectedShape;
float zoom; float zoom;
float panX; float panX;
float panY; float panY;

View File

@@ -31,6 +31,29 @@
</object> </object>
</child> </child>
<!--Another topbar for style control-->
<child>
<object class="GtkBox">
<property name="orientation">horizontal</property>
<property name="hexpand">true</property>
<child>
<object class="GtkImage">
<property name="icon-name">tool-pencil-symbolic</property>
<property name="margin-start">10</property>
</object>
</child>
<child>
<object class="GtkEntry" id="thickness_entry">
<property name="text">1</property>
<property name="margin-start">10</property>
</object>
</child>
</object>
</child>
<!--Main area below--> <!--Main area below-->
<child> <child>
<object class="GtkPaned" id="workspace_paned"> <object class="GtkPaned" id="workspace_paned">