Compare commits

...

18 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
Beriff
e7dc799f54 chore: decouple rendering & geometry generation logic 2026-03-14 01:50:44 +07:00
Beriff
5e883e2d27 chore(minor): clean up circle_handles_updated logic 2026-03-12 21:46:21 +07:00
Beriff
09b84a2aa8 fix: adjust rect & circle handle logic 2026-03-12 21:43:03 +07:00
Beriff
7bc94d3a96 feat(untested): add handle dragging 2026-03-12 11:20:43 +07:00
Beriff
f96d6066ee chore: adjust build flags 2026-03-12 10:45:05 +07:00
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
Beriff
ed9aca01e4 feat(untested): add handles base 2026-03-11 14:19:05 +07:00
Beriff
6c8ca19fbf feat: add circle tool 2026-03-11 09:46:30 +07:00
19 changed files with 976 additions and 179 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

@@ -10,14 +10,22 @@ project(
], ],
) )
gtk = dependency('gtk4', required: true) c_args = meson.get_compiler('c').get_supported_arguments([
epoxy = dependency('epoxy') '-Wno-unused-variable',
'-Wno-unused-parameter',
'-Wno-pedantic'
])
add_project_arguments(c_args, language: 'c')
gtk = dependency('gtk4', required: true, include_type: 'system')
epoxy = dependency('epoxy', include_type: 'system')
src = files( src = files(
'src/main.c', 'src/main.c',
'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',
@@ -27,7 +35,7 @@ src = files(
executable( executable(
'vektor', 'vektor',
src, src,
dependencies: [gtk,epoxy], dependencies: [gtk, epoxy],
link_args: ['-lm'], link_args: ['-lm'],
install: true, install: true,
) )

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
@@ -61,8 +62,7 @@ static void appstate_on_color_change(VektorColorWheel* wheel,
gtk_editable_set_text(GTK_EDITABLE(appstate->widgetState->sidepanelEntryB), gtk_editable_set_text(GTK_EDITABLE(appstate->widgetState->sidepanelEntryB),
str_b); str_b);
/*gtk_gl_area_queue_render( vektor_canvas_geometry_changed(appstate->renderInfo);
GTK_GL_AREA(appstate->widgetState->workspaceCanvas));*/
} }
static void appstate_on_entry_update(GtkEntry* entry, gpointer user_data) { static void appstate_on_entry_update(GtkEntry* entry, gpointer user_data) {
@@ -90,16 +90,17 @@ static void canvas_onclick(GtkGestureClick* gesture, int n_press, double 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);
int canvas_w = state->canvas->width;
int canvas_h = state->canvas->height;
V2 normalized_coords = V2 normalized_coords =
(V2){(2 * (x / widget_w)) - 1, 1 - (2 * (y / widget_h))}; (V2){(2 * (x / widget_w)) - 1, 1 - (2 * (y / widget_h))};
vektor_appstate_canvas_click(state, normalized_coords.x, vektor_appstate_canvas_click(state, normalized_coords.x,
normalized_coords.y); normalized_coords.y);
// gtk_gl_area_queue_render(GTK_GL_AREA(widget)); // technically there are cases when a click would not result in change of
// the geometry but this is more concise then writing it inside that
// function a bunch of times and burder future click dispatches with
// handling this signal
vektor_canvas_geometry_changed(state->renderInfo);
} }
void vektor_appstate_canvas_click(VektorAppState* state, double x, double y) { void vektor_appstate_canvas_click(VektorAppState* state, double x, double y) {
@@ -117,23 +118,30 @@ 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");
state->selectedShape = NULL; vektor_appstate_deselect_shape(state);
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
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
if (state->selectedShape == NULL) { if (state->selectedShape == NULL) {
@@ -143,21 +151,56 @@ 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");
state->selectedShape = NULL; 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
vektor_shape_add_handle(state->selectedShape->base, pos);
} else if (state->selectedTool == VektorCircleTool) {
VektorCircle* circle = vektor_circle_new();
VektorPrimitive circlePrimitive =
(VektorPrimitive){.kind = VEKTOR_CIRCLE, .circle = *circle};
VektorStyle style = (VektorStyle){.stroke_color = state->currentColor,
.stroke_width = 0.01};
vektor_shapenodebuf_add(
state->shapeBuffer,
vektor_shapenode_new(vektor_shape_new(circlePrimitive, style, 0)));
state->selectedShape =
&(state->shapeBuffer->nodes[state->shapeBuffer->count - 1]);
vektor_circle_free(circle);
vektor_circle_set_center(&state->selectedShape->base->primitive.circle,
pos);
vektor_circle_set_radius(&state->selectedShape->base->primitive.circle,
0.1f);
state->selectedShape->base->bbox =
vektor_primitive_get_bbox(state->selectedShape->base->primitive);
vektor_circle_create_handles(
&state->selectedShape->base->primitive.circle,
&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();
@@ -165,32 +208,117 @@ 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,
// state->selectedShape = NULL; vec2_add(pos, (V2){0.1f, 0.1f}));
vektor_shapes_update_bbox(state->shapeBuffer); vektor_rectangle_create_handles(
&state->selectedShape->base->primitive.rectangle,
&state->selectedShape->base->handles,
&state->selectedShape->base->handleCount);
state->selectedShape->base->bbox =
vektor_primitive_get_bbox(state->selectedShape->base->primitive);
} 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
// try to grab handles located on the border of said bbox
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]);
g_print("%d", state->selectedShape == NULL);
return; return;
} }
} }
// was clicked outside any shapes - reset selection // was clicked outside any shapes - reset selection
state->selectedShape = NULL; vektor_appstate_deselect_shape(state);
}
}
void vektor_appstate_canvas_drag_begin(GtkGestureDrag* gesture, gdouble x,
gdouble y, gpointer user_data) {
GtkWidget* widget =
gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(gesture));
VektorAppState* state = (VektorAppState*)user_data;
int widget_w = gtk_widget_get_width(widget);
int widget_h = gtk_widget_get_height(widget);
V2 position = (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) {
VektorShapeNode* selectedShape = state->selectedShape;
// get selected shape's handles and check
// if we click any of them
for (size_t i = 0; i < selectedShape->base->handleCount; i++) {
VektorBBox bbox =
vektor_shape_get_handle_bbox(selectedShape->base->handles[i]);
if (vektor_bbox_isinside(bbox, position)) {
// clicked inside handle
state->heldHandleIndex = i;
vektor_canvas_geometry_changed(state->renderInfo);
break;
}
}
}
}
void vektor_appstate_canvas_drag_update(GtkGestureDrag* gesture, gdouble x,
gdouble y, gpointer user_data) {
// ---- setup normalized coordinates (boilerplate) ----
gdouble start_x, start_y;
gtk_gesture_drag_get_start_point(gesture, &start_x, &start_y);
GtkWidget* widget =
gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(gesture));
VektorAppState* state = (VektorAppState*)user_data;
int widget_w = gtk_widget_get_width(widget);
int widget_h = gtk_widget_get_height(widget);
V2 position = (V2){(2 * ((x + start_x) / widget_w)) - 1,
1 - (2 * ((y + start_y) / widget_h))};
position = m33_transform(m33_inverse(state->renderInfo->canvasMat),
(V2){position.x, position.y});
// drag handle if selected
if (state->selectedShape != NULL && state->heldHandleIndex != -1) {
state->selectedShape->base->handles[state->heldHandleIndex] = position;
vektor_shape_handles_updated(state->selectedShape->base,
&state->heldHandleIndex);
vektor_canvas_geometry_changed(state->renderInfo);
}
}
void vektor_appstate_canvas_drag_end(GtkGestureDrag* gesture, gdouble x,
gdouble y, gpointer user_data) {
VektorAppState* state = (VektorAppState*)user_data;
// if we were dragging a handle
if (state->selectedShape != NULL && state->heldHandleIndex != -1) {
state->heldHandleIndex = -1; // ...then remove handle drag flag
vektor_canvas_geometry_changed(state->renderInfo);
} }
} }
@@ -217,22 +345,22 @@ void vektor_appstate_new(VektorWidgetState* wstate, VektorAppState* stateOut) {
data_selecttool->state = stateOut; data_selecttool->state = stateOut;
data_selecttool->tool = VektorSelectionTool; data_selecttool->tool = VektorSelectionTool;
button_tool_set_data* data_circletool =
malloc(sizeof(button_tool_set_data));
data_circletool->state = stateOut;
data_circletool->tool = VektorCircleTool;
data_circletool->revealer = wstate->workspaceRevealerShapes;
// populate appstate // populate appstate
stateOut->startupTime = g_get_monotonic_time(); stateOut->startupTime = g_get_monotonic_time();
stateOut->shapeBuffer = malloc(sizeof(VektorShapeNodeBuffer));
stateOut->shapeBuffer = malloc(sizeof(VektorShapeBuffer)); *stateOut->shapeBuffer = (VektorShapeNodeBuffer){0};
*stateOut->shapeBuffer = (VektorShapeBuffer){0};
VektorCircle circ = (VektorCircle){.center = (V2){0, 0}, .radius = 0.3};
VektorShape shp = vektor_shape_new(
(VektorPrimitive){.kind = VEKTOR_CIRCLE, .circle = circ},
(VektorStyle){.stroke_color = stateOut->currentColor, 0.01}, 0);
vektor_shapebuffer_add_shape(stateOut->shapeBuffer, shp);
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);
stateOut->selectedShape = NULL; stateOut->selectedShape = NULL;
stateOut->heldHandleIndex = -1;
VektorCanvasRenderInfo* renderInfo = malloc(sizeof(VektorCanvasRenderInfo)); VektorCanvasRenderInfo* renderInfo = malloc(sizeof(VektorCanvasRenderInfo));
renderInfo->zoom = 1; renderInfo->zoom = 1;
renderInfo->panX = 0; renderInfo->panX = 0;
@@ -245,13 +373,14 @@ void vektor_appstate_new(VektorWidgetState* wstate, VektorAppState* stateOut) {
renderInfo->canvasMat = m33_identity(); renderInfo->canvasMat = m33_identity();
vektor_canvas_init(wstate, stateOut->canvas, renderInfo); vektor_canvas_init(wstate, stateOut->canvas, renderInfo);
stateOut->renderInfo = renderInfo; stateOut->renderInfo = renderInfo;
// link all the buttons // link all the buttons
g_signal_connect(G_OBJECT(wstate->workspaceButtonLineTool), "clicked", g_signal_connect(G_OBJECT(wstate->workspaceButtonLineTool), "clicked",
G_CALLBACK(appstate_set_tool), data_linetool); G_CALLBACK(appstate_set_tool), data_linetool);
g_signal_connect(G_OBJECT(wstate->workspaceButtonRectTool), "clicked", g_signal_connect(G_OBJECT(wstate->workspaceButtonRectTool), "clicked",
G_CALLBACK(appstate_set_tool), data_rectangletool); G_CALLBACK(appstate_set_tool), data_rectangletool);
g_signal_connect(G_OBJECT(wstate->workspaceButtonCircleTool), "clicked", g_signal_connect(G_OBJECT(wstate->workspaceButtonCircleTool), "clicked",
G_CALLBACK(appstate_set_tool), data_linetool); G_CALLBACK(appstate_set_tool), data_circletool);
g_signal_connect(G_OBJECT(wstate->workspaceButtonPolygonTool), "clicked", g_signal_connect(G_OBJECT(wstate->workspaceButtonPolygonTool), "clicked",
G_CALLBACK(appstate_set_tool), data_polygontool); G_CALLBACK(appstate_set_tool), data_polygontool);
g_signal_connect(G_OBJECT(wstate->workspaceButtonSelectionTool), "clicked", g_signal_connect(G_OBJECT(wstate->workspaceButtonSelectionTool), "clicked",
@@ -283,4 +412,21 @@ void vektor_appstate_new(VektorWidgetState* wstate, VektorAppState* stateOut) {
G_CALLBACK(canvas_onclick), stateOut); G_CALLBACK(canvas_onclick), stateOut);
gtk_widget_add_controller(GTK_WIDGET(wstate->workspaceCanvas), gtk_widget_add_controller(GTK_WIDGET(wstate->workspaceCanvas),
GTK_EVENT_CONTROLLER(canvasClickGesture)); GTK_EVENT_CONTROLLER(canvasClickGesture));
// Add drag gesture to canvas
GtkGesture* canvasDragGesture = gtk_gesture_drag_new();
g_signal_connect(G_OBJECT(canvasDragGesture), "drag-update",
G_CALLBACK(vektor_appstate_canvas_drag_update), stateOut);
g_signal_connect(G_OBJECT(canvasDragGesture), "drag-begin",
G_CALLBACK(vektor_appstate_canvas_drag_begin), stateOut);
g_signal_connect(G_OBJECT(canvasDragGesture), "drag-end",
G_CALLBACK(vektor_appstate_canvas_drag_end), stateOut);
gtk_widget_add_controller(GTK_WIDGET(wstate->workspaceCanvas),
GTK_EVENT_CONTROLLER(canvasDragGesture));
}
void vektor_appstate_deselect_shape(VektorAppState* state) {
state->heldHandleIndex = -1;
state->selectedShape = NULL;
} }

View File

@@ -4,13 +4,14 @@
#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,
VektorLineTool, VektorLineTool,
VektorPolygonTool, VektorPolygonTool,
VektorRectangleTool VektorRectangleTool,
VektorCircleTool
} VektorAppTool; } VektorAppTool;
typedef struct VektorAppState { typedef struct VektorAppState {
@@ -19,12 +20,13 @@ typedef struct VektorAppState {
VektorWidgetState* widgetState; VektorWidgetState* widgetState;
VektorAppTool selectedTool; VektorAppTool selectedTool;
VektorShape* selectedShape; VektorShapeNode* selectedShape;
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;
@@ -32,5 +34,6 @@ typedef struct VektorAppState {
void vektor_appstate_new(VektorWidgetState* wstate, VektorAppState* stateOut); void vektor_appstate_new(VektorWidgetState* wstate, VektorAppState* stateOut);
void vektor_appstate_canvas_click(VektorAppState* state, double x, double y); void vektor_appstate_canvas_click(VektorAppState* state, double x, double y);
void vektor_appstate_deselect_shape(VektorAppState* state);
#endif #endif

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

@@ -1,8 +1,13 @@
#include "primitives.h" #include "primitives.h"
#include "glib.h"
#include "src/core/matrix.h"
#include "src/core/vector.h"
#include <assert.h> #include <assert.h>
#include <math.h> #include <math.h>
#include <stddef.h> #include <stddef.h>
// ------ PER-PRIMITIVE METHODS ------
VektorPolyline* vektor_polyline_new(void) { VektorPolyline* vektor_polyline_new(void) {
VektorPolyline* pl = malloc(sizeof(VektorPolyline)); VektorPolyline* pl = malloc(sizeof(VektorPolyline));
pl->count = 0; pl->count = 0;
@@ -74,6 +79,8 @@ void vektor_circle_set_radius(VektorCircle* circle, double radius) {
circle->radius = radius; circle->radius = radius;
} }
void vektor_circle_free(VektorCircle* circle) { free(circle); }
VektorRectangle* vektor_rectangle_new(void) { VektorRectangle* vektor_rectangle_new(void) {
VektorRectangle* rct = malloc(sizeof(VektorRectangle)); VektorRectangle* rct = malloc(sizeof(VektorRectangle));
rct->start = (V2){.x = 0, .y = 0}; rct->start = (V2){.x = 0, .y = 0};
@@ -90,32 +97,24 @@ void vektor_rectangle_set_start(VektorRectangle* rct, V2 point) {
void vektor_rectangle_free(VektorRectangle* rct) { free(rct); } void vektor_rectangle_free(VektorRectangle* rct) { free(rct); }
void vektor_shapebuffer_add_shape(VektorShapeBuffer* buffer,
VektorShape shape) {
if (buffer->count >= buffer->capacity) {
buffer->capacity = buffer->capacity ? buffer->capacity * 2 : 4;
buffer->shapes =
realloc(buffer->shapes, sizeof(VektorShape) * buffer->capacity);
}
buffer->shapes[buffer->count++] = shape;
if (buffer->count <= buffer->capacity / 4) {
buffer->capacity /= 2;
buffer->shapes =
realloc(buffer->shapes, sizeof(VektorShape) * buffer->capacity);
}
}
VektorBBox vektor_polyline_get_bbox(VektorPrimitive prim) { VektorBBox vektor_polyline_get_bbox(VektorPrimitive prim) {
float min_x, max_x, min_y, max_y; V2 first = prim.polyline->points[0];
for (size_t i = 0; i < prim.polyline->count; i++) {
V2 p = prim.polyline->points[i]; float min_x = first.x;
float max_x = first.x;
float min_y = first.y;
float max_y = first.y;
for (size_t i = 1; i < prim.polygon->count; i++) {
V2 p = prim.polygon->points[i];
min_x = fminf(min_x, p.x); min_x = fminf(min_x, p.x);
min_y = fminf(min_y, p.y); min_y = fminf(min_y, p.y);
max_x = fminf(max_x, p.x); max_x = fmaxf(max_x, p.x);
max_y = fminf(max_y, p.y); max_y = fmaxf(max_y, p.y);
} }
return (VektorBBox){(V2){min_x, min_y}, (V2){max_x, max_y}}; return (VektorBBox){(V2){min_x, min_y}, (V2){max_x, max_y}};
} }
@@ -144,6 +143,12 @@ VektorBBox vektor_rectangle_get_bbox(VektorPrimitive prim) {
return (VektorBBox){prim.rectangle.start, prim.rectangle.end}; return (VektorBBox){prim.rectangle.start, prim.rectangle.end};
} }
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))};
}
VektorBBox vektor_primitive_get_bbox(VektorPrimitive prim) { VektorBBox vektor_primitive_get_bbox(VektorPrimitive prim) {
switch (prim.kind) { switch (prim.kind) {
case VEKTOR_POLYLINE: case VEKTOR_POLYLINE:
@@ -158,23 +163,310 @@ VektorBBox vektor_primitive_get_bbox(VektorPrimitive prim) {
return vektor_rectangle_get_bbox(prim); return vektor_rectangle_get_bbox(prim);
break; break;
case VEKTOR_CIRCLE:
return vektor_circle_get_bbox(prim);
break;
default: default:
// TODO: fill in all primitives // TODO: fill in all primitives
break; break;
} }
} }
// ------ PRIMITIVE HANDLES GENERATION ------
/* [n]: polyline vertices */
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) {
*count = 0;
*handleArr = NULL;
}
/* [0]: center; [1]: radius */
void vektor_circle_create_handles(VektorCircle* circle, V2** handleArr,
size_t* count) {
*count = 2;
*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) {
*count = 5;
free(*handleArr);
*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);
(*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}));
}
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;
}
}
// ------ AUXILIARY HANDLE METHODS ------
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;
}
VektorBBox vektor_shape_get_handle_bbox(V2 handle) {
return vektor_bbox_fromcenter(handle, 0.02);
}
// ------ PRIMITIVE HANDLES UPDATING ------
void vektor_polyline_handles_updated(VektorPolyline* polyline, V2** handles,
size_t* count, int* heldHandleIndex) {
if (*count != polyline->count) {
g_warning("handle count & point count mismatch in polyline");
return;
}
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, int* heldHandleIndex) {
if (*count != polygon->count) {
g_warning("handle count & point count mismatch in polygon");
return;
}
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, int* heldHandleIndex) {
if (*count != 2) {
g_warning("unexpected circle handle count (%zu)", *count);
return;
}
if (*heldHandleIndex == 0) { // dragging center
V2 translation = vec2_sub((*handles)[0], circle->center);
circle->center = (*handles)[0];
(*handles)[1] = vec2_add(translation, (*handles)[1]);
} else {
circle->radius = vec2_length(vec2_sub((*handles)[0], (*handles)[1]));
}
}
// this shi is big because it dynamically handles handle remapping when
// rectangle enters an invalid state (end < start)
// creating the illusion of an invertable rect, while also keeping it
// valid at all times
void vektor_rectangle_handles_updated(VektorRectangle* rectangle, V2** handles,
size_t* count, int* heldHandleIndex) {
if (*count != 5) {
g_warning("unexpected rectangle handle count (%zu)", *count);
return;
}
V2 start = rectangle->start;
V2 end = rectangle->end;
switch (*heldHandleIndex) {
case 0: // center drag
{
V2 oldCenter = vec2_scale(vec2_add(start, end), 0.5f);
V2 newCenter = (*handles)[0];
V2 translation = vec2_sub(newCenter, oldCenter);
start = vec2_add(start, translation);
end = vec2_add(end, translation);
break;
}
case 1: // top-left
start.x = (*handles)[1].x;
end.y = (*handles)[1].y;
break;
case 2: // top-right
end.x = (*handles)[2].x;
end.y = (*handles)[2].y;
break;
case 3: // bottom-left
start.x = (*handles)[3].x;
start.y = (*handles)[3].y;
break;
case 4: // bottom-right
end.x = (*handles)[4].x;
start.y = (*handles)[4].y;
break;
default:
return;
}
// Store raw values before normalization
float raw_min_x = start.x;
float raw_max_x = end.x;
float raw_min_y = start.y;
float raw_max_y = end.y;
// Normalize rectangle
float min_x = fminf(start.x, end.x);
float max_x = fmaxf(start.x, end.x);
float min_y = fminf(start.y, end.y);
float max_y = fmaxf(start.y, end.y);
bool flipX = raw_min_x > raw_max_x;
bool flipY = raw_min_y > raw_max_y;
// Remap handle if we crossed axes
if (*heldHandleIndex != 0) {
if (flipX) {
switch (*heldHandleIndex) {
case 1:
*heldHandleIndex = 2;
break;
case 2:
*heldHandleIndex = 1;
break;
case 3:
*heldHandleIndex = 4;
break;
case 4:
*heldHandleIndex = 3;
break;
}
}
if (flipY) {
switch (*heldHandleIndex) {
case 1:
*heldHandleIndex = 3;
break;
case 3:
*heldHandleIndex = 1;
break;
case 2:
*heldHandleIndex = 4;
break;
case 4:
*heldHandleIndex = 2;
break;
}
}
}
VektorRectangle properRect = {.start = {min_x, min_y},
.end = {max_x, max_y}};
vektor_rectangle_set_start(rectangle, properRect.start);
vektor_rectangle_set_end(rectangle, properRect.end);
// regenerate handle positions
vektor_rectangle_create_handles(&properRect, handles, count);
}
void vektor_shape_handles_updated(VektorShape* shape, int* heldHandleIndex) {
switch (shape->primitive.kind) {
case VEKTOR_POLYLINE:
vektor_polyline_handles_updated(shape->primitive.polyline,
&shape->handles, &shape->handleCount,
heldHandleIndex);
break;
case VEKTOR_POLYGON:
vektor_polygon_handles_updated(shape->primitive.polygon,
&shape->handles, &shape->handleCount,
heldHandleIndex);
break;
case VEKTOR_CIRCLE:
vektor_circle_handles_updated(&shape->primitive.circle, &shape->handles,
&shape->handleCount, heldHandleIndex);
break;
case VEKTOR_RECTANGLE:
vektor_rectangle_handles_updated(&shape->primitive.rectangle,
&shape->handles, &shape->handleCount,
heldHandleIndex);
break;
}
}
// ------ BBOX METHODS ------
bool vektor_bbox_isinside(VektorBBox bbox, V2 point) { bool vektor_bbox_isinside(VektorBBox bbox, V2 point) {
return point.x >= bbox.min.x && point.y >= bbox.min.y && return point.x >= bbox.min.x && point.y >= bbox.min.y &&
point.x <= bbox.max.x && point.y <= bbox.max.y; point.x <= bbox.max.x && point.y <= bbox.max.y;
} }
VektorShape vektor_shape_new(VektorPrimitive prim, VektorStyle style, 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};
}
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) { int z_index) {
return (VektorShape){.primitive = prim, VektorShape* shape = malloc(sizeof(VektorShape));
.style = style, *shape = (VektorShape){.primitive = prim,
.z_index = z_index, .style = style,
.bbox = vektor_primitive_get_bbox(prim)}; .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) { void vektor_shapes_update_bbox(VektorShapeBuffer* buffer) {
@@ -183,3 +475,19 @@ void vektor_shapes_update_bbox(VektorShapeBuffer* buffer) {
vektor_primitive_get_bbox(buffer->shapes[i].primitive); vektor_primitive_get_bbox(buffer->shapes[i].primitive);
} }
} }
void vektor_shapebuffer_add_shape(VektorShapeBuffer* buffer,
VektorShape shape) {
if (buffer->count >= buffer->capacity) {
buffer->capacity = buffer->capacity ? buffer->capacity * 2 : 4;
buffer->shapes =
realloc(buffer->shapes, sizeof(VektorShape) * buffer->capacity);
}
buffer->shapes[buffer->count++] = shape;
if (buffer->count <= buffer->capacity / 4) {
buffer->capacity /= 2;
buffer->shapes =
realloc(buffer->shapes, sizeof(VektorShape) * buffer->capacity);
}
}

View File

@@ -1,6 +1,7 @@
#ifndef PRIMITIVES_H_ #ifndef PRIMITIVES_H_
#define PRIMITIVES_H_ #define PRIMITIVES_H_
#include "src/core/matrix.h"
#include "src/util/color.h" #include "src/util/color.h"
#include "stddef.h" #include "stddef.h"
#include "stdlib.h" #include "stdlib.h"
@@ -45,23 +46,6 @@ typedef struct {
}; };
} VektorPrimitive; } VektorPrimitive;
VektorPolyline* vektor_polyline_new(void);
void vektor_polyline_add_point(VektorPolyline* pl, V2 point);
void vektor_polyline_free(VektorPolyline* pl);
VektorPolygon* vektor_polygon_new(void);
void vektor_polygon_add_point(VektorPolygon* pl, V2 point);
void vektor_polygon_free(VektorPolygon* pl);
VektorCircle* vektor_circle_new(void);
void vektor_circle_set_center(VektorCircle* circle, V2 point);
void vektor_circle_set_radius(VektorCircle* circle, double radius);
VektorRectangle* vektor_rectangle_new(void);
void vektor_rectangle_set_end(VektorRectangle* rct, V2 point);
void vektor_rectangle_set_start(VektorRectangle* rct, V2 point);
void vektor_rectangle_free(VektorRectangle* rct);
typedef struct { typedef struct {
VektorColor stroke_color; VektorColor stroke_color;
float stroke_width; float stroke_width;
@@ -75,10 +59,35 @@ typedef struct {
typedef struct { typedef struct {
VektorStyle style; VektorStyle style;
int z_index; int z_index;
M33 transform;
VektorBBox bbox; VektorBBox bbox;
VektorPrimitive primitive; VektorPrimitive primitive;
V2* handles;
size_t handleCount;
} VektorShape; } VektorShape;
VektorPolyline* vektor_polyline_new(void);
void vektor_polyline_add_point(VektorPolyline* pl, V2 point);
void vektor_polyline_free(VektorPolyline* pl);
VektorPolygon* vektor_polygon_new(void);
void vektor_polygon_add_point(VektorPolygon* pl, V2 point);
void vektor_polygon_free(VektorPolygon* pl);
VektorCircle* vektor_circle_new(void);
void vektor_circle_set_center(VektorCircle* circle, V2 point);
void vektor_circle_set_radius(VektorCircle* circle, double radius);
void vektor_circle_free(VektorCircle* circle);
VektorRectangle* vektor_rectangle_new(void);
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);
VektorBBox vektor_polyline_get_bbox(VektorPrimitive prim); VektorBBox vektor_polyline_get_bbox(VektorPrimitive prim);
VektorBBox vektor_polygon_get_bbox(VektorPrimitive prim); VektorBBox vektor_polygon_get_bbox(VektorPrimitive prim);
VektorBBox vektor_circle_get_bbox(VektorPrimitive prim); VektorBBox vektor_circle_get_bbox(VektorPrimitive prim);
@@ -86,9 +95,33 @@ VektorBBox vektor_rectangle_get_bbox(VektorPrimitive prim);
VektorBBox vektor_primitive_get_bbox(VektorPrimitive prim); VektorBBox vektor_primitive_get_bbox(VektorPrimitive prim);
bool vektor_bbox_isinside(VektorBBox bbox, V2 point); bool vektor_bbox_isinside(VektorBBox bbox, V2 point);
VektorBBox vektor_bbox_fromcenter(V2 center, float dist);
VektorBBox vektor_bbox_expand(VektorBBox bbox, float val);
VektorShape vektor_shape_new(VektorPrimitive prim, VektorStyle style, // shape handles
int z_index); 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);
VektorBBox vektor_shape_get_handle_bbox(V2 handle);
/* reconstructs the shape based on handles alone */
void vektor_polyline_handles_updated(VektorPolyline* polyline, V2** handles,
size_t* count, int* heldHandleIndex);
void vektor_polygon_handles_updated(VektorPolygon* polygon, V2** handles,
size_t* count, int* heldHandleIndex);
void vektor_circle_handles_updated(VektorCircle* circle, V2** handles,
size_t* count, int* heldHandleIndex);
void vektor_rectangle_handles_updated(VektorRectangle* rectangle, V2** handles,
size_t* count, int* heldHandleIndex);
void vektor_shape_handles_updated(VektorShape* shape, int* heldHandleIndex);
typedef struct { typedef struct {
VektorShape* shapes; VektorShape* shapes;

View File

@@ -1,6 +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/modifier.h"
#include "src/core/vector.h" #include "src/core/vector.h"
#include "stddef.h" #include "stddef.h"
#include <math.h> #include <math.h>
@@ -37,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;
@@ -67,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) {
EdgeBuffer edges = {0}; for (size_t i = 0; i < nodebuf->count; i++) {
for (size_t i = 0; i < shapes->count; i++) { EdgeBuffer edges = {0};
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:
@@ -95,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,
@@ -131,33 +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 = {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};
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

@@ -14,10 +14,16 @@ typedef struct {
double z; double z;
} V3; } V3;
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}; } 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};
} }
@@ -46,16 +52,16 @@ static inline double vec2_cross(const V2 a, const V2 b) {
return a.x * b.y - a.y * b.x; return a.x * b.y - a.y * b.x;
} }
static inline double vec2_norm(const V2 v) { static inline double vec2_length(const V2 v) {
return sqrt(v.x * v.x + v.y * v.y); return sqrt(v.x * v.x + v.y * v.y);
} }
static inline double vec2_quadrance(const V2 v) { static inline double vec2_lengthsq(const V2 v) {
return (v.x * v.x + v.y * v.y); return (v.x * v.x + v.y * v.y);
} }
static inline V2 vec2_normalize(const V2 v) { static inline V2 vec2_normalize(const V2 v) {
return vec2_scale(v, 1 / vec2_norm(v)); return vec2_scale(v, 1 / vec2_length(v));
} }
#endif // VECTOR_H_ #endif // VECTOR_H_

View File

@@ -1,19 +1,11 @@
#include "glib.h" #include "glib.h"
#include "gtk/gtk.h" #include "gtk/gtk.h"
#include "src/application/applicationstate.h" #include "src/application/applicationstate.h"
#include "src/core/primitives.h"
#include "stdio.h" #include "stdio.h"
#include "stdlib.h" #include "stdlib.h"
#include "./application/applicationstate.h" #include "./application/applicationstate.h"
#include "./core/raster.h"
#include "./ui/uicontroller.h" #include "./ui/uicontroller.h"
#include "./ui/vektorcanvas.h"
#include "./util/color.h"
static void on_map(GtkWidget* window, gpointer user_data) {
vektor_uictrl_map((VektorWidgetState*)user_data);
}
static int update_callback(gpointer data) { static int update_callback(gpointer data) {
VektorAppState* appstate = (VektorAppState*)data; VektorAppState* appstate = (VektorAppState*)data;
@@ -30,9 +22,6 @@ static void activate(GtkApplication* app, gpointer user_data) {
VektorAppState* app_state = (VektorAppState*)malloc(sizeof(VektorAppState)); VektorAppState* app_state = (VektorAppState*)malloc(sizeof(VektorAppState));
vektor_appstate_new(widget_state, app_state); vektor_appstate_new(widget_state, app_state);
g_signal_connect(widget_state->window, "map", G_CALLBACK(on_map),
widget_state);
g_timeout_add(1, update_callback, app_state); g_timeout_add(1, update_callback, app_state);
gtk_window_present(widget_state->window); gtk_window_present(widget_state->window);

View File

@@ -31,11 +31,6 @@ void vektor_uictrl_init(GtkApplication* app, VektorWidgetState* stateOut) {
GtkIconTheme* theme = GtkIconTheme* theme =
gtk_icon_theme_get_for_display(gdk_display_get_default()); gtk_icon_theme_get_for_display(gdk_display_get_default());
/*if (gtk_icon_theme_has_icon(theme, "vektor-circle-symbolic"))
g_print("GTK sees it!\n");
else
g_print("Still invisible...\n");*/
// populate state // populate state
stateOut->window = stateOut->window =
GTK_WINDOW(gtk_builder_get_object(builder, "main_window")); GTK_WINDOW(gtk_builder_get_object(builder, "main_window"));
@@ -75,10 +70,9 @@ void vektor_uictrl_init(GtkApplication* app, VektorWidgetState* stateOut) {
gtk_window_set_title(stateOut->window, "Vektor"); gtk_window_set_title(stateOut->window, "Vektor");
gtk_window_set_default_size(stateOut->window, 800, 600); gtk_window_set_default_size(stateOut->window, 800, 600);
// Set dimensions
gtk_paned_set_position(stateOut->workspacePaned, 800 * .7);
gtk_paned_set_position(stateOut->sidepanelPaned, 250);
g_object_unref(builder); g_object_unref(builder);
} }
void vektor_uictrl_map(VektorWidgetState* state) {
gtk_paned_set_position(state->workspacePaned, 800 * .7);
gtk_paned_set_position(state->sidepanelPaned, 250);
}

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;
@@ -45,6 +46,7 @@ static GLuint shader_selection_uMaxLoc;
static GLuint vao; static GLuint vao;
VertexBuffer vb; VertexBuffer vb;
static size_t shape_vertex_count = 0;
static GLuint compile_shader(GLenum type, const char* src) { static GLuint compile_shader(GLenum type, const char* src) {
GLuint shader = glCreateShader(type); GLuint shader = glCreateShader(type);
@@ -102,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 =
@@ -135,23 +139,41 @@ static void init_geometry(void) {
glBindVertexArray(0); glBindVertexArray(0);
} }
static gboolean render(GtkGLArea* a, GdkGLContext* ctx,
VektorCanvasRenderInfo* renderInfo) { void vektor_canvas_geometry_changed(VektorCanvasRenderInfo* renderInfo) {
vb.count = 0; vb.count = 0;
vektor_vb_rasterize(&vb, renderInfo->shapes, renderInfo->zoom);
shape_vertex_count = vb.count;
vektor_rasterize(&vb, renderInfo->shapes, renderInfo->zoom);
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 && if (renderInfo->selectedShape != NULL &&
*(renderInfo->selectedShape) != NULL) { *(renderInfo->selectedShape) != NULL) {
VektorBBox bbox = vektor_primitive_get_bbox(
(*(renderInfo->selectedShape))->primitive); VektorShapeNode* selectedShape = *renderInfo->selectedShape;
// create handle quads if a shape is selected
for (size_t i = 0; i < selectedShape->base->handleCount; i++) {
V2 handle = selectedShape->base->handles[i];
VektorBBox handleBbox = vektor_shape_get_handle_bbox(handle);
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->base->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_vb_add_quad(&vb, bbox.min, bbox.max,
vektor_color_new(255, 255, 255, 255)); vektor_color_new(255, 255, 255, 255));
} }
}
static gboolean render(GtkGLArea* a, GdkGLContext* ctx,
VektorCanvasRenderInfo* 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);
@@ -166,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);
@@ -174,21 +196,24 @@ 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);
glUseProgram(selection_shader_program); glUseProgram(selection_shader_program);
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, 1); glUniform4f(shader_selection_uC1Loc, 0, 0, 0, 0);
glUniform4f(shader_selection_uC2Loc, 0.46, 0.46, 1, 1); glUniform4f(shader_selection_uC2Loc, 0.46, 0.46, 1, 1);
glDrawArrays(GL_TRIANGLES, shape_vertex_count, glDrawArrays(GL_TRIANGLES, shape_vertex_count,
@@ -256,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,
@@ -337,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;
@@ -44,6 +45,7 @@ typedef struct VektorCanvasRenderInfo {
void vektor_canvas_init(VektorWidgetState* state, VektorCanvas* canvasOut, void vektor_canvas_init(VektorWidgetState* state, VektorCanvas* canvasOut,
VektorCanvasRenderInfo* renderInfo); VektorCanvasRenderInfo* renderInfo);
void vektor_canvas_geometry_changed(VektorCanvasRenderInfo* renderInfo);
// void vektor_canvas_update(VektorCanvas* canvas); // void vektor_canvas_update(VektorCanvas* canvas);
// void vektor_canvas_fill(VektorCanvas* canvas, VektorColor color); // void vektor_canvas_fill(VektorCanvas* canvas, VektorColor color);
// void vektor_canvas_drawfrom(VektorFramebuffer* fb, VektorCanvas* canvas); // void vektor_canvas_drawfrom(VektorFramebuffer* fb, VektorCanvas* canvas);

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">