Code

- Snap while rotating an object using the selector tool
authorDiederik van Lierop <mailat-signdiedenrezidotnl>
Mon, 12 Jul 2010 05:51:13 +0000 (07:51 +0200)
committerDiederik van Lierop <mailat-signdiedenrezidotnl>
Mon, 12 Jul 2010 05:51:13 +0000 (07:51 +0200)
- Rename the ConstraintLine class to SnapConstraint
- Move some duplicated code to 2geom

27 files changed:
src/2geom/circle.cpp
src/2geom/circle.h
src/context-fns.cpp
src/display/canvas-axonomgrid.cpp
src/display/canvas-axonomgrid.h
src/display/canvas-grid.cpp
src/display/canvas-grid.h
src/display/snap-indicator.cpp
src/draw-context.cpp
src/gradient-drag.cpp
src/guide-snapper.cpp
src/guide-snapper.h
src/knot-holder-entity.cpp
src/knot-holder-entity.h
src/line-snapper.cpp
src/line-snapper.h
src/live_effects/lpe-circle_3pts.cpp
src/live_effects/lpe-circle_with_radius.cpp
src/object-edit.cpp
src/object-snapper.cpp
src/object-snapper.h
src/pen-context.cpp
src/seltrans.cpp
src/snap.cpp
src/snap.h
src/snapper.h
src/ui/tool/node.cpp

index c3cea0ae78f5edf9a20f675b04351dd10a66cfae..00b91de129cf58aac41e7adcd725e8b980f7f2cc 100644 (file)
@@ -97,6 +97,23 @@ Circle::arc(Point const& initial, Point const& inner, Point const& final,
     return e.arc(initial, inner, final, _svg_compliant);
 }
 
+void
+Circle::getPath(std::vector<Path> &path_out) {
+    Path pb;
+
+    D2<SBasis> B;
+    Linear bo = Linear(0, 2 * M_PI);
+
+    B[0] = cos(bo,4);
+    B[1] = sin(bo,4);
+
+    B = B * m_ray + m_centre;
+
+    pb.append(SBasisCurve(B));
+
+    path_out.push_back(pb);
+}
+
 
 }  // end namespace Geom
 
index 27d4fcc3fe4942969e2d05c5f212d3c3bf5bb576..c346b8c8fb0c7c9f4c7485ec3e24271196c91bf9 100644 (file)
@@ -38,7 +38,7 @@
 
 #include <2geom/point.h>
 #include <2geom/exception.h>
-
+#include <2geom/path.h>
 
 namespace Geom
 {
@@ -56,6 +56,11 @@ class Circle
     {
     }
 
+    Circle(Point center, double r)
+        : m_centre(center), m_ray(r)
+    {
+    }
+
     Circle(double A, double B, double C, double D)
     {
         set(A, B, C, D);
@@ -86,6 +91,9 @@ class Circle
     arc(Point const& initial, Point const& inner, Point const& final,
         bool _svg_compliant = true);
 
+    void
+    getPath(std::vector<Path> &path_out);
+
     Point center() const
     {
         return m_centre;
index 0ff7bd12097c557f65426b551e0197fc5c983f11..b22cd488d79a6a86d20748f1459b511954b9c335 100644 (file)
@@ -132,11 +132,11 @@ Geom::Rect Inkscape::snap_rectangular_box(SPDesktop const *desktop, SPItem *item
 
             /* Try to snap p[0] (the opposite corner) along the constraint vector */
             s[0] = m.constrainedSnap(Inkscape::SnapCandidatePoint(p[0], Inkscape::SNAPSOURCE_NODE_HANDLE),
-                                     Inkscape::Snapper::ConstraintLine(p[0] - p[1]));
+                                     Inkscape::Snapper::SnapConstraint(p[0] - p[1]));
 
             /* Try to snap p[1] (the dragged corner) along the constraint vector */
             s[1] = m.constrainedSnap(Inkscape::SnapCandidatePoint(p[1], Inkscape::SNAPSOURCE_NODE_HANDLE),
-                                     Inkscape::Snapper::ConstraintLine(p[1] - p[0]));
+                                     Inkscape::Snapper::SnapConstraint(p[1] - p[0]));
 
             /* Choose the best snap and update points accordingly */
             if (s[0].getSnapDistance() < s[1].getSnapDistance()) {
@@ -157,7 +157,7 @@ Geom::Rect Inkscape::snap_rectangular_box(SPDesktop const *desktop, SPItem *item
             /* Our origin is the opposite corner.  Snap the drag point along the constraint vector */
             p[0] = center;
             snappoint = m.constrainedSnap(Inkscape::SnapCandidatePoint(p[1], Inkscape::SNAPSOURCE_NODE_HANDLE),
-                                          Inkscape::Snapper::ConstraintLine(p[1] - p[0]));
+                                          Inkscape::Snapper::SnapConstraint(p[1] - p[0]));
             if (snappoint.getSnapped()) {
                 p[1] = snappoint.getPoint();
             }
index 37469fa737bdede42dd9c7a3b564ef53892ce5ea..d17689b063050dc5f471bcd3ebed4e75aae66eab 100644 (file)
@@ -794,9 +794,10 @@ void CanvasAxonomGridSnapper::_addSnappedLine(SnappedConstraints &sc, Geom::Poin
     sc.grid_lines.push_back(dummy);
 }
 
-void CanvasAxonomGridSnapper::_addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const
+void CanvasAxonomGridSnapper::_addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap, Geom::Coord angle) const
 {
     SnappedPoint dummy = SnappedPoint(snapped_point, source, source_num, Inkscape::SNAPTARGET_GRID, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), constrained_snap, true);
+    dummy.setTransformation(Geom::Point(angle, angle)); // Store the rotation (in radians), needed in case of snapping while rotating
     sc.points.push_back(dummy);
 }
 
index 58185e2e6a4bbf940666689c12e049172d18b551..4a9820792b8bc1e72bb07d4793f75d900014c382 100644 (file)
@@ -79,7 +79,7 @@ public:
 private:
     LineList _getSnapLines(Geom::Point const &p) const;
     void _addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, const Geom::Point point_on_line) const;
-    void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const;
+    void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap, Geom::Coord angle) const;
 
     CanvasAxonomGrid *grid;
 };
index a79a6b610c1bc947b0ede5f7bf2c6d4007d86dec..fe1e9282458ba34644583b70282e62dc8b1f403a 100644 (file)
@@ -1030,9 +1030,10 @@ void CanvasXYGridSnapper::_addSnappedLine(SnappedConstraints &sc, Geom::Point co
     sc.grid_lines.push_back(dummy);
 }
 
-void CanvasXYGridSnapper::_addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const
+void CanvasXYGridSnapper::_addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap, Geom::Coord angle) const
 {
     SnappedPoint dummy = SnappedPoint(snapped_point, source, source_num, Inkscape::SNAPTARGET_GRID, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), constrained_snap, true);
+    dummy.setTransformation(Geom::Point(angle, angle)); // Store the rotation (in radians), needed in case of snapping while rotating
     sc.points.push_back(dummy);
 }
 
index a11d77d1dc86d9eb5372c10e6dc50ddfad6c7426..0aedb02a03b85d9c08fd1fffef24e39551c9a899 100644 (file)
@@ -167,7 +167,7 @@ public:
 private:
     LineList _getSnapLines(Geom::Point const &p) const;
     void _addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance,  SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, const Geom::Point point_on_line) const;
-    void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const;
+    void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap, Geom::Coord angle) const;
     CanvasXYGrid *grid;
 };
 
index fe5bd0371927471d2d068a39ea66fe4b18caf769..0409e64b11ad9bd644abc44b8c722f53c6f0d2c4 100644 (file)
@@ -51,7 +51,6 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const &p, bool pre_snap
     g_assert(_desktop != NULL);
 
     if (!p.getSnapped()) {
-        g_warning("No snapping took place, so no snap target will be displayed");
         return; // If we haven't snapped, then it is of no use to draw a snapindicator
     }
 
index 3049f3a6a99a1e910738b71ad8adabd29105b5c3..a531b88d13bc69861932501316fc8beb59abe212 100644 (file)
@@ -511,7 +511,7 @@ void spdc_endpoint_snap_rotation(SPEventContext const *const ec, Geom::Point &p,
             /* Snap it along best vector */
             SnapManager &m = SP_EVENT_CONTEXT_DESKTOP(ec)->namedview->snap_manager;
             m.setup(SP_EVENT_CONTEXT_DESKTOP(ec));
-            m.constrainedSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE, Inkscape::Snapper::ConstraintLine(best));
+            m.constrainedSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE, Inkscape::Snapper::SnapConstraint(best));
         }
     }
 }
index 25309dd614f650d5aa94b129eb623412db25e782..1492e900851def296c83a7e302040a6e55dfdb49 100644 (file)
@@ -688,7 +688,7 @@ gr_knot_moved_handler(SPKnot *knot, Geom::Point const &ppointer, guint state, gp
                     snap_vector = get_snap_vector (p, dr_snap, M_PI/snaps, 0);
                 }
                 if (snap_vector) {
-                    Inkscape::Snapper::ConstraintLine cl(dr_snap, p + *snap_vector - dr_snap);
+                    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);
@@ -838,7 +838,7 @@ gr_knot_moved_midpoint_handler(SPKnot */*knot*/, Geom::Point const &ppointer, gu
     } else {
         p = snap_vector_midpoint (p, low_lim, high_lim, 0);
         if (!(state & GDK_SHIFT_MASK)) {
-            Inkscape::Snapper::ConstraintLine cl(low_lim, high_lim - low_lim);
+            Inkscape::Snapper::SnapConstraint cl(low_lim, high_lim - low_lim);
             SPDesktop *desktop = dragger->parent->desktop;
             SnapManager &m = desktop->namedview->snap_manager;
             m.setup(desktop);
index 4f70521e0904a5fd9a4f9e3ee823be462d17266b..aad4502ca280806c108b4595fb6e470c45d9e7ff 100644 (file)
@@ -81,9 +81,10 @@ void Inkscape::GuideSnapper::_addSnappedLinesOrigin(SnappedConstraints &sc, Geom
 }
 
 
-void Inkscape::GuideSnapper::_addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const
+void Inkscape::GuideSnapper::_addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap, Geom::Coord angle) const
 {
     SnappedPoint dummy = SnappedPoint(snapped_point, source, source_num, Inkscape::SNAPTARGET_GUIDE, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), constrained_snap, true);
+    dummy.setTransformation(Geom::Point(angle, angle)); // Store the rotation (in radians), needed in case of snapping while rotating
     sc.points.push_back(dummy);
 }
 
index 5de1b56a4ae20474a183a8c17de683cf2bec2b35..4e5e5d1d14949436badec82fc15fa81876b991ba 100644 (file)
@@ -36,7 +36,7 @@ private:
     LineList _getSnapLines(Geom::Point const &p) const;
     void _addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance,  SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, Geom::Point const point_on_line) const;
     void _addSnappedLinesOrigin(SnappedConstraints &sc, Geom::Point const origin, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const;
-    void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const;
+    void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap, Geom::Coord angle) const;
 };
 
 }
index 2d0d5eb029f65b9ca1b1e1fc84031c0e277023a1..be61125c4295e9d8ba6b451db657ec6b208e166c 100644 (file)
@@ -101,7 +101,7 @@ KnotHolderEntity::snap_knot_position(Geom::Point const &p)
 }
 
 Geom::Point
-KnotHolderEntity::snap_knot_position_constrained(Geom::Point const &p, Inkscape::Snapper::ConstraintLine const &constraint)
+KnotHolderEntity::snap_knot_position_constrained(Geom::Point const &p, Inkscape::Snapper::SnapConstraint const &constraint)
 {
     Geom::Matrix const i2d (sp_item_i2d_affine(item));
     Geom::Point s = p * i2d;
@@ -123,7 +123,7 @@ KnotHolderEntity::snap_knot_position_constrained(Geom::Point const &p, Inkscape:
     } else {
         // constrainedSnap() will first project the point p onto the constraint line and then try to snap along that line.
         // This way the constraint is already enforced, no need to worry about that later on
-        Inkscape::Snapper::ConstraintLine transformed_constraint = Inkscape::Snapper::ConstraintLine(constraint.getPoint() * i2d, (constraint.getPoint() + constraint.getDirection()) * i2d - constraint.getPoint() * i2d);
+        Inkscape::Snapper::SnapConstraint transformed_constraint = Inkscape::Snapper::SnapConstraint(constraint.getPoint() * i2d, (constraint.getPoint() + constraint.getDirection()) * i2d - constraint.getPoint() * i2d);
         m.constrainedSnapReturnByRef(s, Inkscape::SNAPSOURCE_NODE_HANDLE, transformed_constraint);
     }
 
index c8fd29ddf3dc15fc97e0c19b877eb5a93f30cfad..aba93798a4f5386ecc16e6450045ea7ad1d8476d 100644 (file)
@@ -58,7 +58,7 @@ public:
 
 //private:
     Geom::Point snap_knot_position(Geom::Point const &p);
-    Geom::Point snap_knot_position_constrained(Geom::Point const &p, Inkscape::Snapper::ConstraintLine const &constraint);
+    Geom::Point snap_knot_position_constrained(Geom::Point const &p, Inkscape::Snapper::SnapConstraint const &constraint);
 
     SPKnot *knot;
     SPItem *item;
index fc40643c8f135afc605cadc7c33640e8c666855b..5ceece66bc4d6832e6737f70ab0f5f239ff39b5b 100644 (file)
@@ -63,7 +63,7 @@ void Inkscape::LineSnapper::freeSnap(SnappedConstraints &sc,
 void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc,
                                                Inkscape::SnapCandidatePoint const &p,
                                                Geom::OptRect const &/*bbox_to_snap*/,
-                                               ConstraintLine const &c,
+                                               SnapConstraint const &c,
                                                std::vector<SPItem const *> const */*it*/) const
 
 {
@@ -75,20 +75,46 @@ void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc,
     const LineList lines = _getSnapLines(p.getPoint());
 
     for (LineList::const_iterator i = lines.begin(); i != lines.end(); i++) {
-        if (Geom::L2(c.getDirection()) > 0) { // Can't do a constrained snap without a constraint
-            // constraint line
-            Geom::Point const point_on_line = c.hasPoint() ? c.getPoint() : p.getPoint();
-            Geom::Line line1(point_on_line, point_on_line + c.getDirection());
-
-            // grid/guide line
-            Geom::Point const p1 = i->second; // point at guide/grid line
-            Geom::Point const p2 = p1 + Geom::rot90(i->first); // 2nd point at guide/grid line
-            Geom::Line line2(p1, p2);
+        Geom::Point const point_on_line = c.hasPoint() ? c.getPoint() : p.getPoint();
+        Geom::Line gridguide_line(i->second, i->second + Geom::rot90(i->first));
 
+        if (c.isCircular()) {
+            // Find the intersections between the line and the circular constraint
+            // First, project the origin of the circle onto the line
+            Geom::Point const origin = c.getPoint();
+            Geom::Point const p_proj = Geom::projection(origin, gridguide_line);
+            Geom::Point v_orig = c.getDirection(); // vector from the origin to the original (untransformed) point
+            Geom::Point v_proj = p_proj - origin;
+            Geom::Coord dist = Geom::L2(v_proj); // distance from circle origin to constraint line
+            Geom::Coord radius = c.getRadius();
+            Geom::Coord radians = NR_HUGE;
+            if (dist == radius) {
+                // Only one point of intersection;
+                // Calculate the rotation in radians...
+                radians = atan2(Geom::dot(Geom::rot90(v_orig), v_proj), Geom::dot(v_orig, v_proj));
+                _addSnappedPoint(sc, p_proj, Geom::L2(p.getPoint() - p_proj), p.getSourceType(), p.getSourceNum(), true, radians);
+            } else if (dist < radius) {
+                // Two points of intersection, symmetrical with respect to the projected point
+                // Calculate half the length of the linesegment between the two points of intersection
+                Geom::Coord l = sqrt(radius*radius - dist*dist);
+                Geom::Coord d = Geom::L2(gridguide_line.versor()); // length of versor, needed to normalize the versor
+                if (d > 0) {
+                    Geom::Point v = l*gridguide_line.versor()/d;
+                    v_proj = p_proj + v - origin;
+                    radians = atan2(Geom::dot(Geom::rot90(v_orig), v_proj), Geom::dot(v_orig, v_proj));
+                    _addSnappedPoint(sc, p_proj + v, Geom::L2(p.getPoint() - (p_proj + v)), p.getSourceType(), p.getSourceNum(), true, radians);
+                    v_proj = p_proj - v - origin;
+                    radians = atan2(Geom::dot(Geom::rot90(v_orig), v_proj), Geom::dot(v_orig, v_proj));
+                    _addSnappedPoint(sc, p_proj - v, Geom::L2(p.getPoint() - (p_proj - v)), p.getSourceType(), p.getSourceNum(), true, radians);
+                }
+            }
+        } else {
+            // Find the intersections between the line and the linear constraint
+            Geom::Line constraint_line(point_on_line, point_on_line + c.getDirection());
             Geom::OptCrossing inters = Geom::OptCrossing(); // empty by default
             try
             {
-                inters = Geom::intersection(line1, line2);
+                inters = Geom::intersection(constraint_line, gridguide_line);
             }
             catch (Geom::InfiniteSolutions e)
             {
@@ -97,23 +123,14 @@ void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc,
             }
 
             if (inters) {
-                Geom::Point t = line1.pointAt((*inters).ta);
+                Geom::Point t = constraint_line.pointAt((*inters).ta);
                 const Geom::Coord dist = Geom::L2(t - p.getPoint());
                 if (dist < getSnapperTolerance()) {
                     // When doing a constrained snap, we're already at an intersection.
                     // This snappoint is therefore fully constrained, so there's no need
                     // to look for additional intersections; just return the snapped point
                     // and forget about the line
-                    _addSnappedPoint(sc, t, dist, p.getSourceType(), p.getSourceNum(), true);
-                    // For any line that's within range, we will also look at it's "point on line" p1. For guides
-                    // this point coincides with its origin; for grids this is of no use, but we cannot
-                    // discern between grids and guides here
-                    Geom::Coord const dist_p1 = Geom::L2(p1 - p.getPoint());
-                    if (dist_p1 < getSnapperTolerance()) {
-                        _addSnappedLinesOrigin(sc, p1, dist_p1, p.getSourceType(), p.getSourceNum(), true);
-                        // Only relevant for guides; grids don't have an origin per line
-                        // Therefore _addSnappedLinesOrigin() will only be implemented for guides
-                    }
+                    _addSnappedPoint(sc, t, dist, p.getSourceType(), p.getSourceNum(), true, 1);
                 }
             }
         }
index 1aa3526cc12f7c993fbdf94c48bc95faf8888b1a..403c8cbba77154b8760c01b206a9ea90450f20d7 100644 (file)
@@ -34,7 +34,7 @@ public:
   void constrainedSnap(SnappedConstraints &sc,
                           Inkscape::SnapCandidatePoint const &p,
                           Geom::OptRect const &bbox_to_snap,
-                          ConstraintLine const &c,
+                          SnapConstraint const &c,
                           std::vector<SPItem const *> const *it) const;
 
 protected:
@@ -54,7 +54,7 @@ private:
   // Will only be implemented for guide lines, because grid lines don't have an origin
   virtual void _addSnappedLinesOrigin(SnappedConstraints &sc, Geom::Point const origin, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const;
 
-  virtual void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const = 0;
+  virtual void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap, Geom::Coord angle) const = 0;
 };
 
 }
index d600ef046c3b09901c8dbc68daa3de5c790b8b22..951a3b7c82b10139f38a070229f363cd4b32e2e7 100644 (file)
@@ -17,6 +17,7 @@
 
 // You might need to include other 2geom files. You can add them here:
 #include <2geom/path.h>
+#include <2geom/circle.h>
 
 namespace Inkscape {
 namespace LivePathEffect {
@@ -30,24 +31,6 @@ LPECircle3Pts::~LPECircle3Pts()
 {
 }
 
-static void _circle(Geom::Point center, double radius, std::vector<Geom::Path> &path_out) {
-    using namespace Geom;
-
-    Geom::Path pb;
-
-    D2<SBasis> B;
-    Linear bo = Linear(0, 2 * M_PI);
-
-    B[0] = cos(bo,4);
-    B[1] = sin(bo,4);
-
-    B = B * radius + center;
-
-    pb.append(SBasisCurve(B));
-
-    path_out.push_back(pb);
-}
-
 static void _circle3(Geom::Point const &A, Geom::Point const &B, Geom::Point const &C, std::vector<Geom::Path> &path_out) {
     using namespace Geom;
 
@@ -64,7 +47,8 @@ static void _circle3(Geom::Point const &A, Geom::Point const &B, Geom::Point con
     Point M = D + v * lambda;
     double radius = L2(M - A);
 
-    _circle(M, radius, path_out);
+    Geom::Circle c(M, radius);
+    c.getPath(path_out);
 }
 
 std::vector<Geom::Path>
index 574a9c004b34b2348b3523b379f46702f3f578b1..71611e18bdc6613fbc0a1356a2b2b09ed4200ce5 100644 (file)
@@ -18,6 +18,7 @@
 #include <2geom/sbasis.h>
 #include <2geom/bezier-to-sbasis.h>
 #include <2geom/d2.h>
+#include <2geom/circle.h>
 
 using namespace Geom;
 
@@ -38,22 +39,6 @@ LPECircleWithRadius::~LPECircleWithRadius()
 
 }
 
-void _circle(Geom::Point center, double radius, std::vector<Geom::Path> &path_out) {
-    Geom::Path pb;
-
-    D2<SBasis> B;
-    Linear bo = Linear(0, 2 * M_PI);
-
-    B[0] = cos(bo,4);
-    B[1] = sin(bo,4);
-
-    B = B * radius + center;
-
-    pb.append(SBasisCurve(B));
-
-    path_out.push_back(pb);
-}
-
 std::vector<Geom::Path>
 LPECircleWithRadius::doEffect_path (std::vector<Geom::Path> const & path_in)
 {
@@ -64,7 +49,8 @@ LPECircleWithRadius::doEffect_path (std::vector<Geom::Path> const & path_in)
 
     double radius = Geom::L2(pt - center);
 
-    _circle(center, radius, path_out);
+    Geom::Circle c(center, radius);
+    c.getPath(path_out);
 
     return path_out;
 }
index 1d81aa7f5a86ef2854d5b9771d52c117de8969a0..83b01013c4d16323e4da50f2634f12d9bafc56c9 100644 (file)
@@ -141,7 +141,7 @@ RectKnotHolderEntityRX::knot_set(Geom::Point const &p, Geom::Point const &/*orig
     //In general we cannot just snap this radius to an arbitrary point, as we have only a single
     //degree of freedom. For snapping to an arbitrary point we need two DOF. If we're going to snap
     //the radius then we should have a constrained snap. snap_knot_position() is unconstrained
-    Geom::Point const s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed), Geom::Point(-1, 0)));
+    Geom::Point const s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed), Geom::Point(-1, 0)));
 
     if (state & GDK_CONTROL_MASK) {
         gdouble temp = MIN(rect->height.computed, rect->width.computed) / 2.0;
@@ -191,7 +191,7 @@ RectKnotHolderEntityRY::knot_set(Geom::Point const &p, Geom::Point const &/*orig
     //In general we cannot just snap this radius to an arbitrary point, as we have only a single
     //degree of freedom. For snapping to an arbitrary point we need two DOF. If we're going to snap
     //the radius then we should have a constrained snap. snap_knot_position() is unconstrained
-    Geom::Point const s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed), Geom::Point(0, 1)));
+    Geom::Point const s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed), Geom::Point(0, 1)));
 
     if (state & GDK_CONTROL_MASK) { // When holding control then rx will be kept equal to ry,
                                     // resulting in a perfect circle (and not an ellipse)
@@ -280,13 +280,13 @@ RectKnotHolderEntityWH::set_internal(Geom::Point const &p, Geom::Point const &or
             // snap to horizontal or diagonal
             if (minx != 0 && fabs(miny/minx) > 0.5 * 1/ratio && (SGN(minx) == SGN(miny))) {
                 // closer to the diagonal and in same-sign quarters, change both using ratio
-                s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-ratio, -1)));
+                s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-ratio, -1)));
                 minx = s[Geom::X] - origin[Geom::X];
                 miny = s[Geom::Y] - origin[Geom::Y];
                 rect->height.computed = MAX(h_orig + minx / ratio, 0);
             } else {
                 // closer to the horizontal, change only width, height is h_orig
-                s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-1, 0)));
+                s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-1, 0)));
                 minx = s[Geom::X] - origin[Geom::X];
                 miny = s[Geom::Y] - origin[Geom::Y];
                 rect->height.computed = MAX(h_orig, 0);
@@ -297,13 +297,13 @@ RectKnotHolderEntityWH::set_internal(Geom::Point const &p, Geom::Point const &or
             // snap to vertical or diagonal
             if (miny != 0 && fabs(minx/miny) > 0.5 * ratio && (SGN(minx) == SGN(miny))) {
                 // closer to the diagonal and in same-sign quarters, change both using ratio
-                s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-ratio, -1)));
+                s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-ratio, -1)));
                 minx = s[Geom::X] - origin[Geom::X];
                 miny = s[Geom::Y] - origin[Geom::Y];
                 rect->width.computed = MAX(w_orig + miny * ratio, 0);
             } else {
                 // closer to the vertical, change only height, width is w_orig
-                s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(0, -1)));
+                s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(0, -1)));
                 minx = s[Geom::X] - origin[Geom::X];
                 miny = s[Geom::Y] - origin[Geom::Y];
                 rect->width.computed = MAX(w_orig, 0);
@@ -370,14 +370,14 @@ RectKnotHolderEntityXY::knot_set(Geom::Point const &p, Geom::Point const &origin
             // snap to horizontal or diagonal
             if (minx != 0 && fabs(miny/minx) > 0.5 * 1/ratio && (SGN(minx) == SGN(miny))) {
                 // closer to the diagonal and in same-sign quarters, change both using ratio
-                s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-ratio, -1)));
+                s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-ratio, -1)));
                 minx = s[Geom::X] - origin[Geom::X];
                 miny = s[Geom::Y] - origin[Geom::Y];
                 rect->y.computed = MIN(origin[Geom::Y] + minx / ratio, opposite_y);
                 rect->height.computed = MAX(h_orig - minx / ratio, 0);
             } else {
                 // closer to the horizontal, change only width, height is h_orig
-                s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-1, 0)));
+                s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-1, 0)));
                 minx = s[Geom::X] - origin[Geom::X];
                 miny = s[Geom::Y] - origin[Geom::Y];
                 rect->y.computed = MIN(origin[Geom::Y], opposite_y);
@@ -389,14 +389,14 @@ RectKnotHolderEntityXY::knot_set(Geom::Point const &p, Geom::Point const &origin
             // snap to vertical or diagonal
             if (miny != 0 && fabs(minx/miny) > 0.5 *ratio && (SGN(minx) == SGN(miny))) {
                 // closer to the diagonal and in same-sign quarters, change both using ratio
-                s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-ratio, -1)));
+                s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-ratio, -1)));
                 minx = s[Geom::X] - origin[Geom::X];
                 miny = s[Geom::Y] - origin[Geom::Y];
                 rect->x.computed = MIN(origin[Geom::X] + miny * ratio, opposite_x);
                 rect->width.computed = MAX(w_orig - miny * ratio, 0);
             } else {
                 // closer to the vertical, change only height, width is w_orig
-                s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(0, -1)));
+                s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(0, -1)));
                 minx = s[Geom::X] - origin[Geom::X];
                 miny = s[Geom::Y] - origin[Geom::Y];
                 rect->x.computed = MIN(origin[Geom::X], opposite_x);
index 983a6fedee8eda2423f46ab49833148d6eebdab4..f96335c4a63df72e7ba6f8fdcc262375a84766bf 100644 (file)
@@ -16,6 +16,7 @@
 #include <2geom/point.h>
 #include <2geom/rect.h>
 #include <2geom/line.h>
+#include <2geom/circle.h>
 #include "document.h"
 #include "sp-namedview.h"
 #include "sp-image.h"
@@ -522,7 +523,7 @@ bool Inkscape::ObjectSnapper::isUnselectedNode(Geom::Point const &point, std::ve
 
 void Inkscape::ObjectSnapper::_snapPathsConstrained(SnappedConstraints &sc,
                                      Inkscape::SnapCandidatePoint const &p,
-                                     ConstraintLine const &c) const
+                                     SnapConstraint const &c) const
 {
 
     _collectPaths(p, p.getSourceNum() == 0);
@@ -537,36 +538,54 @@ void Inkscape::ObjectSnapper::_snapPathsConstrained(SnappedConstraints &sc,
         direction_vector = Geom::unit_vector(direction_vector);
     }
 
-    // The intersection point of the constraint line with any path,
-    // must lie within two points on the constraintline: p_min_on_cl and p_max_on_cl
-    // The distance between those points is twice the snapping tolerance
+    // The intersection point of the constraint line with any path, must lie within two points on the
+    // SnapConstraint: p_min_on_cl and p_max_on_cl. The distance between those points is twice the snapping tolerance
     Geom::Point const p_proj_on_cl = p.getPoint(); // projection has already been taken care of in constrainedSnap in the snapmanager;
     Geom::Point const p_min_on_cl = _snapmanager->getDesktop()->dt2doc(p_proj_on_cl - getSnapperTolerance() * direction_vector);
     Geom::Point const p_max_on_cl = _snapmanager->getDesktop()->dt2doc(p_proj_on_cl + getSnapperTolerance() * direction_vector);
+    Geom::Coord tolerance = getSnapperTolerance();
 
-    Geom::Path cl;
-    std::vector<Geom::Path> clv;
-    cl.start(p_min_on_cl);
-    cl.appendNew<Geom::LineSegment>(p_max_on_cl);
-    clv.push_back(cl);
+    // PS: Because the paths we're about to snap to are all expressed relative to document coordinate system, we will have
+    // to convert the snapper coordinates from the desktop coordinates to document coordinates
+
+    std::vector<Geom::Path> constraint_path;
+    if (c.isCircular()) {
+        Geom::Circle constraint_circle(_snapmanager->getDesktop()->dt2doc(c.getPoint()), c.getRadius());
+        constraint_circle.getPath(constraint_path);
+    } else {
+        Geom::Path constraint_line;
+        constraint_line.start(p_min_on_cl);
+        constraint_line.appendNew<Geom::LineSegment>(p_max_on_cl);
+        constraint_path.push_back(constraint_line);
+    }
 
     for (std::vector<Inkscape::SnapCandidatePath >::const_iterator k = _paths_to_snap_to->begin(); k != _paths_to_snap_to->end(); k++) {
         if (k->path_vector) {
-            Geom::CrossingSet cs = Geom::crossings(clv, *(k->path_vector));
-            if (cs.size() > 0) {
-                // We need only the first element of cs, because cl is only a single straight linesegment
-                // This first element contains a vector filled with crossings of cl with k->first
-                for (std::vector<Geom::Crossing>::const_iterator m = cs[0].begin(); m != cs[0].end(); m++) {
-                    if ((*m).ta >= 0 && (*m).ta <= 1 ) {
-                        // Reconstruct the point of intersection
-                        Geom::Point p_inters = p_min_on_cl + ((*m).ta) * (p_max_on_cl - p_min_on_cl);
-                        // When it's within snapping range, then return it
-                        // (within snapping range == between p_min_on_cl and p_max_on_cl == 0 < ta < 1)
-                        Geom::Coord dist = Geom::L2(_snapmanager->getDesktop()->dt2doc(p_proj_on_cl) - p_inters);
-                        SnappedPoint s(_snapmanager->getDesktop()->doc2dt(p_inters), p.getSourceType(), p.getSourceNum(), k->target_type, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), true, k->target_bbox);
+            Geom::CrossingSet cs = Geom::crossings(constraint_path, *(k->path_vector));
+            unsigned int index = 0;
+            for (Geom::CrossingSet::const_iterator i = cs.begin(); i != cs.end(); i++) {
+                if (index >= constraint_path.size()) {
+                    break;
+                }
+                for (Geom::Crossings::const_iterator m = (*i).begin(); m != (*i).end(); m++) {
+                    //std::cout << "ta = " << (*m).ta << " | tb = " << (*m).tb << std::endl;
+                    // Reconstruct the point of intersection
+                    Geom::Point p_inters = constraint_path[index].pointAt((*m).ta);
+                    // .. and convert it to desktop coordinates
+                    p_inters = _snapmanager->getDesktop()->doc2dt(p_inters);
+                    Geom::Coord dist = Geom::L2(p_proj_on_cl - p_inters);
+                    SnappedPoint s = SnappedPoint(p_inters, p.getSourceType(), p.getSourceNum(), k->target_type, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), true, k->target_bbox);;
+                    if (dist <= tolerance) { // If the intersection is within snapping range, then we might snap to it
+                        if (c.isCircular()) {
+                            Geom::Point v_orig = c.getDirection(); // vector from the origin to the original (untransformed) point
+                            Geom::Point v_inters = p_inters - c.getPoint();
+                            Geom::Coord radians = atan2(Geom::dot(Geom::rot90(v_orig), v_inters), Geom::dot(v_orig, v_inters));
+                            s.setTransformation(Geom::Point(radians, radians));
+                        }
                         sc.points.push_back(s);
                     }
                 }
+                index++;
             }
         }
     }
@@ -622,7 +641,7 @@ void Inkscape::ObjectSnapper::freeSnap(SnappedConstraints &sc,
 void Inkscape::ObjectSnapper::constrainedSnap( SnappedConstraints &sc,
                                                   Inkscape::SnapCandidatePoint const &p,
                                                   Geom::OptRect const &bbox_to_snap,
-                                                  ConstraintLine const &c,
+                                                  SnapConstraint const &c,
                                                   std::vector<SPItem const *> const *it) const
 {
     if (_snap_enabled == false || _snapmanager->snapprefs.getSnapFrom(p.getSourceType()) == false) {
@@ -677,7 +696,7 @@ void Inkscape::ObjectSnapper::guideFreeSnap(SnappedConstraints &sc,
 void Inkscape::ObjectSnapper::guideConstrainedSnap(SnappedConstraints &sc,
                                         Geom::Point const &p,
                                         Geom::Point const &guide_normal,
-                                        ConstraintLine const &/*c*/) const
+                                        SnapConstraint const &/*c*/) const
 {
     /* Get a list of all the SPItems that we will try to snap to */
     std::vector<SPItem*> cand;
index caf643f730e867b89f498b701f03c2eff56daa8a..454a18545d54e2ea06b5d00947514057700760fe 100644 (file)
@@ -46,7 +46,7 @@ public:
     void guideConstrainedSnap(SnappedConstraints &sc,
                        Geom::Point const &p,
                        Geom::Point const &guide_normal,
-                       ConstraintLine const &c) const;
+                       SnapConstraint const &c) const;
 
     bool ThisSnapperMightSnap() const;
     bool GuidesMightSnap() const;
@@ -63,7 +63,7 @@ public:
     void constrainedSnap(SnappedConstraints &sc,
                   Inkscape::SnapCandidatePoint const &p,
                   Geom::OptRect const &bbox_to_snap,
-                  ConstraintLine const &c,
+                  SnapConstraint const &c,
                   std::vector<SPItem const *> const *it) const;
 
 private:
@@ -98,7 +98,7 @@ private:
 
     void _snapPathsConstrained(SnappedConstraints &sc,
                  Inkscape::SnapCandidatePoint const &p, // in desktop coordinates
-                 ConstraintLine const &c) const;
+                 SnapConstraint const &c) const;
 
     bool isUnselectedNode(Geom::Point const &point, std::vector<Inkscape::SnapCandidatePoint> const *unselected_nodes) const;
 
index 5b9f6808aa0f3a82ab95bc322a380167836e1270..4a21d7bcbcbc8b797f0e5febd97c74a6cad952fe 100644 (file)
@@ -1470,7 +1470,7 @@ void pen_set_to_nearest_horiz_vert(const SPPenContext *const pc, Geom::Point &pt
         }
     } else {
         // Create a horizontal or vertical constraint line
-        Inkscape::Snapper::ConstraintLine cl(origin, next_dir ? Geom::Point(0, 1) : Geom::Point(1, 0));
+        Inkscape::Snapper::SnapConstraint cl(origin, next_dir ? Geom::Point(0, 1) : Geom::Point(1, 0));
 
         // Snap along the constraint line; if we didn't snap then still the constraint will be applied
         SnapManager &m = pc->desktop->namedview->snap_manager;
index 05f47d4ab017b02996ee661592d8fc5aadfba778..9c83dd63ebf3ce399f6fef40b6d0dfcfc3e25409 100644 (file)
@@ -1209,7 +1209,7 @@ gboolean Inkscape::SelTrans::skewRequest(SPSelTransHandle const &handle, Geom::P
         SnapManager &m = _desktop->namedview->snap_manager;
         m.setup(_desktop, false, _items_const);
 
-        Inkscape::Snapper::ConstraintLine const constraint(component_vectors[dim_b]);
+        Inkscape::Snapper::SnapConstraint const constraint(component_vectors[dim_b]);
         // When skewing, we cannot snap the corners of the bounding box, see the comment in "constrainedSnapSkew" for details
         Geom::Point const s(skew[dim_a], scale[dim_a]);
         Inkscape::SnappedPoint sn = m.constrainedSnapSkew(_snap_points, _point, constraint, s, _origin, Geom::Dim2(dim_b));
@@ -1276,7 +1276,10 @@ gboolean Inkscape::SelTrans::rotateRequest(Geom::Point &pt, guint state)
     if (fabs(h2) < 1e-15) return FALSE;
     Geom::Point q2 = d2 / h2; // normalized new vector to handle
 
-    double radians;
+    Geom::Rotate r1(q1);
+    Geom::Rotate r2(q2);
+
+    double radians = atan2(Geom::dot(Geom::rot90(d1), d2), Geom::dot(d1, d2));;
     if (state & GDK_CONTROL_MASK) {
         // Snap to defined angle increments
         double cos_t = Geom::dot(q1, q2);
@@ -1285,15 +1288,25 @@ gboolean Inkscape::SelTrans::rotateRequest(Geom::Point &pt, guint state)
         if (snaps) {
             radians = ( M_PI / snaps ) * floor( radians * snaps / M_PI + .5 );
         }
-        q1 = Geom::Point(1, 0);
-        q2 = Geom::Point(cos(radians), sin(radians));
+        r1 = Geom::Rotate(0); //q1 = Geom::Point(1, 0);
+        r2 = Geom::Rotate(radians); //q2 = Geom::Point(cos(radians), sin(radians));
     } else {
-        radians = atan2(Geom::dot(Geom::rot90(d1), d2),
-                        Geom::dot(d1, d2));
+        SnapManager &m = _desktop->namedview->snap_manager;
+        m.setup(_desktop, false, _items_const);
+        // When rotating, we cannot snap the corners of the bounding box, see the comment in "constrainedSnapRotate" for details
+        Inkscape::SnappedPoint sn = m.constrainedSnapRotate(_snap_points, _point, radians, _origin);
+
+        if (sn.getSnapped()) {
+            _desktop->snapindicator->set_new_snaptarget(sn);
+            // We snapped something, so change the rotation to reflect it
+            radians = sn.getTransformation()[0];
+            r1 = Geom::Rotate(0);
+            r2 = Geom::Rotate(radians);
+        } else {
+            _desktop->snapindicator->remove_snaptarget();
+        }
     }
 
-    Geom::Rotate const r1(q1);
-    Geom::Rotate const r2(q2);
 
     // Calculate the relative affine
     _relative_affine = r2 * r1.inverse();
@@ -1460,14 +1473,14 @@ void Inkscape::SelTrans::moveTo(Geom::Point const &xy, guint state)
             // the constraint-line once. The constraint lines are parallel, but might not be colinear.
             // Therefore we will have to set the point through which the constraint-line runs
             // individually for each point to be snapped; this will be handled however by _snapTransformed()
-            s.push_back(m.constrainedSnapTranslation(_bbox_points_for_translating,
+            s.push_back(m.constrainedSnapTranslate(_bbox_points_for_translating,
                                                      _point,
-                                                     Inkscape::Snapper::ConstraintLine(component_vectors[dim]),
+                                                     Inkscape::Snapper::SnapConstraint(component_vectors[dim]),
                                                      dxy));
 
-            s.push_back(m.constrainedSnapTranslation(_snap_points,
+            s.push_back(m.constrainedSnapTranslate(_snap_points,
                                                      _point,
-                                                     Inkscape::Snapper::ConstraintLine(component_vectors[dim]),
+                                                     Inkscape::Snapper::SnapConstraint(component_vectors[dim]),
                                                      dxy));
         } else { // !control
 
@@ -1477,8 +1490,8 @@ void Inkscape::SelTrans::moveTo(Geom::Point const &xy, guint state)
             g_get_current_time(&starttime); */
 
             /* Snap to things with no constraint */
-            s.push_back(m.freeSnapTranslation(_bbox_points_for_translating, _point, dxy));
-            s.push_back(m.freeSnapTranslation(_snap_points, _point, dxy));
+            s.push_back(m.freeSnapTranslate(_bbox_points_for_translating, _point, dxy));
+            s.push_back(m.freeSnapTranslate(_snap_points, _point, dxy));
 
               /*g_get_current_time(&endtime);
               double elapsed = ((((double)endtime.tv_sec - starttime.tv_sec) * G_USEC_PER_SEC + (endtime.tv_usec - starttime.tv_usec))) / 1000.0;
@@ -1504,7 +1517,7 @@ void Inkscape::SelTrans::moveTo(Geom::Point const &xy, guint state)
             if (control) {
                 // If we didn't snap, then we should still constrain horizontally or vertically
                 // (When we did snap, then this constraint has already been enforced by
-                // calling constrainedSnapTranslation() above)
+                // calling constrainedSnapTranslate() above)
                 if (fabs(dxy[Geom::X]) > fabs(dxy[Geom::Y])) {
                     dxy[Geom::Y] = 0;
                 } else {
index c47f93ff11c37a77dfcc685ae00f8ce37c458d13..265b7c19af8d17e135bd7fe991f7c5973a8608f7 100644 (file)
@@ -335,7 +335,7 @@ Geom::Point SnapManager::multipleOfGridPitch(Geom::Point const &t, Geom::Point c
 
 void SnapManager::constrainedSnapReturnByRef(Geom::Point &p,
                                              Inkscape::SnapSourceType const source_type,
-                                             Inkscape::Snapper::ConstraintLine const &constraint,
+                                             Inkscape::Snapper::SnapConstraint const &constraint,
                                              Geom::OptRect const &bbox_to_snap) const
 {
     Inkscape::SnappedPoint const s = constrainedSnap(Inkscape::SnapCandidatePoint(p, source_type, 0), constraint, bbox_to_snap);
@@ -359,13 +359,19 @@ void SnapManager::constrainedSnapReturnByRef(Geom::Point &p,
  */
 
 Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::SnapCandidatePoint const &p,
-                                                    Inkscape::Snapper::ConstraintLine const &constraint,
+                                                    Inkscape::Snapper::SnapConstraint const &constraint,
                                                     Geom::OptRect const &bbox_to_snap) const
 {
     // First project the mouse pointer onto the constraint
     Geom::Point pp = constraint.projection(p.getPoint());
 
-    Inkscape::SnappedPoint no_snap = Inkscape::SnappedPoint(pp, p.getSourceType(), p.getSourceNum(), Inkscape::SNAPTARGET_CONSTRAINT, Geom::L2(pp - p.getPoint()), 0, false, true, false);
+    Inkscape::SnappedPoint no_snap = Inkscape::SnappedPoint(pp, p.getSourceType(), p.getSourceNum(), Inkscape::SNAPTARGET_CONSTRAINT, NR_HUGE, 0, false, true, false);
+    if (constraint.isCircular()) {
+        Geom::Point v_orig = constraint.getDirection(); // vector from the origin to the original (untransformed) point
+        Geom::Point v_proj = pp - constraint.getPoint(); // vector from the origin to the projected point
+        Geom::Coord angle = atan2(Geom::dot(Geom::rot90(v_orig), v_proj), Geom::dot(v_orig, v_proj));
+        no_snap.setTransformation(Geom::Point(angle, angle)); // Store the rotation (in radians), needed in case of snapping while rotating
+    }
 
     if (!someSnapperMightSnap()) {
         // Always return point on constraint
@@ -464,7 +470,7 @@ void SnapManager::guideConstrainedSnap(Geom::Point &p, SPGuide const &guideline)
 
     // Snap to nodes or paths
     SnappedConstraints sc;
-    Inkscape::Snapper::ConstraintLine cl(guideline.point_on_line, Geom::rot90(guideline.normal_to_line));
+    Inkscape::Snapper::SnapConstraint cl(guideline.point_on_line, Geom::rot90(guideline.normal_to_line));
     if (object.ThisSnapperMightSnap()) {
         object.constrainedSnap(sc, candidate, Geom::OptRect(), cl, NULL);
     }
@@ -509,7 +515,7 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed(
     std::vector<Inkscape::SnapCandidatePoint> const &points,
     Geom::Point const &pointer,
     bool constrained,
-    Inkscape::Snapper::ConstraintLine const &constraint,
+    Inkscape::Snapper::SnapConstraint const &constraint,
     Transformation transformation_type,
     Geom::Point const &transformation,
     Geom::Point const &origin,
@@ -559,8 +565,17 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed(
     g_assert(best_snapped_point.getAlwaysSnap() == false); // Check initialization of snapped point
     g_assert(best_snapped_point.getAtIntersection() == false);
 
-    std::vector<Inkscape::SnapCandidatePoint>::iterator j = transformed_points.begin();
+    // Warnings for the devs
+    if (constrained && transformation_type == SCALE && !uniform) {
+        g_warning("Non-uniform constrained scaling is not supported!");
+    }
+
+    if (!constrained && transformation_type == ROTATE) {
+        // We do not yet allow for simultaneous rotation and scaling
+        g_warning("Unconstrained rotation is not supported!");
+    }
 
+    std::vector<Inkscape::SnapCandidatePoint>::iterator j = transformed_points.begin();
 
     // std::cout << std::endl;
     bool first_free_snap = true;
@@ -568,27 +583,28 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed(
 
         /* Snap it */
         Inkscape::SnappedPoint snapped_point;
-        Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
-        Geom::Point const b = ((*i).getPoint() - origin); // vector to original point
+        Inkscape::Snapper::SnapConstraint dedicated_constraint = constraint;
+        Geom::Point const b = ((*i).getPoint() - origin); // vector to original point (not the transformed point! required for rotations!)
 
         if (constrained) {
-            if ((transformation_type == SCALE || transformation_type == STRETCH) && uniform) {
+            if (((transformation_type == SCALE || transformation_type == STRETCH) && uniform)) {
                 // When uniformly scaling, each point will have its own unique constraint line,
                 // running from the scaling origin to the original untransformed point. We will
                 // calculate that line here
-                dedicated_constraint = Inkscape::Snapper::ConstraintLine(origin, b);
+                dedicated_constraint = Inkscape::Snapper::SnapConstraint(origin, b);
+            } else if (transformation_type == ROTATE) {
+                // Geom::L2(b) is the radius of the circular constraint
+                dedicated_constraint = Inkscape::Snapper::SnapConstraint(origin, b, Geom::L2(b));
             } else if (transformation_type == STRETCH) { // when non-uniform stretching {
-                dedicated_constraint = Inkscape::Snapper::ConstraintLine((*i).getPoint(), component_vectors[dim]);
-            } else if (transformation_type == TRANSLATION) {
+                dedicated_constraint = Inkscape::Snapper::SnapConstraint((*i).getPoint(), component_vectors[dim]);
+            } else if (transformation_type == TRANSLATE) {
                 // When doing a constrained translation, all points will move in the same direction, i.e.
                 // either horizontally or vertically. The lines along which they move are therefore all
-                // parallel, but might not be colinear. Therefore we will have to set the point through
-                // which the constraint-line runs here, for each point individually.
-                dedicated_constraint.setPoint((*i).getPoint());
+                // parallel, but might not be colinear. Therefore we will have to specify the point through
+                // which the constraint-line runs here, for each point individually. (we could also have done this
+                // earlier on, e.g. in seltrans.cpp but we're being lazy there and don't want to add an iteration loop)
+                dedicated_constraint = Inkscape::Snapper::SnapConstraint((*i).getPoint(), constraint.getDirection());
             } // else: leave the original constraint, e.g. for skewing
-            if (transformation_type == SCALE && !uniform) {
-                g_warning("Non-uniform constrained scaling is not supported!");
-            }
             snapped_point = constrainedSnap(*j, dedicated_constraint, bbox);
         } else {
             bool const c1 = fabs(b[Geom::X]) < 1e-6;
@@ -597,7 +613,7 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed(
                 // When scaling, a point aligned either horizontally or vertically with the origin can only
                 // move in that specific direction; therefore it should only snap in that direction, otherwise
                 // we will get snapped points with an invalid transformation
-                dedicated_constraint = Inkscape::Snapper::ConstraintLine(origin, component_vectors[c1]);
+                dedicated_constraint = Inkscape::Snapper::SnapConstraint(origin, component_vectors[c1]);
                 snapped_point = constrainedSnap(*j, dedicated_constraint, bbox);
             } else {
                 // If we have a collection of SnapCandidatePoints, with mixed constrained snapping and free snapping
@@ -627,7 +643,7 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed(
             //Geom::Point const b = (*i - origin); // vector to original point
 
             switch (transformation_type) {
-                case TRANSLATION:
+                case TRANSLATE:
                     result = snapped_point.getPoint() - (*i).getPoint();
                     /* Consider the case in which a box is almost aligned with a grid in both
                      * horizontal and vertical directions. The distance to the intersection of
@@ -693,6 +709,12 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed(
                     snapped_point.setSnapDistance(std::abs(result[0] - transformation[0]));
                     snapped_point.setSecondSnapDistance(NR_HUGE);
                     break;
+                case ROTATE:
+                    result = snapped_point.getTransformation();
+                    // Store the metric for this transformation as a virtual distance (we're storing an angle)
+                    snapped_point.setSnapDistance(std::abs(result[0] - transformation[0]));
+                    snapped_point.setSecondSnapDistance(NR_HUGE);
+                    break;
                 default:
                     g_assert_not_reached();
             }
@@ -738,22 +760,21 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed(
  *  \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.
  */
 
-Inkscape::SnappedPoint SnapManager::freeSnapTranslation(std::vector<Inkscape::SnapCandidatePoint> const &p,
+Inkscape::SnappedPoint SnapManager::freeSnapTranslate(std::vector<Inkscape::SnapCandidatePoint> const &p,
                                                         Geom::Point const &pointer,
                                                         Geom::Point const &tr) const
 {
     if (p.size() == 1) {
-        Geom::Point pt = _transformPoint(p.at(0), TRANSLATION, tr, Geom::Point(0,0), Geom::X, false);
+        Geom::Point pt = _transformPoint(p.at(0), TRANSLATE, tr, Geom::Point(0,0), Geom::X, false);
         _displaySnapsource(Inkscape::SnapCandidatePoint(pt, p.at(0).getSourceType()));
     }
 
-    return _snapTransformed(p, pointer, false, Geom::Point(0,0), TRANSLATION, tr, Geom::Point(0,0), Geom::X, false);
+    return _snapTransformed(p, pointer, false, Geom::Point(0,0), TRANSLATE, tr, Geom::Point(0,0), Geom::X, false);
 }
 
 /**
  *  \brief Apply a translation to a set of points and try to snap along a constraint
  *
- *  \param point_type Category of points to which the source point belongs: node or bounding box.
  *  \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source.
  *  \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
  *  \param constraint The direction or line along which snapping must occur.
@@ -761,24 +782,23 @@ Inkscape::SnappedPoint SnapManager::freeSnapTranslation(std::vector<Inkscape::Sn
  *  \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.
  */
 
-Inkscape::SnappedPoint SnapManager::constrainedSnapTranslation(std::vector<Inkscape::SnapCandidatePoint> const &p,
+Inkscape::SnappedPoint SnapManager::constrainedSnapTranslate(std::vector<Inkscape::SnapCandidatePoint> const &p,
                                                                Geom::Point const &pointer,
-                                                               Inkscape::Snapper::ConstraintLine const &constraint,
+                                                               Inkscape::Snapper::SnapConstraint const &constraint,
                                                                Geom::Point const &tr) const
 {
     if (p.size() == 1) {
-        Geom::Point pt = _transformPoint(p.at(0), TRANSLATION, tr, Geom::Point(0,0), Geom::X, false);
+        Geom::Point pt = _transformPoint(p.at(0), TRANSLATE, tr, Geom::Point(0,0), Geom::X, false);
         _displaySnapsource(Inkscape::SnapCandidatePoint(pt, p.at(0).getSourceType()));
     }
 
-    return _snapTransformed(p, pointer, true, constraint, TRANSLATION, tr, Geom::Point(0,0), Geom::X, false);
+    return _snapTransformed(p, pointer, true, constraint, TRANSLATE, tr, Geom::Point(0,0), Geom::X, false);
 }
 
 
 /**
  *  \brief Apply a scaling to a set of points and try to snap freely in 2 degrees-of-freedom
  *
- *  \param point_type Category of points to which the source point belongs: node or bounding box.
  *  \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source.
  *  \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
  *  \param s Proposed scaling; the final scaling can only be calculated after snapping has occurred
@@ -803,7 +823,6 @@ Inkscape::SnappedPoint SnapManager::freeSnapScale(std::vector<Inkscape::SnapCand
 /**
  *  \brief Apply a scaling to a set of points and snap such that the aspect ratio of the selection is preserved
  *
- *  \param point_type Category of points to which the source point belongs: node or bounding box.
  *  \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source.
  *  \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
  *  \param s Proposed scaling; the final scaling can only be calculated after snapping has occurred
@@ -828,7 +847,6 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapScale(std::vector<Inkscape::S
 /**
  *  \brief Apply a stretch to a set of points and snap such that the direction of the stretch is preserved
  *
- *  \param point_type Category of points to which the source point belongs: node or bounding box.
  *  \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source.
  *  \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
  *  \param s Proposed stretch; the final stretch can only be calculated after snapping has occurred
@@ -856,7 +874,6 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapStretch(std::vector<Inkscape:
 /**
  *  \brief Apply a skew to a set of points and snap such that the direction of the skew is preserved
  *
- *  \param point_type Category of points to which the source point belongs: node or bounding box.
  *  \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source.
  *  \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
  *  \param constraint The direction or line along which snapping must occur.
@@ -868,7 +885,7 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapStretch(std::vector<Inkscape:
 
 Inkscape::SnappedPoint SnapManager::constrainedSnapSkew(std::vector<Inkscape::SnapCandidatePoint> const &p,
                                                  Geom::Point const &pointer,
-                                                 Inkscape::Snapper::ConstraintLine const &constraint,
+                                                 Inkscape::Snapper::SnapConstraint const &constraint,
                                                  Geom::Point const &s,
                                                  Geom::Point const &o,
                                                  Geom::Dim2 d) const
@@ -892,6 +909,36 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapSkew(std::vector<Inkscape::Sn
     return _snapTransformed(p, pointer, true, constraint, SKEW, s, o, d, false);
 }
 
+/**
+ *  \brief Apply a rotation to a set of points and snap, without scaling
+ *
+ *  \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source.
+ *  \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
+ *  \param angle Proposed rotation (in radians); the final rotation can only be calculated after snapping has occurred
+ *  \param o Origin of the rotation
+ *  \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.
+ */
+
+Inkscape::SnappedPoint SnapManager::constrainedSnapRotate(std::vector<Inkscape::SnapCandidatePoint> const &p,
+                                                    Geom::Point const &pointer,
+                                                    Geom::Coord const &angle,
+                                                    Geom::Point const &o) const
+{
+    // Snapping the nodes of the bounding box of a selection that is being transformed, will only work if
+    // the transformation of the bounding box is equal to the transformation of the individual nodes. This is
+    // NOT the case for example when rotating or skewing. The bounding box itself cannot possibly rotate or skew,
+    // so it's corners have a different transformation. The snappers cannot handle this, therefore snapping
+    // of bounding boxes is not allowed here.
+
+    if (p.size() == 1) {
+        Geom::Point pt = _transformPoint(p.at(0), ROTATE, Geom::Point(angle, angle), o, Geom::X, false);
+        _displaySnapsource(Inkscape::SnapCandidatePoint(pt, p.at(0).getSourceType()));
+    }
+
+    return _snapTransformed(p, pointer, true, Geom::Point(0,0), ROTATE, Geom::Point(angle, angle), o, Geom::X, false);
+
+}
+
 /**
  * \brief Given a set of possible snap targets, find the best target (which is not necessarily
  * also the nearest target), and show the snap indicator if requested
@@ -1116,7 +1163,7 @@ Geom::Point SnapManager::_transformPoint(Inkscape::SnapCandidatePoint const &p,
     /* Work out the transformed version of this point */
     Geom::Point transformed;
     switch (transformation_type) {
-        case TRANSLATION:
+        case TRANSLATE:
             transformed = p.getPoint() + transformation;
             break;
         case SCALE:
@@ -1141,6 +1188,10 @@ Geom::Point SnapManager::_transformPoint(Inkscape::SnapCandidatePoint const &p,
             // Apply that scale factor here
             transformed[1-dim] = (p.getPoint() - origin)[1 - dim] * transformation[1] + origin[1 - dim];
             break;
+        case ROTATE:
+            // for rotations: transformation[0] stores the angle in radians
+            transformed = (p.getPoint() - origin) * Geom::Rotate(transformation[0]) + origin;
+            break;
         default:
             g_assert_not_reached();
     }
index 8a5688bea19473c3df511a46e3ca615c676c1440..26e599cc66773aae66edc20c39b6d8cb682a922b 100644 (file)
@@ -67,10 +67,11 @@ class SnapManager
 {
 public:
     enum Transformation {
-        TRANSLATION,
+        TRANSLATE,
         SCALE,
         STRETCH,
-        SKEW
+        SKEW,
+        ROTATE
     };
 
     SnapManager(SPNamedView const *v);
@@ -113,23 +114,23 @@ public:
     // point, by overwriting p, if snapping has occurred; otherwise p is untouched
     void constrainedSnapReturnByRef(Geom::Point &p,
                                     Inkscape::SnapSourceType const source_type,
-                                    Inkscape::Snapper::ConstraintLine const &constraint,
+                                    Inkscape::Snapper::SnapConstraint const &constraint,
                                     Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const;
 
     Inkscape::SnappedPoint constrainedSnap(Inkscape::SnapCandidatePoint const &p,
-                                           Inkscape::Snapper::ConstraintLine const &constraint,
+                                           Inkscape::Snapper::SnapConstraint const &constraint,
                                            Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const;
 
     void guideFreeSnap(Geom::Point &p, Geom::Point const &guide_normal, SPGuideDragType drag_type) const;
     void guideConstrainedSnap(Geom::Point &p, SPGuide const &guideline) const;
 
-    Inkscape::SnappedPoint freeSnapTranslation(std::vector<Inkscape::SnapCandidatePoint> const &p,
+    Inkscape::SnappedPoint freeSnapTranslate(std::vector<Inkscape::SnapCandidatePoint> const &p,
                                                Geom::Point const &pointer,
                                                Geom::Point const &tr) const;
 
-    Inkscape::SnappedPoint constrainedSnapTranslation(std::vector<Inkscape::SnapCandidatePoint> const &p,
+    Inkscape::SnappedPoint constrainedSnapTranslate(std::vector<Inkscape::SnapCandidatePoint> const &p,
                                                       Geom::Point const &pointer,
-                                                      Inkscape::Snapper::ConstraintLine const &constraint,
+                                                      Inkscape::Snapper::SnapConstraint const &constraint,
                                                       Geom::Point const &tr) const;
 
     Inkscape::SnappedPoint freeSnapScale(std::vector<Inkscape::SnapCandidatePoint> const &p,
@@ -151,11 +152,16 @@ public:
 
     Inkscape::SnappedPoint constrainedSnapSkew(std::vector<Inkscape::SnapCandidatePoint> const &p,
                                                Geom::Point const &pointer,
-                                               Inkscape::Snapper::ConstraintLine const &constraint,
+                                               Inkscape::Snapper::SnapConstraint const &constraint,
                                                Geom::Point const &s, // s[0] = skew factor, s[1] = scale factor
                                                Geom::Point const &o,
                                                Geom::Dim2 d) const;
 
+    Inkscape::SnappedPoint constrainedSnapRotate(std::vector<Inkscape::SnapCandidatePoint> const &p,
+                                                    Geom::Point const &pointer,
+                                                    Geom::Coord const &angle,
+                                                    Geom::Point const &o) const;
+
     Inkscape::GuideSnapper guide;      ///< guide snapper
     Inkscape::ObjectSnapper object;    ///< snapper to other objects
     Inkscape::SnapPreferences snapprefs;
@@ -181,14 +187,11 @@ private:
     SPDesktop const *_desktop;
     bool _snapindicator; ///< When true, an indicator will be drawn at the position that was being snapped to
     std::vector<Inkscape::SnapCandidatePoint> *_unselected_nodes; ///< Nodes of the path that is currently being edited and which have not been selected and which will therefore be stationary. Only these nodes will be considered for snapping to. Of each unselected node both the position (Geom::Point) and the type (Inkscape::SnapTargetType) will be stored
-    //TODO: Make _unselected_nodes type safe; in the line above int is used for Inkscape::SnapTargetType, but if I remember
-    //correctly then in other cases the int is being used for Inkscape::SnapSourceType, or for both. How to make
-    //this type safe?
 
     Inkscape::SnappedPoint _snapTransformed(std::vector<Inkscape::SnapCandidatePoint> const &points,
                                             Geom::Point const &pointer,
                                             bool constrained,
-                                            Inkscape::Snapper::ConstraintLine const &constraint,
+                                            Inkscape::Snapper::SnapConstraint const &constraint,
                                             Transformation transformation_type,
                                             Geom::Point const &transformation,
                                             Geom::Point const &origin,
index b5bb17de9240c210e58b0f6314c4d82b02faf8fa..d8214db8016691d9205e7937b484a47d98e97030 100644 (file)
@@ -15,6 +15,7 @@
 #include <map>
 #include <list>
 #include <boost/optional.hpp>
+#include <glib.h> // for g_assert
 
 #include "snapped-point.h"
 #include "snapped-line.h"
@@ -63,18 +64,29 @@ public:
                           std::vector<SPItem const *> const */*it*/,
                           std::vector<SnapCandidatePoint> */*unselected_nodes*/) const {};
 
-    class ConstraintLine
+    // Class for storing the constraint for constrained snapping; can be
+    // - a line (infinite line with origin, running through _point pointing in _direction)
+    // - a direction (infinite line without origin, i.e. only a direction vector, stored in _direction)
+    // - a circle (_point denotes the center, _radius doesn't need an explanation, _direction contains
+    //      the vector from the origin to the original untransformed point);
+    class SnapConstraint
     {
+    private:
+        enum SnapConstraintType {LINE, DIRECTION, CIRCLE};
+
     public:
-        ConstraintLine(Geom::Point const &d) : _has_point(false), _direction(d) {}
-        ConstraintLine(Geom::Point const &p, Geom::Point const &d) : _has_point(true), _point(p), _direction(d) {}
-        ConstraintLine(Geom::Line const &l) : _has_point(true), _point(l.origin()), _direction(l.versor()) {}
+        // Constructs a direction constraint, e.g. horizontal or vertical but without a specified point
+        SnapConstraint(Geom::Point const &d) : _direction(d), _type(DIRECTION) {}
+        // Constructs a linear constraint
+        SnapConstraint(Geom::Point const &p, Geom::Point const &d) : _point(p), _direction(d), _type(LINE) {}
+        SnapConstraint(Geom::Line const &l) : _point(l.origin()), _direction(l.versor()), _type(LINE) {}
+        // Constructs a circular constraint
+        SnapConstraint(Geom::Point const &p, Geom::Point const &d, Geom::Coord const &r) : _point(p), _direction(d), _radius(r), _type(CIRCLE) {}
 
-        bool hasPoint() const {
-            return _has_point;
-        }
+        bool hasPoint() const {return _type != DIRECTION;}
 
         Geom::Point getPoint() const {
+            g_assert(_type != DIRECTION);
             return _point;
         }
 
@@ -82,28 +94,45 @@ public:
             return _direction;
         }
 
-        void setPoint(Geom::Point const &p) {
-            _point = p;
-            _has_point = true;
+        Geom::Coord getRadius() const {
+            g_assert(_type == CIRCLE);
+            return _radius;
         }
 
-        Geom::Point projection(Geom::Point const &p) const { // returns the projection of p on this constraintline
-            Geom::Point const p1_on_cl = _has_point ? _point : p;
-            Geom::Point const p2_on_cl = p1_on_cl + _direction;
-            return Geom::projection(p, Geom::Line(p1_on_cl, p2_on_cl));
+        bool isCircular() const { return _type == CIRCLE; }
+        bool isLinear() const { return _type == LINE; }
+        bool isDirection() const { return _type == DIRECTION; }
+
+        Geom::Point projection(Geom::Point const &p) const { // returns the projection of p on this constraint
+            if (_type == CIRCLE) {
+                // project on to a circular constraint
+                Geom::Point v_orig = p - _point;
+                Geom::Coord l = Geom::L2(v_orig);
+                if (l > 0) {
+                    return _point + _radius * v_orig/l; // Length of _direction is equal to the radius
+                } else {
+                    // point to be projected is exactly at the center of the circle, so any point on the circle is a projection
+                    return _point + Geom::Point(_radius, 0);
+                }
+            } else {
+                // project on to a linear constraint
+                Geom::Point const p1_on_cl = (_type == LINE) ? _point : p;
+                Geom::Point const p2_on_cl = p1_on_cl + _direction;
+                return Geom::projection(p, Geom::Line(p1_on_cl, p2_on_cl));
+            }
         }
 
     private:
-
-        bool _has_point;
         Geom::Point _point;
         Geom::Point _direction;
+        Geom::Coord _radius;
+        SnapConstraintType _type;
     };
 
     virtual void constrainedSnap(SnappedConstraints &/*sc*/,
                                  Inkscape::SnapCandidatePoint const &/*p*/,
                                  Geom::OptRect const &/*bbox_to_snap*/,
-                                 ConstraintLine const &/*c*/,
+                                 SnapConstraint const &/*c*/,
                                  std::vector<SPItem const *> const */*it*/) const {};
 
 protected:
index 9c9c58ff14dc86e315c786a54edbbf3723fb1711..886ddd1bed248543aa29b60ee9cab7f6748a4e9e 100644 (file)
@@ -266,7 +266,7 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event)
 
         Node *node_away = (this == &_parent->_front ? _parent->_prev() : _parent->_next());
         if (_parent->type() == NODE_SMOOTH && Node::_is_line_segment(_parent, node_away)) {
-            Inkscape::Snapper::ConstraintLine cl(_parent->position(),
+            Inkscape::Snapper::SnapConstraint cl(_parent->position(),
                 _parent->position() - node_away->position());
             Inkscape::SnappedPoint p;
             p = sm.constrainedSnap(Inkscape::SnapCandidatePoint(new_pos, SNAPSOURCE_NODE_HANDLE), cl);
@@ -974,7 +974,7 @@ void Node::dragged(Geom::Point &new_pos, GdkEventMotion *event)
             // with Ctrl+Alt, constrain to handle lines
             // project the new position onto a handle line that is closer
             boost::optional<Geom::Point> front_point, back_point;
-            boost::optional<Inkscape::Snapper::ConstraintLine> line_front, line_back;
+            boost::optional<Inkscape::Snapper::SnapConstraint> line_front, line_back;
             if (_front.isDegenerate()) {
                 if (_is_line_segment(this, _next()))
                     front_point = _next()->position() - origin;
@@ -988,9 +988,9 @@ void Node::dragged(Geom::Point &new_pos, GdkEventMotion *event)
                 back_point = _back.relativePos();
             }
             if (front_point)
-                line_front = Inkscape::Snapper::ConstraintLine(origin, *front_point);
+                line_front = Inkscape::Snapper::SnapConstraint(origin, *front_point);
             if (back_point)
-                line_back = Inkscape::Snapper::ConstraintLine(origin, *back_point);
+                line_back = Inkscape::Snapper::SnapConstraint(origin, *back_point);
 
             // TODO: combine the snap and non-snap branches by modifying snap.h / snap.cpp
             if (snap) {
@@ -1029,8 +1029,8 @@ void Node::dragged(Geom::Point &new_pos, GdkEventMotion *event)
             // with Ctrl, constrain to axes
             // TODO combine the two branches
             if (snap) {
-                Inkscape::Snapper::ConstraintLine line_x(origin, Geom::Point(1, 0));
-                Inkscape::Snapper::ConstraintLine line_y(origin, Geom::Point(0, 1));
+                Inkscape::Snapper::SnapConstraint line_x(origin, Geom::Point(1, 0));
+                Inkscape::Snapper::SnapConstraint line_y(origin, Geom::Point(0, 1));
                 fp = sm.constrainedSnap(Inkscape::SnapCandidatePoint(new_pos, _snapSourceType()), line_x);
                 bp = sm.constrainedSnap(Inkscape::SnapCandidatePoint(new_pos, _snapSourceType()), line_y);
             }