Code

Leave constrained angular snapping to the snap manager, instead of handling it locally
authorDiederik van Lierop <mailat-signdiedenrezidotnl>
Sun, 7 Nov 2010 20:16:45 +0000 (21:16 +0100)
committerDiederik van Lierop <mailat-signdiedenrezidotnl>
Sun, 7 Nov 2010 20:16:45 +0000 (21:16 +0100)
src/draw-context.cpp
src/gradient-drag.cpp
src/seltrans.cpp
src/snap.cpp
src/snap.h

index ca68b3f6df0fe66aa0b9beed444c584ee6a8c752..66a2309b2e949e6f1629ea8b7518d8b811cb2388 100644 (file)
@@ -42,7 +42,6 @@
 #include "sp-namedview.h"
 #include "live_effects/lpe-patternalongpath.h"
 #include "style.h"
-#include "util/mathfns.h"
 
 static void sp_draw_context_class_init(SPDrawContextClass *klass);
 static void sp_draw_context_init(SPDrawContext *dc);
@@ -477,43 +476,26 @@ void spdc_endpoint_snap_rotation(SPEventContext const *const ec, Geom::Point &p,
 {
     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
     unsigned const snaps = abs(prefs->getInt("/options/rotationsnapsperpi/value", 12));
+    SnapManager &m = SP_EVENT_CONTEXT_DESKTOP(ec)->namedview->snap_manager;
+    m.setup(SP_EVENT_CONTEXT_DESKTOP(ec));
 
-    if (snaps > 0) { // 0 means no snapping
-        // p is at an arbitrary angle. Now we should snap this angle to specific increments.
-        // For this we'll calculate the closest two angles, one at each side of the current angle
-        Geom::Line y_axis(Geom::Point(0, 0), Geom::Point(0, 1));
-        Geom::Line p_line(o, p);
-        double angle = Geom::angle_between(y_axis, p_line);
-        double angle_incr = M_PI / snaps;
-        double angle_ceil = round_to_upper_multiple_plus(angle, angle_incr);
-        double angle_floor = round_to_lower_multiple_plus(angle, angle_incr);
-        // We have to angles now. The constrained snapper will try each of them and return the closest
-        // But first we should setup the snapper
-
-        SnapManager &m = SP_EVENT_CONTEXT_DESKTOP(ec)->namedview->snap_manager;
-        m.setup(SP_EVENT_CONTEXT_DESKTOP(ec));
-        bool snap_enabled = m.snapprefs.getSnapEnabledGlobally();
-        if (state & GDK_SHIFT_MASK) {
-            // SHIFT disables all snapping, except the angular snapping. After all, the user explicitly asked for angular
-            // snapping by pressing CTRL, otherwise we wouldn't have arrived here. But although we temporarily disable
-            // the snapping here, we must still call for a constrained snap in order to apply the constraints (i.e. round
-            // to the nearest angle increment)
-            m.snapprefs.setSnapEnabledGlobally(false);
-        }
-
-        // Now do the snapping...
-        std::vector<Inkscape::Snapper::SnapConstraint> constraints;
-        constraints.push_back(Inkscape::Snapper::SnapConstraint(Geom::Line(o, angle_ceil - M_PI/2)));
-        constraints.push_back(Inkscape::Snapper::SnapConstraint(Geom::Line(o, angle_floor - M_PI/2)));
+    bool snap_enabled = m.snapprefs.getSnapEnabledGlobally();
+    if (state & GDK_SHIFT_MASK) {
+        // SHIFT disables all snapping, except the angular snapping. After all, the user explicitly asked for angular
+        // snapping by pressing CTRL, otherwise we wouldn't have arrived here. But although we temporarily disable
+        // the snapping here, we must still call for a constrained snap in order to apply the constraints (i.e. round
+        // to the nearest angle increment)
+        m.snapprefs.setSnapEnabledGlobally(false);
+    }
 
-        Inkscape::SnappedPoint sp = m.multipleConstrainedSnaps(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_NODE_HANDLE), constraints);
-        p = sp.getPoint();
+    Inkscape::SnappedPoint dummy = m.constrainedAngularSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_NODE_HANDLE), boost::optional<Geom::Point>(), o, snaps);
+    p = dummy.getPoint();
 
-        m.unSetup();
-        if (state & GDK_SHIFT_MASK) {
-            m.snapprefs.setSnapEnabledGlobally(snap_enabled); // restore the original setting
-        }
+    if (state & GDK_SHIFT_MASK) {
+        m.snapprefs.setSnapEnabledGlobally(snap_enabled); // restore the original setting
     }
+
+    m.unSetup();
 }
 
 
index 32aa7c084ee63c53eb1447ebe3036a4824c1b65f..d5ab64794cb33ad544bc56c102c6afd073273ebe 100644 (file)
@@ -568,22 +568,6 @@ SPObject *GrDraggable::getServer()
     return server;
 }
 
-static
-boost::optional<Geom::Point>
-get_snap_vector (Geom::Point p, Geom::Point o, double snap, double initial)
-{
-    double r = L2 (p - o);
-    if (r < 1e-3) {
-        return boost::optional<Geom::Point>();
-    }
-
-    double angle = atan2 (p - o);
-    // snap angle to snaps increments, starting from initial:
-    double a_snapped = initial + floor((angle - initial)/snap + 0.5) * snap;
-    // calculate the new position and subtract p to get the vector:
-    return (o + r * Geom::Point(cos(a_snapped), sin(a_snapped)) - p);
-}
-
 static void
 gr_knot_moved_handler(SPKnot *knot, Geom::Point const &ppointer, guint state, gpointer data)
 {
@@ -645,15 +629,17 @@ gr_knot_moved_handler(SPKnot *knot, Geom::Point const &ppointer, guint state, gp
         }
     }
 
-    m.setup(desktop);
     if (!((state & GDK_SHIFT_MASK) || (state & GDK_CONTROL_MASK))) {
+        m.setup(desktop);
         Inkscape::SnappedPoint s = m.freeSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_OTHER_HANDLE));
+        m.unSetup();
         if (s.getSnapped()) {
             p = s.getPoint();
             sp_knot_moveto (knot, p);
         }
     } else if (state & GDK_CONTROL_MASK) {
         SnappedConstraints sc;
+        Inkscape::SnapCandidatePoint scp = Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
         unsigned snaps = abs(prefs->getInt("/options/rotationsnapsperpi/value", 12));
         /* 0 means no snapping. */
@@ -699,40 +685,40 @@ gr_knot_moved_handler(SPKnot *knot, Geom::Point const &ppointer, guint state, gp
                 dr_snap = dragger->point_original;
             }
 
-            boost::optional<Geom::Point> snap_vector;
+            // dr_snap contains the origin of the gradient, whereas p will be the new endpoint which we will try to snap now
+            Inkscape::SnappedPoint sp;
             if (dr_snap.isFinite()) {
+                m.setup(desktop);
                 if (state & GDK_MOD1_MASK) {
                     // with Alt, snap to the original angle and its perpendiculars
-                    snap_vector = get_snap_vector (p, dr_snap, M_PI/2, Geom::atan2 (dragger->point_original - dr_snap));
+                    sp = m.constrainedAngularSnap(scp, dragger->point_original, dr_snap, 2);
                 } else {
                     // with Ctrl, snap to M_PI/snaps
-                    snap_vector = get_snap_vector (p, dr_snap, M_PI/snaps, 0);
-                }
-                if (snap_vector) {
-                    Inkscape::Snapper::SnapConstraint cl(dr_snap, p + *snap_vector - dr_snap);
-                    Inkscape::SnappedPoint s = m.constrainedSnap(Inkscape::SnapCandidatePoint(p + *snap_vector, Inkscape::SNAPSOURCE_OTHER_HANDLE), cl);
-                    if (s.getSnapped()) {
-                        s.setTransformation(s.getPoint() - p);
-                        sc.points.push_back(s);
-                    } else {
-                        Inkscape::SnappedPoint dummy(p + *snap_vector, Inkscape::SNAPSOURCE_OTHER_HANDLE, 0, Inkscape::SNAPTARGET_CONSTRAINED_ANGLE, Geom::L2(*snap_vector), 10000, true, true, false);
-                        dummy.setTransformation(*snap_vector);
-                        sc.points.push_back(dummy);
-                    }
+                    sp = m.constrainedAngularSnap(scp, boost::optional<Geom::Point>(), dr_snap, snaps);
                 }
+                m.unSetup();
+                sc.points.push_back(sp);
             }
         }
 
-        Inkscape::SnappedPoint bsp = m.findBestSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_OTHER_HANDLE), sc, true); // snap indicator will be displayed if needed
-
-        if (bsp.getSnapped()) {
-            p += bsp.getTransformation();
-            sp_knot_moveto (knot, p);
+        m.setup(desktop, false); // turn of the snap indicator temporarily
+        Inkscape::SnappedPoint bsp = m.findBestSnap(scp, sc, true);
+        m.unSetup();
+        if (!bsp.getSnapped()) {
+            // If we didn't truly snap to an object or to a grid, then we will still have to look for the
+            // closest projection onto one of the constraints. findBestSnap() will not do this for us
+            for (std::list<Inkscape::SnappedPoint>::const_iterator i = sc.points.begin(); i != sc.points.end(); i++) {
+                if (i == sc.points.begin() || (Geom::L2((*i).getPoint() - p) < Geom::L2(bsp.getPoint() - p))) {
+                    bsp.setPoint((*i).getPoint());
+                    bsp.setTarget(Inkscape::SNAPTARGET_CONSTRAINED_ANGLE);
+                }
+            }
         }
+        //p = sc.points.front().getPoint();
+        p = bsp.getPoint();
+        sp_knot_moveto (knot, p);
     }
 
-    m.unSetup();
-
     drag->keep_selection = (bool) g_list_find(drag->selected, dragger);
     bool scale_radial = (state & GDK_CONTROL_MASK) && (state & GDK_SHIFT_MASK);
 
index 9a1fdf4adcd8d8b6d45c15cf730a8a41ce2d88b5..7ea2a86c05bc356b2a6585f203a8072272ea27fa 100644 (file)
@@ -1656,6 +1656,7 @@ void Inkscape::SelTrans::_keepClosestPointOnly(std::vector<Inkscape::SnapCandida
         }
     }
 
+    closest_point.setSourceNum(-1);
     points.clear();
     points.push_back(closest_point);
 }
index 1f37536007ed5bd4a0ae10c2f35d401217753528..46bd01f4d5996e06e5d04ee735634fed1ce0567f 100644 (file)
@@ -34,6 +34,7 @@
 #include "sp-guide.h"
 #include "preferences.h"
 #include "event-context.h"
+#include "util/mathfns.h"
 using std::vector;
 
 /**
@@ -471,12 +472,8 @@ Inkscape::SnappedPoint SnapManager::multipleConstrainedSnaps(Inkscape::SnapCandi
     }
 
     if (result.getSnapped()) {
-        // only change the snap indicator if we really snapped to something
-        if (_snapindicator && _desktop) {
-            _desktop->snapindicator->set_new_snaptarget(result);
-        }
         if (snap_mouse) {
-            // We still have to apply the constraint, because so far we only tried a freeSnap
+            // If "snap_mouse" then we still have to apply the constraint, because so far we only tried a freeSnap
             Geom::Point result_closest;
             for (std::vector<Inkscape::Snapper::SnapConstraint>::const_iterator c = constraints.begin(); c != constraints.end(); c++) {
                 // Project the mouse pointer onto the constraint; In case we don't snap then we will
@@ -506,6 +503,55 @@ Inkscape::SnappedPoint SnapManager::multipleConstrainedSnaps(Inkscape::SnapCandi
     return no_snap;
 }
 
+/**
+ *  \brief Try to snap a point to something at a specific angle
+ *
+ *  When drawing a straight line or modifying a gradient, it will snap to specific angle increments
+ *  if CTRL is being pressed. This method will enforce this angular constraint (even if there is nothing
+ *  to snap to)
+ *
+ *  \param p Source point to be snapped
+ *  \param p_ref Optional original point, relative to which the angle should be calculated. If empty then
+ *  the angle will be calculated relative to the y-axis
+ *  \param snaps Number of angular increments per PI radians; E.g. if snaps = 2 then we will snap every PI/2 = 90 degrees
+ */
+
+Inkscape::SnappedPoint SnapManager::constrainedAngularSnap(Inkscape::SnapCandidatePoint const &p,
+                                                            boost::optional<Geom::Point> const &p_ref,
+                                                            Geom::Point const &o,
+                                                            unsigned const snaps) const
+{
+    Inkscape::SnappedPoint sp;
+    if (snaps > 0) { // 0 means no angular snapping
+        // p is at an arbitrary angle. Now we should snap this angle to specific increments.
+        // For this we'll calculate the closest two angles, one at each side of the current angle
+        Geom::Line y_axis(Geom::Point(0, 0), Geom::Point(0, 1));
+        Geom::Line p_line(o, p.getPoint());
+        double angle = Geom::angle_between(y_axis, p_line);
+        double angle_incr = M_PI / snaps;
+        double angle_offset = 0;
+        if (p_ref) {
+            Geom::Line p_line_ref(o, *p_ref);
+            angle_offset = Geom::angle_between(y_axis, p_line_ref);
+        }
+        double angle_ceil = round_to_upper_multiple_plus(angle, angle_incr, angle_offset);
+        double angle_floor = round_to_lower_multiple_plus(angle, angle_incr, angle_offset);
+        // We have two angles now. The constrained snapper will try each of them and return the closest
+
+        // Now do the snapping...
+        std::vector<Inkscape::Snapper::SnapConstraint> constraints;
+        constraints.push_back(Inkscape::Snapper::SnapConstraint(Geom::Line(o, angle_ceil - M_PI/2)));
+        constraints.push_back(Inkscape::Snapper::SnapConstraint(Geom::Line(o, angle_floor - M_PI/2)));
+        sp = multipleConstrainedSnaps(p, constraints); // Constraints will always be applied, even if we didn't snap
+        if (!sp.getSnapped()) { // If we haven't snapped then we only had the constraint applied;
+            sp.setTarget(Inkscape::SNAPTARGET_CONSTRAINED_ANGLE);
+        }
+    } else {
+        sp = freeSnap(p);
+    }
+    return sp;
+}
+
 /**
  *  \brief Try to snap a point of a guide to another guide or to a node
  *
index 0f27017a5619c403703f3a592120808da4cc9ce0..3c9af7d515ab84011a04af3ef0381087ca36547e 100644 (file)
@@ -138,6 +138,11 @@ public:
                                                         std::vector<Inkscape::Snapper::SnapConstraint> const &constraints,
                                                         Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const;
 
+    Inkscape::SnappedPoint constrainedAngularSnap(Inkscape::SnapCandidatePoint const &p,
+                                                    boost::optional<Geom::Point> const &p_ref,
+                                                    Geom::Point const &o,
+                                                    unsigned const snaps) const;
+
     void guideFreeSnap(Geom::Point &p, Geom::Point const &guide_normal, SPGuideDragType drag_type) const;
     void guideConstrainedSnap(Geom::Point &p, SPGuide const &guideline) const;