Code

display guide anchor on canvas; anchor and angle can be edited by mouse
authorcilix42 <cilix42@users.sourceforge.net>
Mon, 26 Jan 2009 14:41:05 +0000 (14:41 +0000)
committercilix42 <cilix42@users.sourceforge.net>
Mon, 26 Jan 2009 14:41:05 +0000 (14:41 +0000)
src/2geom/point.cpp
src/2geom/point.h
src/desktop-events.cpp
src/display/guideline.cpp
src/display/guideline.h
src/sp-guide.cpp

index 5dd7b52f295126b468f76d49e239202d561566cb..b8a858f8d48d6f9910441a93100cef69a9a8a743 100644 (file)
@@ -2,7 +2,7 @@
 #include <assert.h>
 #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
 
 /*
index d89b53f83c632425513c6b5aa30f690eed75a12f..39538c832686a60b7469fd709ef147d085f558f7 100644 (file)
@@ -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 */
index fbbbfee197d3e045adf249ade89da14a63c562ec..6cf1dde1ab10f248e419bf755c7a91c5a03819fc 100644 (file)
@@ -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<SPDesktop*>(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;
index cd1a122131f33dbb1a3ea1c34513878c4614dd2a..5380eec45f849e4aa8d99a015a406218c495a3fe 100644 (file)
@@ -6,9 +6,11 @@
  * Authors:
  *   Lauris Kaplinski <lauris@kaplinski.com>
  *   Johan Engelen
+ *   Maximilian Albert <maximilian.albert@gmail.com>
  *
  * 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 &center, 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
index 451aec1da24d5ca4db4a960618b7b3e35784917a..e1d5ecab2ec387223134d8bde0f02488e9f003f0 100644 (file)
 #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
 
index bbb85cb781656e70e5e2570a36443b2b78e40bb3..307402d1e8b820c6699a866d97f039e2b328a779 100644 (file)
@@ -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;
         }