diff --git a/icons/hicolor/scalable/actions/tool-brush-symbolic.svg b/icons/hicolor/scalable/actions/tool-brush-symbolic.svg
new file mode 100644
index 0000000..d6150fe
--- /dev/null
+++ b/icons/hicolor/scalable/actions/tool-brush-symbolic.svg
@@ -0,0 +1,8 @@
+
diff --git a/icons/hicolor/scalable/actions/tool-pencil-symbolic.svg b/icons/hicolor/scalable/actions/tool-pencil-symbolic.svg
new file mode 100644
index 0000000..10f5846
--- /dev/null
+++ b/icons/hicolor/scalable/actions/tool-pencil-symbolic.svg
@@ -0,0 +1,10 @@
+
diff --git a/src/application/applicationstate.c b/src/application/applicationstate.c
index d906f72..6112a2b 100644
--- a/src/application/applicationstate.c
+++ b/src/application/applicationstate.c
@@ -47,7 +47,7 @@ static void appstate_on_color_change(VektorColorWheel* wheel,
appstate->currentColor = c;
if (appstate->selectedShape != NULL) {
- appstate->selectedShape->base.style.stroke_color = c;
+ appstate->selectedShape->base->style.stroke_color = c;
}
// set entry fields under the color selector
@@ -125,7 +125,7 @@ begin_click_dispatch:
state->selectedShape =
&(state->shapeBuffer->nodes[state->shapeBuffer->count - 1]);
- } else if (state->selectedShape->base.primitive.kind !=
+ } else if (state->selectedShape->base->primitive.kind !=
VEKTOR_POLYLINE) {
// selecting a tool resets the selection, so this condition
// should not happen
@@ -134,13 +134,13 @@ begin_click_dispatch:
goto begin_click_dispatch; // retry
}
- vektor_polyline_add_point(state->selectedShape->base.primitive.polyline,
+ vektor_polyline_add_point(state->selectedShape->base->primitive.polyline,
pos);
- state->selectedShape->base.bbox =
- vektor_primitive_get_bbox(state->selectedShape->base.primitive);
+ 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);
+ vektor_shape_add_handle(state->selectedShape->base, pos);
} else if (state->selectedTool == VektorPolygonTool) {
// create new polygon shape if none is selected
@@ -158,20 +158,20 @@ begin_click_dispatch:
state->selectedShape =
&(state->shapeBuffer->nodes[state->shapeBuffer->count - 1]);
- } else if (state->selectedShape->base.primitive.kind !=
+ } else if (state->selectedShape->base->primitive.kind !=
VEKTOR_POLYGON) {
g_warning("Invalid selected primitive; polygon expected");
vektor_appstate_deselect_shape(state);
goto begin_click_dispatch; // retry
}
- vektor_polygon_add_point(state->selectedShape->base.primitive.polygon,
+ vektor_polygon_add_point(state->selectedShape->base->primitive.polygon,
pos);
- state->selectedShape->base.bbox =
- vektor_primitive_get_bbox(state->selectedShape->base.primitive);
+ 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);
+ vektor_shape_add_handle(state->selectedShape->base, pos);
} else if (state->selectedTool == VektorCircleTool) {
@@ -189,18 +189,18 @@ begin_click_dispatch:
vektor_circle_free(circle);
- vektor_circle_set_center(&state->selectedShape->base.primitive.circle,
+ vektor_circle_set_center(&state->selectedShape->base->primitive.circle,
pos);
- vektor_circle_set_radius(&state->selectedShape->base.primitive.circle,
+ vektor_circle_set_radius(&state->selectedShape->base->primitive.circle,
0.1f);
- state->selectedShape->base.bbox =
- vektor_primitive_get_bbox(state->selectedShape->base.primitive);
+ 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);
+ &state->selectedShape->base->primitive.circle,
+ &state->selectedShape->base->handles,
+ &state->selectedShape->base->handleCount);
} else if (state->selectedTool == VektorRectangleTool) {
VektorRectangle* rect = vektor_rectangle_new();
@@ -218,22 +218,22 @@ begin_click_dispatch:
vektor_rectangle_free(rect);
vektor_rectangle_set_start(
- &state->selectedShape->base.primitive.rectangle, pos);
+ &state->selectedShape->base->primitive.rectangle, pos);
vektor_rectangle_set_end(
- &state->selectedShape->base.primitive.rectangle,
+ &state->selectedShape->base->primitive.rectangle,
vec2_add(pos, (V2){0.1f, 0.1f}));
vektor_rectangle_create_handles(
- &state->selectedShape->base.primitive.rectangle,
- &state->selectedShape->base.handles,
- &state->selectedShape->base.handleCount);
+ &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);
+ state->selectedShape->base->bbox =
+ vektor_primitive_get_bbox(state->selectedShape->base->primitive);
} else if (state->selectedTool == VektorSelectionTool) {
for (size_t i = 0; i < state->shapeBuffer->count; i++) {
VektorBBox bbox = vektor_primitive_get_bbox(
- state->shapeBuffer->nodes[i].base.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
@@ -268,9 +268,9 @@ void vektor_appstate_canvas_drag_begin(GtkGestureDrag* gesture, gdouble x,
// get selected shape's handles and check
// if we click any of them
- for (size_t i = 0; i < selectedShape->base.handleCount; i++) {
+ for (size_t i = 0; i < selectedShape->base->handleCount; i++) {
VektorBBox bbox =
- vektor_shape_get_handle_bbox(selectedShape->base.handles[i]);
+ vektor_shape_get_handle_bbox(selectedShape->base->handles[i]);
if (vektor_bbox_isinside(bbox, position)) {
// clicked inside handle
state->heldHandleIndex = i;
@@ -303,8 +303,8 @@ void vektor_appstate_canvas_drag_update(GtkGestureDrag* gesture, gdouble x,
// 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->selectedShape->base->handles[state->heldHandleIndex] = position;
+ vektor_shape_handles_updated(state->selectedShape->base,
&state->heldHandleIndex);
vektor_canvas_geometry_changed(state->renderInfo);
}
diff --git a/src/core/modifier.c b/src/core/modifier.c
index fe984ff..da43734 100644
--- a/src/core/modifier.c
+++ b/src/core/modifier.c
@@ -1,14 +1,69 @@
#include "modifier.h"
+#include
-VektorShapeNode vektor_shapenode_new(VektorShape shape) {
+VektorShapeNode vektor_shapenode_new(VektorShape* shape) {
VektorShapeNode node = (VektorShapeNode){
- .base = shape, .modifier_count = 0, .evaluated = shape};
+ .base = shape,
+ .modifier_count = 0,
+ .evaluated = shape,
+ .base_dirty = true
+ };
return node;
}
-VektorShape* vektor_shapenode_get_evaluated(VektorShapeNode* shapeNode) {
+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;
+ }
- return &shapeNode->evaluated;
}
void vektor_shapenodebuf_add(VektorShapeNodeBuffer* buffer,
diff --git a/src/core/modifier.h b/src/core/modifier.h
index 0f17f8e..b37ad96 100644
--- a/src/core/modifier.h
+++ b/src/core/modifier.h
@@ -3,7 +3,10 @@
#include "src/core/primitives.h"
-typedef enum { VEKTOR_MODIFIER_BEVEL } VektorModifierType;
+typedef enum {
+ VEKTOR_MODIFIER_IDENTITY,
+ VEKTOR_MODIFIER_BEVEL
+} VektorModifierType;
typedef struct VektorModifier {
VektorModifierType type;
@@ -11,13 +14,14 @@ typedef struct VektorModifier {
bool dirty;
void* parameters;
- VektorShape (*apply)(struct VektorModifier mod, VektorShape input);
+ VektorShape (*apply)(struct VektorModifier* mod, VektorShape* input);
+ VektorShape cachedEvaluatedShape;
} VektorModifier;
typedef struct VektorShapeNode {
- VektorShape base;
- VektorShape evaluated;
+ VektorShape* base;
+ VektorShape* evaluated;
VektorModifier* modifiers;
size_t modifier_count;
@@ -31,8 +35,12 @@ typedef struct VektorShapeNodeBuffer {
size_t capacity;
} VektorShapeNodeBuffer;
-VektorShapeNode vektor_shapenode_new(VektorShape shape);
+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,
diff --git a/src/core/modifiers/m_identity.c b/src/core/modifiers/m_identity.c
new file mode 100644
index 0000000..f731796
--- /dev/null
+++ b/src/core/modifiers/m_identity.c
@@ -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
+ };
+}
+
diff --git a/src/core/primitives.c b/src/core/primitives.c
index 2744bf0..225db61 100644
--- a/src/core/primitives.c
+++ b/src/core/primitives.c
@@ -450,9 +450,10 @@ VektorBBox vektor_bbox_expand(VektorBBox bbox, float val) {
// ------ SHAPE METHODS ------
-VektorShape vektor_shape_new(VektorPrimitive prim, VektorStyle style,
+VektorShape* vektor_shape_new(VektorPrimitive prim, VektorStyle style,
int z_index) {
- VektorShape shape = (VektorShape){.primitive = prim,
+ VektorShape* shape = malloc(sizeof(VektorShape));
+ *shape = (VektorShape){.primitive = prim,
.style = style,
.transform = m33_identity(),
.z_index = z_index,
@@ -464,7 +465,7 @@ VektorShape vektor_shape_new(VektorPrimitive prim, VektorStyle style,
the passed value's pointer to an array of handles remains valid in the
passed copy.
*/
- vektor_shape_create_handles(&shape);
+ vektor_shape_create_handles(shape);
return shape;
}
diff --git a/src/core/primitives.h b/src/core/primitives.h
index 32e919d..33b5b46 100644
--- a/src/core/primitives.h
+++ b/src/core/primitives.h
@@ -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_free(VektorRectangle* rct);
-VektorShape vektor_shape_new(VektorPrimitive prim, VektorStyle style,
+VektorShape* vektor_shape_new(VektorPrimitive prim, VektorStyle style,
int z_index);
VektorBBox vektor_polyline_get_bbox(VektorPrimitive prim);
diff --git a/src/core/raster.c b/src/core/raster.c
index 72f6c0d..41d29ec 100644
--- a/src/core/raster.c
+++ b/src/core/raster.c
@@ -74,9 +74,13 @@ void vektor_vb_rasterize(VertexBuffer* vb, VektorShapeNodeBuffer* nodebuf,
double scale) {
for (size_t i = 0; i < nodebuf->count; i++) {
EdgeBuffer edges = {0};
- VektorPrimitive* p = &nodebuf->nodes[i].base.primitive;
- VektorStyle style = nodebuf->nodes[i].base.style;
- M33 transform = nodebuf->nodes[i].base.transform;
+
+ vektor_shapenode_update(&nodebuf->nodes[i]);
+ VektorShape* currentShape = vektor_shapenode_get_evaluated(&nodebuf->nodes[i]);
+
+ VektorPrimitive* p = ¤tShape->primitive;
+ VektorStyle style = currentShape->style;
+ M33 transform = currentShape->transform;
switch (p->kind) {
case VEKTOR_POLYLINE:
diff --git a/src/ui/vektorcanvas.c b/src/ui/vektorcanvas.c
index eef4f0a..21d95bc 100644
--- a/src/ui/vektorcanvas.c
+++ b/src/ui/vektorcanvas.c
@@ -151,8 +151,8 @@ void vektor_canvas_geometry_changed(VektorCanvasRenderInfo* renderInfo) {
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];
+ 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));
@@ -162,7 +162,7 @@ void vektor_canvas_geometry_changed(VektorCanvasRenderInfo* renderInfo) {
// create selection quad if a shape is selected
VektorBBox bbox =
- vektor_primitive_get_bbox(selectedShape->base.primitive);
+ vektor_primitive_get_bbox(selectedShape->base->primitive);
// expand it a little so it is not inset
bbox = vektor_bbox_expand(bbox, 0.03f);
@@ -202,7 +202,7 @@ static gboolean render(GtkGLArea* a, GdkGLContext* ctx,
// re-fetch bbox (we know a shape is selected)
VektorBBox bbox = vektor_primitive_get_bbox(
- (*(renderInfo->selectedShape))->base.primitive);
+ (*(renderInfo->selectedShape))->base->primitive);
bbox = vektor_bbox_expand(bbox, 0.03f);
glUseProgram(selection_shader_program);
diff --git a/ui/main.ui b/ui/main.ui
index 0e19809..80014cc 100644
--- a/ui/main.ui
+++ b/ui/main.ui
@@ -31,6 +31,29 @@
+
+
+
+
+