Compare commits

...

9 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
1fb4b1c1e1 fix: make the selection box thickness scale invariant 2026-03-14 15:07:42 +05:30
af6b0c4d30 feat: implement miter joins 2026-03-14 14:37:03 +05:30
Beriff
e054fc4fe7 fix(minor): apply canvas transform handle bbox detection 2026-03-14 02:19:46 +07:00
17 changed files with 498 additions and 177 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

@@ -5,6 +5,7 @@ in vec2 vPos;
out vec4 FragColor; out vec4 FragColor;
uniform float uTime; uniform float uTime;
uniform float uScale;
uniform vec4 uColor1; uniform vec4 uColor1;
uniform vec4 uColor2; uniform vec4 uColor2;
uniform vec2 uMin; uniform vec2 uMin;
@@ -12,7 +13,7 @@ uniform vec2 uMax;
void main() void main()
{ {
float borderWidth = 0.008; float borderWidth = 0.008 / uScale;
float distX = min(vPos.x - uMin.x, uMax.x - vPos.x); float distX = min(vPos.x - uMin.x, uMax.x - vPos.x);

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
@@ -95,9 +96,9 @@ static void canvas_onclick(GtkGestureClick* gesture, int n_press, double x,
vektor_appstate_canvas_click(state, normalized_coords.x, vektor_appstate_canvas_click(state, normalized_coords.x,
normalized_coords.y); normalized_coords.y);
// technically there are cases when a click would not result in change of the geometry // technically there are cases when a click would not result in change of
// but this is more concise then writing it inside that function // the geometry but this is more concise then writing it inside that
// a bunch of times and burder future click dispatches with // function a bunch of times and burder future click dispatches with
// handling this signal // handling this signal
vektor_canvas_geometry_changed(state->renderInfo); vektor_canvas_geometry_changed(state->renderInfo);
} }
@@ -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(
&state->selectedShape->base->primitive.rectangle,
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->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;
} }
} }
@@ -243,16 +259,18 @@ void vektor_appstate_canvas_drag_begin(GtkGestureDrag* gesture, gdouble x,
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 position = V2 position = (V2){(2 * (x / widget_w)) - 1, 1 - (2 * (y / widget_h))};
(V2){(2 * (x / widget_w)) - 1, 1 - (2 * (y / widget_h))}; position = m33_transform(m33_inverse(state->renderInfo->canvasMat),
(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 = vektor_shape_get_handle_bbox(selectedShape->handles[i]); VektorBBox bbox =
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;
@@ -260,7 +278,6 @@ void vektor_appstate_canvas_drag_begin(GtkGestureDrag* gesture, gdouble x,
break; break;
} }
} }
} }
} }
@@ -281,11 +298,14 @@ void vektor_appstate_canvas_drag_update(GtkGestureDrag* gesture, gdouble x,
V2 position = (V2){(2 * ((x + start_x) / widget_w)) - 1, V2 position = (V2){(2 * ((x + start_x) / widget_w)) - 1,
1 - (2 * ((y + start_y) / widget_h))}; 1 - (2 * ((y + start_y) / widget_h))};
position = m33_transform(m33_inverse(state->renderInfo->canvasMat),
(V2){position.x, position.y});
// 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, &state->heldHandleIndex); vektor_shape_handles_updated(state->selectedShape->base,
&state->heldHandleIndex);
vektor_canvas_geometry_changed(state->renderInfo); vektor_canvas_geometry_changed(state->renderInfo);
} }
} }
@@ -333,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

@@ -238,7 +238,6 @@ void vektor_shape_create_handles(VektorShape* shape) {
// ------ AUXILIARY HANDLE METHODS ------ // ------ AUXILIARY HANDLE METHODS ------
void vektor_shape_add_handle(VektorShape* shape, V2 handle) { void vektor_shape_add_handle(VektorShape* shape, V2 handle) {
// could be optimised with capacity property // could be optimised with capacity property
// but this function is only called when adding new // but this function is only called when adding new
@@ -291,15 +290,14 @@ void vektor_circle_handles_updated(VektorCircle* circle, V2** handles,
} else { } else {
circle->radius = vec2_length(vec2_sub((*handles)[0], (*handles)[1])); circle->radius = vec2_length(vec2_sub((*handles)[0], (*handles)[1]));
} }
} }
// this shi is big because it dynamically handles handle remapping when // this shi is big because it dynamically handles handle remapping when
// rectangle enters an invalid state (end < start) // rectangle enters an invalid state (end < start)
// creating the illusion of an invertable rect, while also keeping it // creating the illusion of an invertable rect, while also keeping it
// valid at all times // valid at all times
void vektor_rectangle_handles_updated(VektorRectangle* rectangle, V2** handles, size_t* count, int* heldHandleIndex) { void vektor_rectangle_handles_updated(VektorRectangle* rectangle, V2** handles,
size_t* count, int* heldHandleIndex) {
if (*count != 5) { if (*count != 5) {
g_warning("unexpected rectangle handle count (%zu)", *count); g_warning("unexpected rectangle handle count (%zu)", *count);
return; return;
@@ -308,8 +306,7 @@ void vektor_rectangle_handles_updated(VektorRectangle* rectangle, V2** handles,
V2 start = rectangle->start; V2 start = rectangle->start;
V2 end = rectangle->end; V2 end = rectangle->end;
switch (*heldHandleIndex) switch (*heldHandleIndex) {
{
case 0: // center drag case 0: // center drag
{ {
V2 oldCenter = vec2_scale(vec2_add(start, end), 0.5f); V2 oldCenter = vec2_scale(vec2_add(start, end), 0.5f);
@@ -362,31 +359,44 @@ void vektor_rectangle_handles_updated(VektorRectangle* rectangle, V2** handles,
bool flipY = raw_min_y > raw_max_y; bool flipY = raw_min_y > raw_max_y;
// Remap handle if we crossed axes // Remap handle if we crossed axes
if (*heldHandleIndex != 0) if (*heldHandleIndex != 0) {
{
if (flipX) { if (flipX) {
switch (*heldHandleIndex) { switch (*heldHandleIndex) {
case 1: *heldHandleIndex = 2; break; case 1:
case 2: *heldHandleIndex = 1; break; *heldHandleIndex = 2;
case 3: *heldHandleIndex = 4; break; break;
case 4: *heldHandleIndex = 3; break; case 2:
*heldHandleIndex = 1;
break;
case 3:
*heldHandleIndex = 4;
break;
case 4:
*heldHandleIndex = 3;
break;
} }
} }
if (flipY) { if (flipY) {
switch (*heldHandleIndex) { switch (*heldHandleIndex) {
case 1: *heldHandleIndex = 3; break; case 1:
case 3: *heldHandleIndex = 1; break; *heldHandleIndex = 3;
case 2: *heldHandleIndex = 4; break; break;
case 4: *heldHandleIndex = 2; break; case 3:
*heldHandleIndex = 1;
break;
case 2:
*heldHandleIndex = 4;
break;
case 4:
*heldHandleIndex = 2;
break;
} }
} }
} }
VektorRectangle properRect = { VektorRectangle properRect = {.start = {min_x, min_y},
.start = {min_x, min_y}, .end = {max_x, max_y}};
.end = {max_x, max_y}
};
vektor_rectangle_set_start(rectangle, properRect.start); vektor_rectangle_set_start(rectangle, properRect.start);
vektor_rectangle_set_end(rectangle, properRect.end); vektor_rectangle_set_end(rectangle, properRect.end);
@@ -399,11 +409,13 @@ void vektor_shape_handles_updated(VektorShape* shape, int* heldHandleIndex) {
switch (shape->primitive.kind) { switch (shape->primitive.kind) {
case VEKTOR_POLYLINE: case VEKTOR_POLYLINE:
vektor_polyline_handles_updated(shape->primitive.polyline, vektor_polyline_handles_updated(shape->primitive.polyline,
&shape->handles, &shape->handleCount, heldHandleIndex); &shape->handles, &shape->handleCount,
heldHandleIndex);
break; break;
case VEKTOR_POLYGON: case VEKTOR_POLYGON:
vektor_polygon_handles_updated(shape->primitive.polygon, vektor_polygon_handles_updated(shape->primitive.polygon,
&shape->handles, &shape->handleCount, heldHandleIndex); &shape->handles, &shape->handleCount,
heldHandleIndex);
break; break;
case VEKTOR_CIRCLE: case VEKTOR_CIRCLE:
vektor_circle_handles_updated(&shape->primitive.circle, &shape->handles, vektor_circle_handles_updated(&shape->primitive.circle, &shape->handles,
@@ -411,7 +423,8 @@ void vektor_shape_handles_updated(VektorShape* shape, int* heldHandleIndex) {
break; break;
case VEKTOR_RECTANGLE: case VEKTOR_RECTANGLE:
vektor_rectangle_handles_updated(&shape->primitive.rectangle, vektor_rectangle_handles_updated(&shape->primitive.rectangle,
&shape->handles, &shape->handleCount, heldHandleIndex); &shape->handles, &shape->handleCount,
heldHandleIndex);
break; break;
} }
} }
@@ -437,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,
@@ -451,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);
@@ -109,7 +109,6 @@ void vektor_rectangle_create_handles(VektorRectangle* rectangle, V2** handleArr,
size_t* count); 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);
VektorBBox vektor_shape_get_handle_bbox(V2 handle); VektorBBox vektor_shape_get_handle_bbox(V2 handle);

View File

@@ -1,7 +1,9 @@
#include "raster.h" #include "raster.h"
#include "epoxy/gl.h" #include "epoxy/gl.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>
@@ -38,7 +40,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.000025; double err = 0.0025;
size_t res = PI * sqrt((scale * circle->radius) / (2 * err)); 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;
@@ -68,27 +70,37 @@ 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 < nodebuf->count; i++) {
EdgeBuffer edges = {0}; EdgeBuffer edges = {0};
for (size_t i = 0; i < shapes->count; i++) {
VektorPrimitive* p = &shapes->shapes[i].primitive; vektor_shapenode_update(&nodebuf->nodes[i]);
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:
vektor_polyline_tessellate(&edges, p->polyline, i, scale); vektor_polyline_tessellate(&edges, p->polyline, i, scale);
vektor_edges_to_triangles(vb, &edges, &transform, style, FALSE);
break; break;
case VEKTOR_POLYGON: case VEKTOR_POLYGON:
vektor_polygon_tessellate(&edges, p->polygon, i, scale); vektor_polygon_tessellate(&edges, p->polygon, i, scale);
vektor_edges_to_triangles(vb, &edges, &transform, style, TRUE);
break; break;
case VEKTOR_CIRCLE: case VEKTOR_CIRCLE:
vektor_circle_tessellate(&edges, &p->circle, i, scale); vektor_circle_tessellate(&edges, &p->circle, i, scale);
vektor_edges_to_triangles(vb, &edges, &transform, style, TRUE);
break; break;
case VEKTOR_RECTANGLE: case VEKTOR_RECTANGLE:
vektor_rectangle_tessellate(&edges, &p->rectangle, i, scale); vektor_rectangle_tessellate(&edges, &p->rectangle, i, scale);
vektor_edges_to_triangles(vb, &edges, &transform, style, TRUE);
break; break;
default: default:
@@ -96,8 +108,6 @@ void vektor_rasterize(VertexBuffer* vb, VektorShapeBuffer* shapes,
break; break;
} }
} }
vektor_edges_to_triangles(vb, &edges, shapes);
} }
void vektor_vb_add_triangle(VertexBuffer* vb, V2 v0, V2 v1, V2 v2, void vektor_vb_add_triangle(VertexBuffer* vb, V2 v0, V2 v1, V2 v2,
@@ -132,37 +142,109 @@ void vektor_vb_add_quad(VertexBuffer* vb, V2 a, V2 b, VektorColor color) {
vektor_vb_add_triangle(vb, tl, br, tr, color); vektor_vb_add_triangle(vb, tl, br, tr, color);
} }
void vektor_edge_to_triangles(VertexBuffer* vb, Edge e, Edge edge_transform(const Edge* e, const M33* t) {
VektorShapeBuffer* shape_buffer) { Edge out = *e;
float dx = e.p2.x - e.p1.x; out.p1 = m33_transform(*t, e->p1);
float dy = e.p2.y - e.p1.y; out.p2 = m33_transform(*t, e->p2);
float len = sqrtf(dx * dx + dy * dy); return out;
if (len == 0) }
return;
float px = V2 line_intersection(V2 p, V2 r, V2 q, V2 s) {
-dy / len * (shape_buffer->shapes[e.shape_id].style.stroke_width / 2); float t = vec2_cross(vec2_sub(q, p), s) / vec2_cross(r, s);
float py = return vec2_add(p, vec2_scale(r, t));
dx / len * (shape_buffer->shapes[e.shape_id].style.stroke_width / 2);
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);
vektor_vb_add_triangle(vb, v2, v1, v3,
shape_buffer->shapes[e.shape_id].style.stroke_color);
} }
void vektor_edges_to_triangles(VertexBuffer* vb, EdgeBuffer* edges, void vektor_edges_to_triangles(VertexBuffer* vb, EdgeBuffer* edges,
VektorShapeBuffer* shape_buffer) { M33* transform, VektorStyle style, bool closed) {
if (!edges || edges->count < 1)
return;
float hw = style.stroke_width * 0.5f;
for (size_t i = 0; i < edges->count; i++) { for (size_t i = 0; i < edges->count; i++) {
vektor_edge_to_triangles(vb, edges->edges[i], shape_buffer); Edge e = edge_transform(&edges->edges[i], transform);
V2 d = vec2_normalize(vec2_sub(e.p2, e.p1));
V2 n = vec2_perp(d);
V2 off = vec2_scale(n, hw);
V2 v0 = vec2_add(e.p1, off);
V2 v1 = vec2_sub(e.p1, off);
V2 v2 = vec2_add(e.p2, off);
V2 v3 = vec2_sub(e.p2, off);
vektor_vb_add_triangle(vb, v0, v1, v2, style.stroke_color);
vektor_vb_add_triangle(vb, v2, v1, v3, style.stroke_color);
}
size_t limit = closed ? edges->count : edges->count - 1;
for (size_t i = 0; i < limit; i++) {
Edge e1 = edge_transform(&edges->edges[i], transform);
Edge e2 =
edge_transform(&edges->edges[(i + 1) % edges->count], transform);
V2 corner = e1.p2;
V2 d1 = vec2_normalize(vec2_sub(e1.p2, e1.p1));
V2 d2 = vec2_normalize(vec2_sub(e2.p2, e2.p1));
V2 n1 = vec2_perp(d1);
V2 n2 = vec2_perp(d2);
V2 off1 = vec2_scale(n1, hw);
V2 off2 = vec2_scale(n2, hw);
V2 v10 = vec2_add(e1.p1, off1);
V2 v11 = vec2_sub(e1.p1, off1);
V2 v12 = vec2_add(e1.p2, off1);
V2 v13 = vec2_sub(e1.p2, off1);
V2 v20 = vec2_add(e2.p1, off2);
V2 v21 = vec2_sub(e2.p1, off2);
V2 v22 = vec2_add(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,
vec2_add(corner, off2), d2);
V2 inner_miter = line_intersection(vec2_sub(corner, off1), d1,
vec2_sub(corner, off2), d2);
V2 mo = vec2_sub(outer_miter, corner);
V2 mi = vec2_sub(inner_miter, corner);
V2 vo1 = line_intersection(corner, vec2_normalize(vec2_perp(mo)),
vec2_add(corner, off1), d1);
V2 vo2 = line_intersection(corner,
vec2_negate(vec2_normalize(vec2_perp(mo))),
vec2_add(corner, off2), d2);
V2 vi1 = line_intersection(corner, vec2_normalize(vec2_perp(mi)),
vec2_sub(corner, off1), d1);
V2 vi2 = line_intersection(corner,
vec2_negate(vec2_normalize(vec2_perp(mi))),
vec2_sub(corner, off2), d2);
vektor_vb_add_triangle(vb, vo1, outer_miter, vo2, style.stroke_color);
vektor_vb_add_triangle(vb, vi1, inner_miter, vi2, style.stroke_color);
} }
} }

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>
@@ -48,10 +49,10 @@ void vektor_vb_add_triangle(VertexBuffer* vb, V2 v0, V2 v1, V2 v2,
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, void vektor_edge_to_triangles(VertexBuffer* vb, Edge e,
VektorShapeBuffer* shape_buffer); VektorShapeNodeBuffer* node_buffer);
void vektor_edges_to_triangles(VertexBuffer* vb, EdgeBuffer* edges, void vektor_edges_to_triangles(VertexBuffer* vb, EdgeBuffer* edges,
VektorShapeBuffer* shape_buffer); 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

@@ -20,6 +20,10 @@ static inline V3 vec2_vector(const V2 v) { return (V3){v.x, v.y, 0}; }
static inline V3 vec2_point(const V2 v) { return (V3){v.x, v.y, 1}; } static inline V3 vec2_point(const V2 v) { return (V3){v.x, v.y, 1}; }
static inline V2 vec2_perp(const V2 v) { return (V2){-v.y, v.x}; }
static inline V2 vec2_negate(const V2 v) { return (V2){-v.x, -v.y}; }
static inline V2 vec2_add(const V2 v1, const V2 v2) { static inline V2 vec2_add(const V2 v1, const V2 v2) {
return (V2){v1.x + v2.x, v1.y + v2.y}; return (V2){v1.x + v2.x, v1.y + v2.y};
} }

View File

@@ -38,6 +38,7 @@ static GLuint shader_standard_uProjMatrixLoc;
static GLuint shader_selection_uProjMatrixLoc; static GLuint shader_selection_uProjMatrixLoc;
static GLuint shader_selection_uTimeLoc; static GLuint shader_selection_uTimeLoc;
static GLuint shader_selection_uScaleLoc;
static GLuint shader_selection_uC1Loc; static GLuint shader_selection_uC1Loc;
static GLuint shader_selection_uC2Loc; static GLuint shader_selection_uC2Loc;
static GLuint shader_selection_uMinLoc; static GLuint shader_selection_uMinLoc;
@@ -103,6 +104,8 @@ static void init_shader(void) {
glGetUniformLocation(selection_shader_program, "uProjection"); glGetUniformLocation(selection_shader_program, "uProjection");
shader_selection_uTimeLoc = shader_selection_uTimeLoc =
glGetUniformLocation(selection_shader_program, "uTime"); glGetUniformLocation(selection_shader_program, "uTime");
shader_selection_uScaleLoc =
glGetUniformLocation(selection_shader_program, "uScale");
shader_selection_uC1Loc = shader_selection_uC1Loc =
glGetUniformLocation(selection_shader_program, "uColor1"); glGetUniformLocation(selection_shader_program, "uColor1");
shader_selection_uC2Loc = shader_selection_uC2Loc =
@@ -139,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));
@@ -158,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);
@@ -171,8 +175,6 @@ static gboolean render(GtkGLArea* a, GdkGLContext* ctx,
VektorCanvasRenderInfo* renderInfo) { VektorCanvasRenderInfo* renderInfo) {
// vektor_canvas_geometry_changed(renderInfo); // vektor_canvas_geometry_changed(renderInfo);
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);
@@ -186,7 +188,7 @@ static gboolean render(GtkGLArea* a, GdkGLContext* ctx,
glBindVertexArray(vao); glBindVertexArray(vao);
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
@@ -194,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);
@@ -207,6 +210,7 @@ static gboolean render(GtkGLArea* a, GdkGLContext* ctx,
glUniformMatrix4fv(shader_selection_uProjMatrixLoc, 1, GL_FALSE, glUniformMatrix4fv(shader_selection_uProjMatrixLoc, 1, GL_FALSE,
renderInfo->canvasTransform); renderInfo->canvasTransform);
glUniform1f(shader_selection_uTimeLoc, time); glUniform1f(shader_selection_uTimeLoc, time);
glUniform1f(shader_selection_uScaleLoc, renderInfo->zoom);
glUniform2f(shader_selection_uMinLoc, bbox.min.x, bbox.min.y); glUniform2f(shader_selection_uMinLoc, bbox.min.x, bbox.min.y);
glUniform2f(shader_selection_uMaxLoc, bbox.max.x, bbox.max.y); glUniform2f(shader_selection_uMaxLoc, bbox.max.x, bbox.max.y);
glUniform4f(shader_selection_uC1Loc, 0, 0, 0, 0); glUniform4f(shader_selection_uC1Loc, 0, 0, 0, 0);
@@ -277,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,
@@ -358,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">