diff --git a/src/application/applicationstate.c b/src/application/applicationstate.c index 8a16544..92d24a5 100644 --- a/src/application/applicationstate.c +++ b/src/application/applicationstate.c @@ -124,7 +124,7 @@ begin_click_dispatch: // selecting a tool resets the selection, so this condition // should not happen g_warning("Invalid selected primitive; polyline expected"); - state->selectedShape = NULL; + vektor_appstate_deselect_shape(state); goto begin_click_dispatch; // retry } @@ -153,7 +153,7 @@ begin_click_dispatch: } else if (state->selectedShape->primitive.kind != VEKTOR_POLYGON) { g_warning("Invalid selected primitive; polygon expected"); - state->selectedShape = NULL; + vektor_appstate_deselect_shape(state); goto begin_click_dispatch; // retry } @@ -215,17 +215,19 @@ begin_click_dispatch: for (size_t i = 0; i < state->shapeBuffer->count; i++) { VektorBBox bbox = vektor_primitive_get_bbox( state->shapeBuffer->shapes[i].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)) { state->selectedShape = &(state->shapeBuffer->shapes[i]); return; } } // was clicked outside any shapes - reset selection - state->selectedShape = NULL; + vektor_appstate_deselect_shape(state); } - - if (state->selectedShape != NULL) - g_print("%zu\n", state->selectedShape->handleCount); } void vektor_appstate_canvas_drag_begin(GtkGestureDrag* gesture, gdouble x, @@ -233,26 +235,65 @@ void vektor_appstate_canvas_drag_begin(GtkGestureDrag* gesture, gdouble x, 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 normalized_coords = + V2 position = (V2){(2 * (x / widget_w)) - 1, 1 - (2 * (y / widget_h))}; + + if(state->selectedShape != NULL) { + VektorShape* selectedShape = state->selectedShape; + + // get selected shape's handles and check + // if we click any of them + for(size_t i = 0; i < selectedShape->handleCount; i++) { + VektorBBox bbox = vektor_shape_get_handle_bbox(selectedShape->handles[i]); + if(vektor_bbox_isinside(bbox, position)) { + // clicked inside handle + state->heldHandleIndex = i; + 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 norm = (V2){(2 * ((x + start_x) / widget_w)) - 1, + V2 position = (V2){(2 * ((x + start_x) / widget_w)) - 1, 1 - (2 * ((y + start_y) / widget_h))}; + + // drag handle if selected + if(state->selectedShape != NULL && state->heldHandleIndex != -1) { + state->selectedShape->handles[state->heldHandleIndex] = position; + vektor_shape_handles_updated(state->selectedShape); + } +} + +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 + } } void vektor_appstate_new(VektorWidgetState* wstate, VektorAppState* stateOut) { @@ -292,6 +333,7 @@ void vektor_appstate_new(VektorWidgetState* wstate, VektorAppState* stateOut) { stateOut->widgetState = wstate; stateOut->currentColor = vektor_color_solid(0, 0, 0); stateOut->selectedShape = NULL; + stateOut->heldHandleIndex = -1; VektorCanvasRenderInfo* renderInfo = malloc(sizeof(VektorCanvasRenderInfo)); renderInfo->zoom = 1; @@ -351,7 +393,14 @@ void vektor_appstate_new(VektorWidgetState* wstate, VektorAppState* stateOut) { 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; } \ No newline at end of file diff --git a/src/application/applicationstate.h b/src/application/applicationstate.h index 2e48c2a..259fa81 100644 --- a/src/application/applicationstate.h +++ b/src/application/applicationstate.h @@ -21,6 +21,7 @@ typedef struct VektorAppState { VektorAppTool selectedTool; VektorShape* selectedShape; + int heldHandleIndex; VektorColor currentColor; @@ -33,5 +34,6 @@ typedef struct VektorAppState { void vektor_appstate_new(VektorWidgetState* wstate, VektorAppState* stateOut); void vektor_appstate_canvas_click(VektorAppState* state, double x, double y); +void vektor_appstate_deselect_shape(VektorAppState* state); #endif \ No newline at end of file diff --git a/src/core/primitives.c b/src/core/primitives.c index fe667bf..54527ae 100644 --- a/src/core/primitives.c +++ b/src/core/primitives.c @@ -207,8 +207,6 @@ void vektor_rectangle_create_handles(VektorRectangle* rectangle, V2** handleArr, V2 halfdist = vec2_scale(vec2_sub(rectangle->end, rectangle->start), 0.5f); V2 center = vec2_add(rectangle->start, halfdist); - g_print("boobs: %f %f\n", rectangle->start.x, rectangle->start.y); - g_print("pussy: %f %f\n", rectangle->end.x, rectangle->end.y); (*handleArr)[0] = center; (*handleArr)[1] = vec2_add(center, vec2_mul(halfdist, (V2){-1.0f, 1.0f})); @@ -238,6 +236,23 @@ void vektor_shape_create_handles(VektorShape* shape) { } } +// ------ 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, @@ -335,16 +350,6 @@ void vektor_shape_handles_updated(VektorShape* shape) { } } -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; -} - // ------ BBOX METHODS ------ bool vektor_bbox_isinside(VektorBBox bbox, V2 point) { diff --git a/src/core/primitives.h b/src/core/primitives.h index dbd0d86..2690de0 100644 --- a/src/core/primitives.h +++ b/src/core/primitives.h @@ -108,7 +108,10 @@ void vektor_circle_create_handles(VektorCircle* circle, V2** handleArr, 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, diff --git a/src/ui/vektorcanvas.c b/src/ui/vektorcanvas.c index 41b6b93..4de0059 100644 --- a/src/ui/vektorcanvas.c +++ b/src/ui/vektorcanvas.c @@ -151,7 +151,7 @@ static gboolean render(GtkGLArea* a, GdkGLContext* ctx, // create handle quads if a shape is selected for (size_t i = 0; i < selectedShape->handleCount; i++) { V2 handle = selectedShape->handles[i]; - VektorBBox handleBbox = vektor_bbox_fromcenter(handle, 0.01f); + VektorBBox handleBbox = vektor_shape_get_handle_bbox(handle); vektor_vb_add_quad(&vb, handleBbox.min, handleBbox.max, vektor_color_new(255, 255, 255, 255)); }