diff --git a/src/application/applicationstate.c b/src/application/applicationstate.c index 00fa350..d11b216 100644 --- a/src/application/applicationstate.c +++ b/src/application/applicationstate.c @@ -90,9 +90,6 @@ static void canvas_onclick(GtkGestureClick* gesture, int n_press, double x, int widget_w = gtk_widget_get_width(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){(2 * (x / widget_w)) - 1, 1 - (2 * (y / widget_h))}; @@ -163,15 +160,14 @@ begin_click_dispatch: VektorCircle* circle = vektor_circle_new(); VektorPrimitive circlePrimitive = (VektorPrimitive){.kind = VEKTOR_CIRCLE, .circle = *circle}; - VektorStyle style = (VektorStyle){ - .stroke_color = state->currentColor, .stroke_width = 0.01}; + VektorStyle style = (VektorStyle){.stroke_color = state->currentColor, + .stroke_width = 0.01}; vektor_shapebuffer_add_shape( - state->shapeBuffer, - vektor_shape_new(circlePrimitive, style, 0)); + state->shapeBuffer, vektor_shape_new(circlePrimitive, style, 0)); state->selectedShape = &(state->shapeBuffer->shapes[state->shapeBuffer->count - 1]); - + vektor_circle_free(circle); vektor_circle_set_center(&state->selectedShape->primitive.circle, pos); @@ -213,6 +209,36 @@ begin_click_dispatch: } } +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)); + + int widget_w = gtk_widget_get_width(widget); + int widget_h = gtk_widget_get_height(widget); + + V2 normalized_coords = + (V2){(2 * (x / widget_w)) - 1, 1 - (2 * (y / widget_h))}; + +} + +void vektor_appstate_canvas_drag_update(GtkGestureDrag* gesture, + gdouble x, gdouble y, + gpointer user_data) { + 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)); + + int widget_w = gtk_widget_get_width(widget); + int widget_h = gtk_widget_get_height(widget); + + V2 norm = + (V2){(2 * ( (x+start_x) / widget_w)) - 1, 1 - (2 * ( (y+start_y) / widget_h))}; + +} + void vektor_appstate_new(VektorWidgetState* wstate, VektorAppState* stateOut) { button_tool_set_data* data_linetool = malloc(sizeof(button_tool_set_data)); data_linetool->state = stateOut; @@ -302,4 +328,14 @@ void vektor_appstate_new(VektorWidgetState* wstate, VektorAppState* stateOut) { G_CALLBACK(canvas_onclick), stateOut); gtk_widget_add_controller(GTK_WIDGET(wstate->workspaceCanvas), 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); + + gtk_widget_add_controller(GTK_WIDGET(wstate->workspaceCanvas), + GTK_EVENT_CONTROLLER(canvasDragGesture)); } \ No newline at end of file diff --git a/src/core/primitives.c b/src/core/primitives.c index 7cacf75..0ea2309 100644 --- a/src/core/primitives.c +++ b/src/core/primitives.c @@ -1,9 +1,12 @@ #include "primitives.h" +#include "glib.h" #include "src/core/vector.h" #include #include #include +// ------ PER-PRIMITIVE METHODS ------ + VektorPolyline* vektor_polyline_new(void) { VektorPolyline* pl = malloc(sizeof(VektorPolyline)); pl->count = 0; @@ -95,22 +98,6 @@ void vektor_rectangle_set_start(VektorRectangle* rct, V2 point) { 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) { V2 first = prim.polyline->points[0]; @@ -188,11 +175,167 @@ VektorBBox vektor_primitive_get_bbox(VektorPrimitive prim) { } } +// ------ PRIMITIVE HANDLES GENERATION ------ + +/* [n]: polyline vertices */ +void vektor_polyline_create_handles(VektorPolyline* polyline, V2** handleArr, size_t* count) { + *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; + } +} + +// ------ PRIMITIVE HANDLES UPDATING ------ + +void vektor_polyline_handles_updated(VektorPolyline* polyline, V2** handles, size_t* count) { + if(*count != polyline->count) { + g_warning("handle count & point count mismatch in polyline"); + return; + } + for(size_t i = 0; i < *count; i++) { + polyline->points[i] = (*handles)[i]; + } +} + +void vektor_polygon_handles_updated(VektorPolygon* polygon, V2** handles, size_t* count) { + if(*count != polygon->count) { + g_warning("handle count & point count mismatch in polygon"); + return; + } + for(size_t i = 0; i < *count; i++) { + polygon->points[i] = (*handles)[i]; + } +} + +void vektor_circle_handles_updated(VektorCircle* circle, V2** handles, size_t* count) { + if(*count != 2) { + g_warning("unexpected circle handle count (%zu)", *count); + return; + } + circle->center = (*handles)[0]; + circle->radius = vec2_length(vec2_sub((*handles)[0], (*handles)[1])); +} + +void vektor_rectangle_handles_updated(VektorRectangle* rectangle, V2** handles, size_t* count) { + if(*count != 5) { + g_warning("unexpected rectangle handle count (%zu)", *count); + return; + } + + // get rectangle center + V2 halfdist = vec2_scale(vec2_sub(rectangle->end, rectangle->start), 0.5f); + V2 rectcenter = vec2_add(rectangle->start, halfdist); + + // center according to handles + V2 center = (*handles)[0]; + + if(vec2_equals(center, rectcenter)) { // corner handles were changed + V2 p1 = (*handles)[1]; + V2 p2 = (*handles)[2]; + V2 p3 = (*handles)[3]; + V2 p4 = (*handles)[4]; + + float min_x = fminf(p1.x, fminf(p2.x, fminf(p3.x, p4.x))); + float min_y = fminf(p1.y, fminf(p2.y, fminf(p3.y, p4.y))); + float max_x = fmaxf(p1.x, fmaxf(p2.x, fmaxf(p3.x, p4.x))); + float max_y = fmaxf(p1.y, fmaxf(p2.y, fmaxf(p3.y, p4.y))); + + V2 min = (V2){min_x, min_y}; + V2 max = (V2){max_x, max_y}; + + VektorRectangle propertRect = (VektorRectangle){min,max}; + // overwrite handles array (create_handles() frees the passed one) + vektor_rectangle_create_handles(&propertRect, handles, count); + + } else { // corner was dragged + V2 translation = vec2_sub(center, rectcenter); + V2 newmax = vec2_add((*handles)[2], translation); + V2 newmin = vec2_add((*handles)[3], translation); + + VektorRectangle propertRect = (VektorRectangle){newmin,newmax}; + vektor_rectangle_create_handles(&propertRect, handles, count); + } + +} + +void vektor_shape_handles_updated(VektorShape* shape) { + switch(shape->primitive.kind) { + case VEKTOR_POLYLINE: + vektor_polyline_handles_updated(shape->primitive.polyline, &shape->handles, &shape->handleCount); + break; + case VEKTOR_POLYGON: + vektor_polygon_handles_updated(shape->primitive.polygon, &shape->handles, &shape->handleCount); + break; + case VEKTOR_CIRCLE: + vektor_circle_handles_updated(&shape->primitive.circle, &shape->handles, &shape->handleCount); + break; + case VEKTOR_RECTANGLE: + vektor_rectangle_handles_updated(&shape->primitive.rectangle, &shape->handles, &shape->handleCount); + break; + } +} + +// ------ BBOX METHODS ------ + bool vektor_bbox_isinside(VektorBBox bbox, V2 point) { return point.x >= bbox.min.x && point.y >= bbox.min.y && point.x <= bbox.max.x && point.y <= bbox.max.y; } +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}; +} + +// ------ SHAPE METHODS ------ + VektorShape vektor_shape_new(VektorPrimitive prim, VektorStyle style, int z_index) { return (VektorShape){.primitive = prim, @@ -206,4 +349,20 @@ void vektor_shapes_update_bbox(VektorShapeBuffer* buffer) { buffer->shapes[i].bbox = 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); + } } \ No newline at end of file diff --git a/src/core/primitives.h b/src/core/primitives.h index c736f8e..8fd694b 100644 --- a/src/core/primitives.h +++ b/src/core/primitives.h @@ -45,6 +45,26 @@ typedef struct { }; } VektorPrimitive; +typedef struct { + VektorColor stroke_color; + float stroke_width; +} VektorStyle; + +typedef struct { + V2 min; + V2 max; +} VektorBBox; + +typedef struct { + VektorStyle style; + int z_index; + VektorBBox bbox; + VektorPrimitive primitive; + + V2* handles; + size_t handleCount; +} VektorShape; + VektorPolyline* vektor_polyline_new(void); void vektor_polyline_add_point(VektorPolyline* pl, V2 point); void vektor_polyline_free(VektorPolyline* pl); @@ -63,22 +83,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); -typedef struct { - VektorColor stroke_color; - float stroke_width; -} VektorStyle; - -typedef struct { - V2 min; - V2 max; -} VektorBBox; - -typedef struct { - VektorStyle style; - int z_index; - VektorBBox bbox; - VektorPrimitive primitive; -} VektorShape; +VektorShape vektor_shape_new(VektorPrimitive prim, VektorStyle style, int z_index); VektorBBox vektor_polyline_get_bbox(VektorPrimitive prim); VektorBBox vektor_polygon_get_bbox(VektorPrimitive prim); @@ -87,9 +92,21 @@ VektorBBox vektor_rectangle_get_bbox(VektorPrimitive prim); VektorBBox vektor_primitive_get_bbox(VektorPrimitive prim); bool vektor_bbox_isinside(VektorBBox bbox, V2 point); +VektorBBox vektor_bbox_fromcenter(V2 center, float dist); -VektorShape vektor_shape_new(VektorPrimitive prim, VektorStyle style, - int z_index); +// shape handles +void vektor_polyline_create_handles(VektorPolyline* polyline, V2** handleArr, size_t* count); +void vektor_polygon_create_handles(VektorPolygon* polygon, V2** handleArr, size_t* count); +void vektor_circle_create_handles(VektorCircle* circle, V2** handleArr, size_t* count); +void vektor_rectangle_create_handles(VektorRectangle* rectangle, V2** handleArr, size_t* count); +void vektor_shape_create_handles(VektorShape* shape); + + /* reconstructs the shape based on handles alone */ +void vektor_polyline_handles_updated(VektorPolyline* polyline, V2** handles, size_t* count); +void vektor_polygon_handles_updated(VektorPolygon* polygon, V2** handles, size_t* count); +void vektor_circle_handles_updated(VektorCircle* circle, V2** handles, size_t* count); +void vektor_rectangle_handles_updated(VektorRectangle* rectangle, V2** handles, size_t* count); +void vektor_shape_handles_updated(VektorShape* shape); typedef struct { VektorShape* shapes; diff --git a/src/core/vector.h b/src/core/vector.h index dd53e9e..84f45f2 100644 --- a/src/core/vector.h +++ b/src/core/vector.h @@ -48,16 +48,16 @@ static inline double vec2_cross(const V2 a, const V2 b) { 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); } -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); } 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_ diff --git a/src/ui/vektorcanvas.c b/src/ui/vektorcanvas.c index f475210..9dff91d 100644 --- a/src/ui/vektorcanvas.c +++ b/src/ui/vektorcanvas.c @@ -188,7 +188,7 @@ static gboolean render(GtkGLArea* a, GdkGLContext* ctx, glUniform1f(shader_selection_uTimeLoc, time); glUniform2f(shader_selection_uMinLoc, bbox.min.x, bbox.min.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); glDrawArrays(GL_TRIANGLES, shape_vertex_count,