Code

When doing a constrained snap, then don't try snapping the mouse pointer itself but...
authorDiederik van Lierop <mailat-signdiedenrezidotnl>
Fri, 20 Aug 2010 20:20:02 +0000 (22:20 +0200)
committerDiederik van Lierop <mailat-signdiedenrezidotnl>
Fri, 20 Aug 2010 20:20:02 +0000 (22:20 +0200)
src/line-snapper.cpp
src/object-snapper.cpp
src/object-snapper.h

index 0a1567a47a21454cfa6357acf50b9d9969779a26..be64438ed503d5b6282b77fed33348822f4b79b6 100644 (file)
@@ -72,11 +72,14 @@ void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc,
         return;
     }
 
+    // project the mouse pointer onto the constraint. Only the projected point will be considered for snapping
+    Geom::Point pp = c.projection(p.getPoint());
+
     /* Get the lines that we will try to snap to */
-    const LineList lines = _getSnapLines(p.getPoint());
+    const LineList lines = _getSnapLines(pp);
 
     for (LineList::const_iterator i = lines.begin(); i != lines.end(); i++) {
-        Geom::Point const point_on_line = c.hasPoint() ? c.getPoint() : p.getPoint();
+        Geom::Point const point_on_line = c.hasPoint() ? c.getPoint() : pp;
         Geom::Line gridguide_line(i->second, i->second + Geom::rot90(i->first));
 
         if (c.isCircular()) {
@@ -88,7 +91,7 @@ void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc,
             Geom::Coord radius = c.getRadius();
             if (dist == radius) {
                 // Only one point of intersection;
-                _addSnappedPoint(sc, p_proj, Geom::L2(p.getPoint() - p_proj), p.getSourceType(), p.getSourceNum(), true);
+                _addSnappedPoint(sc, p_proj, Geom::L2(pp - p_proj), p.getSourceType(), p.getSourceNum(), true);
             } 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
@@ -96,8 +99,8 @@ void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc,
                 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;
-                    _addSnappedPoint(sc, p_proj + v, Geom::L2(p.getPoint() - (p_proj + v)), p.getSourceType(), p.getSourceNum(), true);
-                    _addSnappedPoint(sc, p_proj - v, Geom::L2(p.getPoint() - (p_proj - v)), p.getSourceType(), p.getSourceNum(), true);
+                    _addSnappedPoint(sc, p_proj + v, Geom::L2(pp - (p_proj + v)), p.getSourceType(), p.getSourceNum(), true);
+                    _addSnappedPoint(sc, p_proj - v, Geom::L2(pp - (p_proj - v)), p.getSourceType(), p.getSourceNum(), true);
                 }
             }
         } else {
@@ -116,7 +119,7 @@ void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc,
 
             if (inters) {
                 Geom::Point t = constraint_line.pointAt((*inters).ta);
-                const Geom::Coord dist = Geom::L2(t - p.getPoint());
+                const Geom::Coord dist = Geom::L2(t - pp);
                 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
index 1540fbabca8df437834890b8d22ed6a9162675d1..f9e21174acb3694eb4fb80f9844d4e363936a0fa 100644 (file)
@@ -258,7 +258,8 @@ void Inkscape::ObjectSnapper::_collectNodes(Inkscape::SnapSourceType const &t,
 void Inkscape::ObjectSnapper::_snapNodes(SnappedConstraints &sc,
                                          Inkscape::SnapCandidatePoint const &p,
                                          std::vector<SnapCandidatePoint> *unselected_nodes,
-                                         SnapConstraint const &c) const
+                                         SnapConstraint const &c,
+                                         Geom::Point const &p_proj_on_constraint) const
 {
     // Iterate through all nodes, find out which one is the closest to p, and snap to it!
 
@@ -274,6 +275,7 @@ void Inkscape::ObjectSnapper::_snapNodes(SnappedConstraints &sc,
 
     for (std::vector<SnapCandidatePoint>::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); k++) {
         Geom::Point target_pt = (*k).getPoint();
+        Geom::Coord dist = NR_HUGE;
         if (!c.isUndefined()) {
             // We're snapping to nodes along a constraint only, so find out if this node
             // is at the constraint, while allowing for a small margin
@@ -282,9 +284,12 @@ void Inkscape::ObjectSnapper::_snapNodes(SnappedConstraints &sc,
                 // is too large, so this point is not on the constraint. Skip it!
                 continue;
             }
+            dist = Geom::L2(target_pt - p_proj_on_constraint);
+        } else {
+            // Free (unconstrained) snapping
+            dist = Geom::L2(target_pt - p.getPoint());
         }
 
-        Geom::Coord dist = Geom::L2(target_pt - p.getPoint());
         if (dist < getSnapperTolerance() && dist < s.getSnapDistance()) {
             s = SnappedPoint(target_pt, p.getSourceType(), p.getSourceNum(), (*k).getTargetType(), dist, getSnapperTolerance(), getSnapperAlwaysSnap(), false, true, (*k).getTargetBBox());
             success = true;
@@ -304,7 +309,7 @@ void Inkscape::ObjectSnapper::_snapTranslatingGuide(SnappedConstraints &sc,
     _collectNodes(SNAPSOURCE_GUIDE, true);
 
     if (_snapmanager->snapprefs.getSnapToItemPath() || _snapmanager->snapprefs.getSnapToBBoxPath() || _snapmanager->snapprefs.getSnapToPageBorder()) {
-        _collectPaths(Inkscape::SnapCandidatePoint(p, SNAPSOURCE_GUIDE), true);
+        _collectPaths(p, SNAPSOURCE_GUIDE, true);
         _snapPaths(sc, Inkscape::SnapCandidatePoint(p, SNAPSOURCE_GUIDE), NULL, NULL);
     }
 
@@ -330,7 +335,8 @@ void Inkscape::ObjectSnapper::_snapTranslatingGuide(SnappedConstraints &sc,
  * Returns index of first NR_END bpath in array.
  */
 
-void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapCandidatePoint const &p,
+void Inkscape::ObjectSnapper::_collectPaths(Geom::Point p,
+                                         Inkscape::SnapSourceType const source_type,
                                          bool const &first_point) const
 {
     // Now, let's first collect all paths to snap to. If we have a whole bunch of points to snap,
@@ -342,8 +348,8 @@ void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapCandidatePoint const &
         // Determine the type of bounding box we should snap to
         SPItem::BBoxType bbox_type = SPItem::GEOMETRIC_BBOX;
 
-        bool p_is_a_node = p.getSourceType() & Inkscape::SNAPSOURCE_NODE_CATEGORY;
-        bool p_is_other = p.getSourceType() & Inkscape::SNAPSOURCE_OTHER_CATEGORY;
+        bool p_is_a_node = source_type & Inkscape::SNAPSOURCE_NODE_CATEGORY;
+        bool p_is_other = source_type & Inkscape::SNAPSOURCE_OTHER_CATEGORY;
 
         if (_snapmanager->snapprefs.getSnapToBBoxPath()) {
             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
@@ -441,7 +447,7 @@ void Inkscape::ObjectSnapper::_snapPaths(SnappedConstraints &sc,
                                      std::vector<Inkscape::SnapCandidatePoint> *unselected_nodes,
                                      SPPath const *selected_path) const
 {
-    _collectPaths(p, p.getSourceNum() == 0);
+    _collectPaths(p.getPoint(), p.getSourceType(), p.getSourceNum() == 0);
     // Now we can finally do the real snapping, using the paths collected above
 
     g_assert(_snapmanager->getDesktop() != NULL);
@@ -542,15 +548,16 @@ bool Inkscape::ObjectSnapper::isUnselectedNode(Geom::Point const &point, std::ve
 
 void Inkscape::ObjectSnapper::_snapPathsConstrained(SnappedConstraints &sc,
                                      Inkscape::SnapCandidatePoint const &p,
-                                     SnapConstraint const &c) const
+                                     SnapConstraint const &c,
+                                     Geom::Point const &p_proj_on_constraint) const
 {
 
-    _collectPaths(p, p.getSourceNum() == 0);
+    _collectPaths(p_proj_on_constraint, p.getSourceType(), p.getSourceNum() == 0);
 
     // Now we can finally do the real snapping, using the paths collected above
 
     g_assert(_snapmanager->getDesktop() != NULL);
-    Geom::Point const p_doc = _snapmanager->getDesktop()->dt2doc(p.getPoint());
+    Geom::Point const p_doc = _snapmanager->getDesktop()->dt2doc(p_proj_on_constraint);
 
     Geom::Point direction_vector = c.getDirection();
     if (!is_zero(direction_vector)) {
@@ -559,9 +566,8 @@ void Inkscape::ObjectSnapper::_snapPathsConstrained(SnappedConstraints &sc,
 
     // 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::Point const p_min_on_cl = _snapmanager->getDesktop()->dt2doc(p_proj_on_constraint - getSnapperTolerance() * direction_vector);
+    Geom::Point const p_max_on_cl = _snapmanager->getDesktop()->dt2doc(p_proj_on_constraint + getSnapperTolerance() * direction_vector);
     Geom::Coord tolerance = getSnapperTolerance();
 
     // PS: Because the paths we're about to snap to are all expressed relative to document coordinate system, we will have
@@ -592,7 +598,7 @@ void Inkscape::ObjectSnapper::_snapPathsConstrained(SnappedConstraints &sc,
                     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);
+                    Geom::Coord dist = Geom::L2(p_proj_on_constraint - 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
                         sc.points.push_back(s);
@@ -675,9 +681,12 @@ void Inkscape::ObjectSnapper::constrainedSnap( SnappedConstraints &sc,
         return;
     }
 
+    // project the mouse pointer onto the constraint. Only the projected point will be considered for snapping
+    Geom::Point pp = c.projection(p.getPoint());
+
     /* Get a list of all the SPItems that we will try to snap to */
     if (p.getSourceNum() == 0) {
-        Geom::Rect const local_bbox_to_snap = bbox_to_snap ? *bbox_to_snap : Geom::Rect(p.getPoint(), p.getPoint());
+        Geom::Rect const local_bbox_to_snap = bbox_to_snap ? *bbox_to_snap : Geom::Rect(pp, pp);
         _findCandidates(sp_document_root(_snapmanager->getDocument()), it, p.getSourceNum() == 0, local_bbox_to_snap, false, Geom::identity());
     }
 
@@ -701,11 +710,11 @@ void Inkscape::ObjectSnapper::constrainedSnap( SnappedConstraints &sc,
                             ));
 
     if (snap_nodes) {
-        _snapNodes(sc, p, unselected_nodes, c);
+        _snapNodes(sc, p, unselected_nodes, c, pp);
     }
 
     if (_snapmanager->snapprefs.getSnapToItemPath() || _snapmanager->snapprefs.getSnapToBBoxPath() || _snapmanager->snapprefs.getSnapToPageBorder()) {
-        _snapPathsConstrained(sc, p, c);
+        _snapPathsConstrained(sc, p, c, pp);
     }
 }
 
index 4933d84598ed72ec1acce5e3ca984c282b5eed59..6bde3dd391a531fd7fc75bea43b0df780e67903d 100644 (file)
@@ -74,9 +74,10 @@ private:
                        Geom::Matrix const additional_affine) const;
 
     void _snapNodes(SnappedConstraints &sc,
-                      Inkscape::SnapCandidatePoint const &p,
+                      Inkscape::SnapCandidatePoint const &p, // in desktop coordinates
                       std::vector<SnapCandidatePoint> *unselected_nodes,
-                      SnapConstraint const &c = SnapConstraint()) const; // in desktop coordinates
+                      SnapConstraint const &c = SnapConstraint(),
+                      Geom::Point const &p_proj_on_constraint = Geom::Point()) const;
 
     void _snapTranslatingGuide(SnappedConstraints &sc,
                      Geom::Point const &p,
@@ -92,12 +93,14 @@ private:
 
     void _snapPathsConstrained(SnappedConstraints &sc,
                  Inkscape::SnapCandidatePoint const &p, // in desktop coordinates
-                 SnapConstraint const &c) const;
+                 SnapConstraint const &c,
+                 Geom::Point const &p_proj_on_constraint) const;
 
     bool isUnselectedNode(Geom::Point const &point, std::vector<Inkscape::SnapCandidatePoint> const *unselected_nodes) const;
 
-    void _collectPaths(Inkscape::SnapCandidatePoint const &p,
-                  bool const &first_point) const;
+    void _collectPaths(Geom::Point p,
+                      Inkscape::SnapSourceType const source_type,
+                      bool const &first_point) const;
 
     void _clear_paths() const;
     Geom::PathVector* _getBorderPathv() const;