From d7e5c97357def056c805b596bdf1dccd0df19ff4 Mon Sep 17 00:00:00 2001 From: cilix42 Date: Mon, 26 Jan 2009 14:41:05 +0000 Subject: [PATCH] display guide anchor on canvas; anchor and angle can be edited by mouse --- src/2geom/point.cpp | 14 ++++- src/2geom/point.h | 3 + src/desktop-events.cpp | 120 +++++++++++++++++++++++++++++++------- src/display/guideline.cpp | 47 ++++++++++++++- src/display/guideline.h | 4 ++ src/sp-guide.cpp | 4 +- 6 files changed, 166 insertions(+), 26 deletions(-) diff --git a/src/2geom/point.cpp b/src/2geom/point.cpp index 5dd7b52f2..b8a858f8d 100644 --- a/src/2geom/point.cpp +++ b/src/2geom/point.cpp @@ -2,7 +2,7 @@ #include #include <2geom/coord.h> #include <2geom/isnan.h> //temporary fix for isnan() -#include <2geom/matrix.h> +#include <2geom/transforms.h> namespace Geom { @@ -150,6 +150,18 @@ Point &Point::operator*=(Matrix const &m) return *this; } +Point constrain_angle(Point const &ref, Point const &pt, unsigned int n, Point const &dir) +{ + // for special cases we could perhaps use faster routines + if (n == 0.0) { + return pt; + } + Point diff(pt - ref); + double angle = -angle_between(diff, dir); + double k = round(angle * (double)n / (2.0*M_PI)); + return ref + dir * Rotate(k * 2.0 * M_PI / (double)n) * L2(diff); +} + } //Namespace Geom /* diff --git a/src/2geom/point.h b/src/2geom/point.h index d89b53f83..39538c832 100644 --- a/src/2geom/point.h +++ b/src/2geom/point.h @@ -229,6 +229,9 @@ Point operator*(Point const &v, Matrix const &m); Point operator/(Point const &p, Matrix const &m); +/** Constrains the angle between a and b to a multiple of pi/n with respect to dir. */ +Point constrain_angle(Point const &ref, Point const &pt, unsigned int n = 4, Geom::Point const &dir = Geom::Point(1,0)); + } /* namespace Geom */ #endif /* !SEEN_Geom_POINT_H */ diff --git a/src/desktop-events.cpp b/src/desktop-events.cpp index fbbbfee19..6cf1dde1a 100644 --- a/src/desktop-events.cpp +++ b/src/desktop-events.cpp @@ -39,7 +39,7 @@ #include "preferences.h" #include "helper/action.h" #include "tools-switch.h" -#include <2geom/point.h> +#include <2geom/line.h> static void snoop_extended(GdkEvent* event, SPDesktop *desktop); static void init_extended(); @@ -198,19 +198,30 @@ int sp_dt_vruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidget *dtw) /* Guides */ +static Geom::Point drag_origin; + +enum SPGuideDragType { + SP_DRAG_TRANSLATE, + SP_DRAG_TRANSLATE_CONSTRAINED, + SP_DRAG_ROTATE, + SP_DRAG_MOVE_ORIGIN, + SP_DRAG_NONE +}; + +static SPGuideDragType drag_type = SP_DRAG_NONE; + gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) { - static bool dragging = false; static bool moved = false; gint ret = FALSE; SPGuide *guide = SP_GUIDE(data); SPDesktop *desktop = static_cast(gtk_object_get_data(GTK_OBJECT(item->canvas), "SPDesktop")); - switch (event->type) { + switch (event->type) { case GDK_2BUTTON_PRESS: if (event->button.button == 1) { - dragging = false; + drag_type = SP_DRAG_NONE; sp_canvas_set_snap_delay_active(desktop->canvas, false); sp_canvas_item_ungrab(item, event->button.time); Inkscape::UI::Dialogs::GuidelinePropertiesDialog::showDialog(guide, desktop); @@ -226,30 +237,68 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) ret = TRUE; break; } - dragging = true; + sp_canvas_set_snap_delay_active(desktop->canvas, true); - sp_canvas_item_grab(item, - ( GDK_BUTTON_RELEASE_MASK | - GDK_BUTTON_PRESS_MASK | - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK ), - NULL, - event->button.time); + double tol = 40.0; + Geom::Point const event_w(event->button.x, event->button.y); + Geom::Point const event_dt(desktop->w2d(event_w)); + drag_origin = event_dt; + if (Geom::L2(guide->point_on_line - event_dt) < tol) { + // the click was on the guide 'anchor' + drag_type = (event->button.state & GDK_SHIFT_MASK) ? SP_DRAG_MOVE_ORIGIN : SP_DRAG_TRANSLATE; + } else { + drag_type = (event->button.state & GDK_SHIFT_MASK) ? SP_DRAG_TRANSLATE : SP_DRAG_ROTATE; + sp_canvas_item_grab(item, + ( GDK_BUTTON_RELEASE_MASK | + GDK_BUTTON_PRESS_MASK | + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK ), + NULL, + event->button.time); + } ret = TRUE; } break; - case GDK_MOTION_NOTIFY: - if (dragging) { + case GDK_MOTION_NOTIFY: + if (drag_type != SP_DRAG_NONE) { Geom::Point const motion_w(event->motion.x, - event->motion.y); - Geom::Point motion_dt(to_2geom(desktop->w2d(from_2geom(motion_w)))); + event->motion.y); + Geom::Point motion_dt(desktop->w2d(motion_w)); - // This is for snapping while dragging existing guidelines. New guidelines, + // This is for snapping while dragging existing guidelines. New guidelines, // which are dragged off the ruler, are being snapped in sp_dt_ruler_event SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop); m.guideSnap(motion_dt, to_2geom(guide->normal_to_line)); - sp_guide_moveto(*guide, from_2geom(motion_dt), false); + switch (drag_type) { + case SP_DRAG_TRANSLATE: + { + sp_guide_moveto(*guide, guide->point_on_line + motion_dt - drag_origin, false); + break; + } + case SP_DRAG_TRANSLATE_CONSTRAINED: + { + Geom::Point pt_constr = Geom::constrain_angle(guide->point_on_line, motion_dt); + sp_guide_moveto(*guide, pt_constr, false); + break; + } + case SP_DRAG_ROTATE: + { + double angle = angle_between(drag_origin - guide->point_on_line, motion_dt - guide->point_on_line); + sp_guide_set_normal(*guide, guide->normal_to_line * Geom::Rotate(angle), false); + break; + } + case SP_DRAG_MOVE_ORIGIN: + { + Geom::Line line(guide->point_on_line, guide->angle()); + Geom::Coord t = line.nearestPoint(motion_dt); + sp_guide_moveto(*guide, line.pointAt(t), false); + break; + } + case SP_DRAG_NONE: + g_assert_not_reached(); + break; + } moved = true; desktop->set_coordinate_status(from_2geom(motion_dt)); desktop->setPosition(from_2geom(motion_dt)); @@ -258,7 +307,7 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) } break; case GDK_BUTTON_RELEASE: - if (dragging && event->button.button == 1) { + if (drag_type != SP_DRAG_NONE && event->button.button == 1) { if (moved) { Geom::Point const event_w(event->button.x, event->button.y); @@ -269,12 +318,41 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) m.guideSnap(event_dt, guide->normal_to_line); if (sp_canvas_world_pt_inside_window(item->canvas, event_w)) { - sp_guide_moveto(*guide, from_2geom(event_dt), true); + switch (drag_type) { + case SP_DRAG_TRANSLATE: + { + sp_guide_moveto(*guide, guide->point_on_line + event_dt - drag_origin, true); + break; + } + case SP_DRAG_TRANSLATE_CONSTRAINED: + { + Geom::Point pt_constr = Geom::constrain_angle(guide->point_on_line, event_dt); + sp_guide_moveto(*guide, pt_constr, true); + break; + } + case SP_DRAG_ROTATE: + { + double angle = angle_between(drag_origin - guide->point_on_line, event_dt - guide->point_on_line); + sp_guide_set_normal(*guide, guide->normal_to_line * Geom::Rotate(angle), true); + break; + } + case SP_DRAG_MOVE_ORIGIN: + { + Geom::Line line(guide->point_on_line, guide->angle()); + Geom::Coord t = line.nearestPoint(event_dt); + sp_guide_moveto(*guide, line.pointAt(t), true); + break; + } + case SP_DRAG_NONE: + g_assert_not_reached(); + break; + } sp_document_done(sp_desktop_document(desktop), SP_VERB_NONE, - _("Move guide")); + _("Move guide")); } else { /* Undo movement of any attached shapes. */ sp_guide_moveto(*guide, guide->point_on_line, false); + sp_guide_set_normal(*guide, guide->normal_to_line, false); sp_guide_remove(guide); sp_document_done(sp_desktop_document(desktop), SP_VERB_NONE, _("Delete guide")); @@ -283,7 +361,7 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) desktop->set_coordinate_status(from_2geom(event_dt)); desktop->setPosition (from_2geom(event_dt)); } - dragging = false; + drag_type = SP_DRAG_NONE; sp_canvas_set_snap_delay_active(desktop->canvas, false); sp_canvas_item_ungrab(item, event->button.time); ret=TRUE; diff --git a/src/display/guideline.cpp b/src/display/guideline.cpp index cd1a12213..5380eec45 100644 --- a/src/display/guideline.cpp +++ b/src/display/guideline.cpp @@ -6,9 +6,11 @@ * Authors: * Lauris Kaplinski * Johan Engelen + * Maximilian Albert * * Copyright (C) 2000-2002 Lauris Kaplinski * Copyright (C) 2007 Johan Engelen + * Copyright (C) 2009 Maximilian Albert * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -18,6 +20,7 @@ #include <2geom/transforms.h> #include "display-forward.h" #include "sp-canvas-util.h" +#include "sp-ctrlquadr.h" #include "guideline.h" static void sp_guideline_class_init(SPGuideLineClass *c); @@ -32,6 +35,7 @@ static double sp_guideline_point(SPCanvasItem *item, Geom::Point p, SPCanvasItem static void sp_guideline_drawline (SPCanvasBuf *buf, gint x0, gint y0, gint x1, gint y1, guint32 rgba); static SPCanvasItemClass *parent_class; +static const double radius = 7.0; GType sp_guideline_get_type() { @@ -76,10 +80,24 @@ static void sp_guideline_init(SPGuideLine *gl) gl->angle = 3.14159265358979323846/2; gl->point_on_line = Geom::Point(0,0); gl->sensitive = 0; + + gl->origin = NULL; } static void sp_guideline_destroy(GtkObject *object) { + g_return_if_fail (object != NULL); + g_return_if_fail (SP_IS_GUIDELINE (object)); + //g_return_if_fail (SP_GUIDELINE(object)->origin != NULL); + //g_return_if_fail (SP_IS_CTRLQUADR(SP_GUIDELINE(object)->origin)); + + if (SP_GUIDELINE(object)->origin != NULL && SP_IS_CTRLQUADR(SP_GUIDELINE(object)->origin)) { + gtk_object_destroy(GTK_OBJECT(SP_GUIDELINE(object)->origin)); + } else { + // FIXME: This branch shouldn't be reached (although it seems to be harmless). + //g_error("Why can it be that gl->origin is not a valid SPCtrlQuadr?\n"); + } + GTK_OBJECT_CLASS(parent_class)->destroy(object); } @@ -169,6 +187,16 @@ static void sp_guideline_render(SPCanvasItem *item, SPCanvasBuf *buf) } } +static void set_origin_coords(SPCtrlQuadr *quadr, Geom::Point const ¢er, double const r) +{ + sp_ctrlquadr_set_coords(quadr, + center + Geom::Point(-r,-r), + center + Geom::Point(-r, r), + center + Geom::Point( r, r), + center + Geom::Point( r,-r)); + +} + static void sp_guideline_update(SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags) { SPGuideLine *gl = SP_GUIDELINE(item); @@ -180,6 +208,9 @@ static void sp_guideline_update(SPCanvasItem *item, Geom::Matrix const &affine, gl->point_on_line[Geom::X] = affine[4]; gl->point_on_line[Geom::Y] = affine[5]; + set_origin_coords(gl->origin, gl->point_on_line * affine.inverse(), radius); + sp_canvas_item_request_update(SP_CANVAS_ITEM (gl->origin)); + if (gl->is_horizontal()) { sp_canvas_update_bbox (item, -1000000, (int) Inkscape::round(gl->point_on_line[Geom::Y]), 1000000, (int) Inkscape::round(gl->point_on_line[Geom::Y] + 1)); } else if (gl->is_vertical()) { @@ -208,21 +239,26 @@ static double sp_guideline_point(SPCanvasItem *item, Geom::Point p, SPCanvasItem SPCanvasItem *sp_guideline_new(SPCanvasGroup *parent, Geom::Point point_on_line, Geom::Point normal) { SPCanvasItem *item = sp_canvas_item_new(parent, SP_TYPE_GUIDELINE, NULL); + SPCanvasItem *origin = sp_canvas_item_new(parent, SP_TYPE_CTRLQUADR, NULL); SPGuideLine *gl = SP_GUIDELINE(item); + SPCtrlQuadr *cp = SP_CTRLQUADR(origin); + gl->origin = cp; normal.normalize(); gl->normal_to_line = normal; gl->angle = tan( -gl->normal_to_line[Geom::X] / gl->normal_to_line[Geom::Y]); sp_guideline_set_position(gl, point_on_line); + set_origin_coords(cp, point_on_line, radius); + return item; } void sp_guideline_set_position(SPGuideLine *gl, Geom::Point point_on_line) { - sp_canvas_item_affine_absolute(SP_CANVAS_ITEM (gl), - Geom::Matrix(Geom::Translate(point_on_line))); + sp_canvas_item_affine_absolute(SP_CANVAS_ITEM (gl), Geom::Matrix(Geom::Translate(point_on_line))); + sp_canvas_item_affine_absolute(SP_CANVAS_ITEM (gl->origin), Geom::Matrix(Geom::Translate(point_on_line))); } void sp_guideline_set_normal(SPGuideLine *gl, Geom::Point normal_to_line) @@ -236,6 +272,7 @@ void sp_guideline_set_normal(SPGuideLine *gl, Geom::Point normal_to_line) void sp_guideline_set_color(SPGuideLine *gl, unsigned int rgba) { gl->rgba = rgba; + sp_ctrlquadr_set_rgba32(gl->origin, rgba); sp_canvas_item_request_update(SP_CANVAS_ITEM(gl)); } @@ -245,6 +282,12 @@ void sp_guideline_set_sensitive(SPGuideLine *gl, int sensitive) gl->sensitive = sensitive; } +void sp_guideline_delete(SPGuideLine *gl) +{ + //gtk_object_destroy(GTK_OBJECT(gl->origin)); + gtk_object_destroy(GTK_OBJECT(gl)); +} + //########################################################## // Line rendering #define SAFE_SETPIXEL //undefine this when it is certain that setpixel is never called with invalid params diff --git a/src/display/guideline.h b/src/display/guideline.h index 451aec1da..e1d5ecab2 100644 --- a/src/display/guideline.h +++ b/src/display/guideline.h @@ -21,8 +21,11 @@ #define SP_GUIDELINE(o) (GTK_CHECK_CAST((o), SP_TYPE_GUIDELINE, SPGuideLine)) #define SP_IS_GUIDELINE(o) (GTK_CHECK_TYPE((o), SP_TYPE_GUIDELINE)) +class SPCtrlQuadr; + struct SPGuideLine { SPCanvasItem item; + SPCtrlQuadr *origin; // unlike 'item', this is only held locally guint32 rgba; @@ -48,6 +51,7 @@ void sp_guideline_set_position(SPGuideLine *gl, Geom::Point point_on_line); void sp_guideline_set_normal(SPGuideLine *gl, Geom::Point normal_to_line); void sp_guideline_set_color(SPGuideLine *gl, unsigned int rgba); void sp_guideline_set_sensitive(SPGuideLine *gl, int sensitive); +void sp_guideline_delete(SPGuideLine *gl); #endif diff --git a/src/sp-guide.cpp b/src/sp-guide.cpp index bbb85cb78..307402d1e 100644 --- a/src/sp-guide.cpp +++ b/src/sp-guide.cpp @@ -167,7 +167,7 @@ static void sp_guide_release(SPObject *object) SPGuide *guide = (SPGuide *) object; while (guide->views) { - gtk_object_destroy(GTK_OBJECT(guide->views->data)); + sp_guideline_delete(SP_GUIDELINE(guide->views->data)); guide->views = g_slist_remove(guide->views, guide->views->data); } @@ -306,7 +306,7 @@ void sp_guide_hide(SPGuide *guide, SPCanvas *canvas) for (GSList *l = guide->views; l != NULL; l = l->next) { if (canvas == SP_CANVAS_ITEM(l->data)->canvas) { - gtk_object_destroy(GTK_OBJECT(l->data)); + sp_guideline_delete(SP_GUIDELINE(l->data)); guide->views = g_slist_remove(guide->views, l->data); return; } -- 2.30.2