Code

- refactor snapping code, removing code duplication
authordvlierop2 <dvlierop2@users.sourceforge.net>
Sun, 21 Sep 2008 20:18:04 +0000 (20:18 +0000)
committerdvlierop2 <dvlierop2@users.sourceforge.net>
Sun, 21 Sep 2008 20:18:04 +0000 (20:18 +0000)
- fix snapping to page corners

src/gradient-drag.cpp
src/line-snapper.cpp
src/object-snapper.cpp
src/snap.cpp
src/snapped-curve.cpp
src/snapped-curve.h
src/snapped-line.cpp
src/snapped-point.cpp
src/snapped-point.h

index 1caaf1d115466a19bce60abf51d61ceb9299c0a3..9432d4b5a004d777c3d111a1271a1b950c639f49 100644 (file)
@@ -593,7 +593,7 @@ gr_knot_moved_handler(SPKnot *knot, NR::Point const *ppointer, guint state, gpoi
                 dist = fabs(p[NR::Y] - dragger->parent->hor_levels[i]);
                 if (dist < snap_dist) {
                     p[NR::Y] = dragger->parent->hor_levels[i];
-                    s = Inkscape::SnappedPoint(p, Inkscape::SNAPTARGET_GRADIENT, dist, snap_dist, false);
+                    s = Inkscape::SnappedPoint(p, Inkscape::SNAPTARGET_GRADIENT, dist, snap_dist, false, false);
                     was_snapped = true;
                     sp_knot_moveto (knot, p);
                 }
@@ -602,7 +602,7 @@ gr_knot_moved_handler(SPKnot *knot, NR::Point const *ppointer, guint state, gpoi
                 dist = fabs(p[NR::X] - dragger->parent->vert_levels[i]);
                 if (dist < snap_dist) {
                     p[NR::X] = dragger->parent->vert_levels[i];
-                    s = Inkscape::SnappedPoint(p, Inkscape::SNAPTARGET_GRADIENT, dist, snap_dist, false);
+                    s = Inkscape::SnappedPoint(p, Inkscape::SNAPTARGET_GRADIENT, dist, snap_dist, false, false);
                     was_snapped = true;
                     sp_knot_moveto (knot, p);
                 }
index 5453c1cf27bc5d71140909251f71491785f9be70..b2934c2e39ad058e328ea40c5bee9d0e43aa4d91 100644 (file)
@@ -97,7 +97,7 @@ void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc,
                     // 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
-                    sc.points.push_back(SnappedPoint(t, Inkscape::SNAPTARGET_UNDEFINED, dist, getSnapperTolerance(), getSnapperAlwaysSnap()));
+                    sc.points.push_back(SnappedPoint(t, Inkscape::SNAPTARGET_UNDEFINED, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), true));
                     // The type of the snap target is yet undefined, as we cannot tell whether 
                     // we're snapping to grid or the guide lines; must be set by on a higher level   
                 }
index 41866b41ca4c1cd0e076389e5c9704bddc3bd685..13fba80609a287a28d96c379d4437ef04caa49e2 100644 (file)
@@ -98,7 +98,7 @@ void Inkscape::ObjectSnapper::_findCandidates(SPObject* parent,
     
     for (SPObject* o = sp_object_first_child(parent); o != NULL; o = SP_OBJECT_NEXT(o)) {
         g_assert(_snapmanager->getDesktop() != NULL);
-       if (SP_IS_ITEM(o) && !SP_ITEM(o)->isLocked() && !(_snapmanager->getDesktop()->itemIsHidden(SP_ITEM(o)) && !clip_or_mask)) {
+        if (SP_IS_ITEM(o) && !SP_ITEM(o)->isLocked() && !(_snapmanager->getDesktop()->itemIsHidden(SP_ITEM(o)) && !clip_or_mask)) {
             // Don't snap to locked items, and
             // don't snap to hidden objects, unless they're a clipped path or a mask
             /* See if this item is on the ignore list */
@@ -249,7 +249,7 @@ void Inkscape::ObjectSnapper::_snapNodes(SnappedConstraints &sc,
     for (std::vector<Geom::Point>::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); k++) {
         Geom::Coord dist = Geom::L2(*k - p);        
         if (dist < getSnapperTolerance() && dist < s.getDistance()) {
-            s = SnappedPoint(*k, SNAPTARGET_NODE, dist, getSnapperTolerance(), getSnapperAlwaysSnap());
+            s = SnappedPoint(*k, SNAPTARGET_NODE, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), true);
             success = true;
         }
     }
@@ -278,7 +278,7 @@ void Inkscape::ObjectSnapper::_snapTranslatingGuideToNodes(SnappedConstraints &s
         Geom::Coord dist = Geom::L2(*k - p_proj); // distance from node to the guide         
         Geom::Coord dist2 = Geom::L2(p - p_proj); // distance from projection of node on the guide, to the mouse location
         if ((dist < tol && dist2 < tol || getSnapperAlwaysSnap()) && dist < s.getDistance()) {
-            s = SnappedPoint(*k, SNAPTARGET_NODE, dist, tol, getSnapperAlwaysSnap());
+            s = SnappedPoint(*k, SNAPTARGET_NODE, dist, tol, getSnapperAlwaysSnap(), true);
             success = true;
         }
     }
@@ -438,10 +438,10 @@ void Inkscape::ObjectSnapper::_snapPaths(SnappedConstraints &sc,
             std::vector<double>::const_iterator np = anp.begin();
             unsigned int index = 0;
             for (; np != anp.end(); np++, index++) {
-               Geom::Curve const *curve = &((*it_pv).at_index(index));
-               Geom::Point const sp_doc = curve->pointAt(*np);
-               
-               bool c1 = true;
+                Geom::Curve const *curve = &((*it_pv).at_index(index));
+                Geom::Point const sp_doc = curve->pointAt(*np);
+               
+                bool c1 = true;
                 bool c2 = true;                                
                 if (being_edited) {
                     /* If the path is being edited, then we should only snap though to stationary pieces of the path
@@ -460,7 +460,7 @@ void Inkscape::ObjectSnapper::_snapPaths(SnappedConstraints &sc,
                 if (!being_edited || (c1 && c2)) {
                     Geom::Coord const dist = Geom::distance(sp_doc, p_doc);
                     if (dist < getSnapperTolerance()) {
-                        sc.curves.push_back(Inkscape::SnappedCurve(from_2geom(sp_dt), dist, getSnapperTolerance(), getSnapperAlwaysSnap(), curve));   
+                        sc.curves.push_back(Inkscape::SnappedCurve(from_2geom(sp_dt), dist, getSnapperTolerance(), getSnapperAlwaysSnap(), false, curve));   
                     }
                 }
             }        
@@ -536,7 +536,7 @@ void Inkscape::ObjectSnapper::_snapPathsConstrained(SnappedConstraints &sc,
                         // 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), SNAPTARGET_PATH, dist, getSnapperTolerance(), getSnapperAlwaysSnap());
+                        SnappedPoint s(_snapmanager->getDesktop()->doc2dt(p_inters), SNAPTARGET_PATH, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), true);
                         sc.points.push_back(s);
                     }  
                 } 
index c4ed536e54d86ee7d11da1784326bec0e3e00f2b..394bc7f912bb6ba5b2ea524481806b1eaceee763 100644 (file)
@@ -206,7 +206,7 @@ Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType point_
                                              boost::optional<Geom::Rect> const &bbox_to_snap) const
 {
     if (!SomeSnapperMightSnap()) {
-        return Inkscape::SnappedPoint(p, Inkscape::SNAPTARGET_UNDEFINED, NR_HUGE, 0, false);
+        return Inkscape::SnappedPoint(p, Inkscape::SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false);
     }
     
     std::vector<SPItem const *> *items_to_ignore;
@@ -328,7 +328,7 @@ Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType
                                                     boost::optional<Geom::Rect> const &bbox_to_snap) const
 {
     if (!SomeSnapperMightSnap()) {
-        return Inkscape::SnappedPoint(p, Inkscape::SNAPTARGET_UNDEFINED, NR_HUGE, 0, false);
+        return Inkscape::SnappedPoint(p, Inkscape::SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false);
     }
     
     std::vector<SPItem const *> *items_to_ignore;
@@ -463,8 +463,6 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed(
     /* The current best metric for the best transformation; lower is better, NR_HUGE
     ** means that we haven't snapped anything.
     */
-    Geom::Coord best_metric = NR_HUGE;
-    Geom::Coord best_second_metric = NR_HUGE;
     Geom::Point best_scale_metric(NR_HUGE, NR_HUGE);
     Inkscape::SnappedPoint best_snapped_point;
     g_assert(best_snapped_point.getAlwaysSnap() == false); // Check initialization of snapped point
@@ -503,8 +501,6 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed(
         }
 
         Geom::Point result;
-        Geom::Coord metric = NR_HUGE;
-        Geom::Coord second_metric = NR_HUGE;
         Geom::Point scale_metric(NR_HUGE, NR_HUGE);
         
         if (snapped_point.getSnapped()) {
@@ -526,8 +522,8 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed(
                      * distance is defined as the distance to the nearest line of the intersection,
                      * and not to the intersection itself! 
                      */
-                    metric = snapped_point.getDistance(); //used to be: metric = Geom::L2(result);
-                    second_metric = snapped_point.getSecondDistance();
+                    // Only for translations, the relevant metric will be the real snapped distance,
+                    // so we don't have to do anything special here
                     break;
                 case SCALE:
                 {
@@ -561,18 +557,24 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed(
                            result[dim] = result[1-dim];
                         }
                     }
-                    metric = std::abs(result[dim] - transformation[dim]);
+                    // Store the metric for this transformation as a virtual distance
+                    snapped_point.setDistance(std::abs(result[dim] - transformation[dim]));
+                    snapped_point.setSecondDistance(NR_HUGE);
                     break;
                 case SKEW:
                     result[0] = (snapped_point.getPoint()[dim] - (*i)[dim]) / ((*i)[1 - dim] - origin[1 - dim]); // skew factor
                     result[1] = transformation[1]; // scale factor
-                    metric = std::abs(result[0] - transformation[0]);
+                    // Store the metric for this transformation as a virtual distance
+                    snapped_point.setDistance(std::abs(result[0] - transformation[0])); 
+                    snapped_point.setSecondDistance(NR_HUGE);
                     break;
                 default:
                     g_assert_not_reached();
             }
             
-            /* Note it if it's the best so far */
+            // When scaling, we're considering the best transformation in each direction separately. We will have a metric in each
+            // direction, whereas for all other transformation we only a single one-dimensional metric. That's why we need to handle
+            // the scaling metric differently
             if (transformation_type == SCALE) {
                 for (int index = 0; index < 2; index++) {
                     if (fabs(scale_metric[index]) < fabs(best_scale_metric[index])) {
@@ -594,41 +596,32 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed(
                         best_scale_metric[0] = best_scale_metric[1];
                     }
                 }
-                best_metric = std::min(best_scale_metric[0], best_scale_metric[1]);
-                // std::cout << "P_orig = " << (*i) << " | scale_metric = " << scale_metric << " | distance = " << snapped_point.getDistance() << " | P_snap = " << snapped_point.getPoint() << std::endl;
-            } else {
-                bool const c1 = metric < best_metric;
-                bool const c2 = metric == best_metric && snapped_point.getAtIntersection() == true && best_snapped_point.getAtIntersection() == false;
-                       bool const c3a = metric == best_metric && snapped_point.getAtIntersection() == true && best_snapped_point.getAtIntersection() == true;
-                bool const c3b = second_metric < best_second_metric;
-                bool const c4 = snapped_point.getAlwaysSnap() == true && best_snapped_point.getAlwaysSnap() == false;
-                bool const c4n = snapped_point.getAlwaysSnap() == false && best_snapped_point.getAlwaysSnap() == true;
-                
-                if ((c1 || c2 || (c3a && c3b) || c4) && !c4n) {
+            } else { // For all transformations other than scaling
+                if (best_snapped_point.isOtherOneBetter(snapped_point)) {
                     best_transformation = result;
-                    best_metric = metric;
-                    best_second_metric = second_metric;
-                    best_snapped_point = snapped_point; 
-                    // std::cout << "SEL ";
-                } // else { std::cout << "    ";}
-                // std::cout << "P_orig = " << (*i) << " | metric = " << metric << " | distance = " << snapped_point.getDistance() << " | second metric = " << second_metric << " | P_snap = " << snapped_point.getPoint() << std::endl;
+                    best_snapped_point = snapped_point;                    
+                }                
             }
         }
         
         j++;
     }
     
+    Geom::Coord best_metric;
     if (transformation_type == SCALE) {
         // When scaling, don't ever exit with one of scaling components set to NR_HUGE
         for (int index = 0; index < 2; index++) {
             if (best_transformation[index] == NR_HUGE) {
                 if (uniform && best_transformation[1-index] < NR_HUGE) {
-                       best_transformation[index] = best_transformation[1-index];
+                    best_transformation[index] = best_transformation[1-index];
                 } else {
-                       best_transformation[index] = transformation[index];     
+                    best_transformation[index] = transformation[index];    
                 }
             }
         }
+        best_metric = std::min(best_scale_metric[0], best_scale_metric[1]);
+    } else { // For all transformations other than scaling
+        best_metric = best_snapped_point.getDistance();        
     }
     
     best_snapped_point.setTransformation(best_transformation);
@@ -769,14 +762,16 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapSkew(Inkscape::Snapper::Point
 
 Inkscape::SnappedPoint SnapManager::findBestSnap(Geom::Point const &p, SnappedConstraints &sc, bool constrained) const
 {
+    
     /*
     std::cout << "Type and number of snapped constraints: " << std::endl;
     std::cout << "  Points      : " << sc.points.size() << std::endl;
     std::cout << "  Lines       : " << sc.lines.size() << std::endl;
     std::cout << "  Grid lines  : " << sc.grid_lines.size()<< std::endl;
     std::cout << "  Guide lines : " << sc.guide_lines.size()<< std::endl;
+    std::cout << "  Curves      : " << sc.curves.size()<< std::endl;
     */
-        
+    
     // Store all snappoints
     std::list<Inkscape::SnappedPoint> sp_list;
     
@@ -793,11 +788,11 @@ Inkscape::SnappedPoint SnapManager::findBestSnap(Geom::Point const &p, SnappedCo
     }
     
     if (_intersectionCS) {
-           // search for the closest snapped intersection of curves
-           Inkscape::SnappedPoint closestCurvesIntersection;
-           if (getClosestIntersectionCS(sc.curves, p, closestCurvesIntersection)) {
-               sp_list.push_back(closestCurvesIntersection);
-           }
+        // search for the closest snapped intersection of curves
+        Inkscape::SnappedPoint closestCurvesIntersection;
+        if (getClosestIntersectionCS(sc.curves, p, closestCurvesIntersection)) {
+            sp_list.push_back(closestCurvesIntersection);
+        }
     }    
 
     // search for the closest snapped grid line
@@ -837,38 +832,29 @@ Inkscape::SnappedPoint SnapManager::findBestSnap(Geom::Point const &p, SnappedCo
         
         // search for the closest snapped intersection of grid with guide lines
         if (_intersectionGG) {
-           Inkscape::SnappedPoint closestGridGuidePoint;
-           if (getClosestIntersectionSL(sc.grid_lines, sc.guide_lines, closestGridGuidePoint)) {
-               closestGridGuidePoint.setTarget(Inkscape::SNAPTARGET_GRID_GUIDE_INTERSECTION);
+            Inkscape::SnappedPoint closestGridGuidePoint;
+            if (getClosestIntersectionSL(sc.grid_lines, sc.guide_lines, closestGridGuidePoint)) {
+                closestGridGuidePoint.setTarget(Inkscape::SNAPTARGET_GRID_GUIDE_INTERSECTION);
                 sp_list.push_back(closestGridGuidePoint);
-           }
+            }
         }
     }
     
     // now let's see which snapped point gets a thumbs up
-    Inkscape::SnappedPoint bestSnappedPoint = Inkscape::SnappedPoint(p, Inkscape::SNAPTARGET_UNDEFINED, NR_HUGE, 0, false);
+    Inkscape::SnappedPoint bestSnappedPoint = Inkscape::SnappedPoint(p, Inkscape::SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false);
+    // std::cout << "Finding the best snap..." << std::endl;
     for (std::list<Inkscape::SnappedPoint>::const_iterator i = sp_list.begin(); i != sp_list.end(); i++) {
-               // first find out if this snapped point is within snapping range
+        // first find out if this snapped point is within snapping range
+        // std::cout << "sp = " << from_2geom((*i).getPoint());
         if ((*i).getDistance() <= (*i).getTolerance()) {
-               // if it's the first point
-               bool c1 = (i == sp_list.begin());  
-               // or, if it's closer
-               bool c2 = (*i).getDistance() < bestSnappedPoint.getDistance();
-            // or, if it's for a snapper with "always snap" turned on, and the previous wasn't
-            bool c3 = (*i).getAlwaysSnap() && !bestSnappedPoint.getAlwaysSnap();
-               // But in no case fall back from a snapper with "always snap" on to one with "always snap" off
-            bool c3n = !(*i).getAlwaysSnap() && bestSnappedPoint.getAlwaysSnap();
-            // or, if it's just as close then consider the second distance
-               // (which is only relevant for points at an intersection)
-               bool c4a = ((*i).getDistance() == bestSnappedPoint.getDistance()); 
-               bool c4b = (*i).getSecondDistance() < bestSnappedPoint.getSecondDistance();
-               // then prefer this point over the previous one
-            if ((c1 || c2 || c3 || (c4a && c4b)) && !c3n) {
+            // if it's the first point, or if it is closer than the best snapped point so far
+            if (i == sp_list.begin() || bestSnappedPoint.isOtherOneBetter(*i)) { 
+                // then prefer this point over the previous one
                 bestSnappedPoint = *i;
             }
         }
-    }
-    
+        // std::cout << std::endl;
+    }    
     
     // Update the snap indicator, if requested
     if (_snapindicator) {
@@ -886,7 +872,7 @@ Inkscape::SnappedPoint SnapManager::findBestSnap(Geom::Point const &p, SnappedCo
 void SnapManager::setup(SPDesktop const *desktop, bool snapindicator, SPItem const *item_to_ignore, std::vector<Geom::Point> *unselected_nodes)
 {
     g_assert(desktop != NULL);
-       _item_to_ignore = item_to_ignore;
+    _item_to_ignore = item_to_ignore;
     _items_to_ignore = NULL;
     _desktop = desktop;
     _snapindicator = snapindicator;
@@ -895,8 +881,8 @@ void SnapManager::setup(SPDesktop const *desktop, bool snapindicator, SPItem con
 
 void SnapManager::setup(SPDesktop const *desktop, bool snapindicator, std::vector<SPItem const *> &items_to_ignore, std::vector<Geom::Point> *unselected_nodes)
 {
-       g_assert(desktop != NULL);
-       _item_to_ignore = NULL;
+    g_assert(desktop != NULL);
+    _item_to_ignore = NULL;
     _items_to_ignore = &items_to_ignore;
     _desktop = desktop;
     _snapindicator = snapindicator;
@@ -905,7 +891,7 @@ void SnapManager::setup(SPDesktop const *desktop, bool snapindicator, std::vecto
 
 SPDocument *SnapManager::getDocument() const
 {
-       return _named_view->document;
+    return _named_view->document;
 }
 
 /*
index dfed84531d807e3bb23736d4f6a5bf24333dedb1..3a6512e5e0522097316d175d864565ed23aa5119 100644 (file)
-/**\r
- *    \file src/snapped-curve.cpp\r
- *    \brief SnappedCurve class.\r
- *\r
- *    Authors:\r
- *      Diederik van Lierop <mail@diedenrezi.nl>\r
- *\r
- *    Released under GNU GPL, read the file 'COPYING' for more information.\r
- */\r
-\r
-#include "snapped-curve.h"\r
-#include "libnr/nr-values.h"\r
-#include <2geom/crossing.h>\r
-#include <2geom/path-intersection.h>\r
-#include <libnr/nr-convert2geom.h>\r
-\r
-// These two are needed for SP_ACTIVE_DESKTOP; this is a dirty hack\r
-#include "desktop.h"\r
-#include "inkscape.h"\r
-\r
-Inkscape::SnappedCurve::SnappedCurve(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, Geom::Coord const &snapped_tolerance, bool const &always_snap, Geom::Curve const *curve)\r
-{\r
-       _distance = snapped_distance;\r
-    _tolerance = snapped_tolerance;\r
-    _always_snap = always_snap;\r
-    _curve = curve;\r
-       _second_distance = NR_HUGE;\r
-    _second_tolerance = 0;\r
-    _second_always_snap = false;\r
-       _point = snapped_point;\r
-       _at_intersection = false;\r
-}\r
-\r
-Inkscape::SnappedCurve::SnappedCurve() \r
-{\r
-       _distance = NR_HUGE;\r
-    _tolerance = 0;\r
-    _always_snap = false;\r
-    _curve = NULL;\r
-       _second_distance = NR_HUGE;\r
-    _second_tolerance = 0;\r
-    _second_always_snap = false;\r
-       _point = Geom::Point(0,0);\r
-       _at_intersection = false;\r
-}\r
-\r
-Inkscape::SnappedCurve::~SnappedCurve()\r
-{\r
-}\r
-\r
-Inkscape::SnappedPoint Inkscape::SnappedCurve::intersect(SnappedCurve const &curve, Geom::Point const &p) const \r
-{\r
-    // Calculate the intersections of two curves, which are both within snapping range, and\r
-    // return only the closest intersection\r
-    // The point of intersection should be considered for snapping, but might be outside the snapping range\r
-    // PS: We need p (the location of the mouse pointer) for find out which intersection is the\r
-    // closest, as there might be multiple intersections of two curves\r
-    Geom::Crossings cs = crossings(*(this->_curve), *(curve._curve));\r
-     \r
-    if (cs.size() > 0) {\r
-        // There might be multiple intersections: find the closest\r
-        Geom::Coord best_dist = NR_HUGE;\r
-        Geom::Point best_p = Geom::Point(NR_HUGE, NR_HUGE);\r
-        for (std::vector<Geom::Crossing>::const_iterator i = cs.begin(); i != cs.end(); i++) {\r
-            Geom::Point p_ix = this->_curve->pointAt((*i).ta);\r
-            Geom::Coord dist = Geom::distance(p_ix, p);\r
-            if (dist < best_dist) {\r
-                best_dist = dist;\r
-                best_p = p_ix;\r
-            }\r
-        }\r
-        \r
-        // Now we've found the closests intersection, return it as a SnappedPoint\r
-        bool const use_this_as_primary = _distance < curve.getDistance();\r
-        Inkscape::SnappedCurve const *primaryC = use_this_as_primary ? this : &curve;\r
-        Inkscape::SnappedCurve const *secondaryC = use_this_as_primary ? &curve : this;\r
-        // The intersection should in fact be returned in desktop coordinates, but for this\r
-        // we need a desktop: this is a dirty hack\r
-        SPDesktop const *desktop = SP_ACTIVE_DESKTOP;\r
-        best_p = desktop->dt2doc(best_p);\r
-        // TODO: Investigate whether it is possible to use document coordinates everywhere\r
-        // in the snapper code. Only the mouse position should be in desktop coordinates, I guess.\r
-        // All paths are already in document coords and we are certainly not going to change THAT.\r
-        return SnappedPoint(from_2geom(best_p), Inkscape::SNAPTARGET_PATH_INTERSECTION, primaryC->getDistance(), primaryC->getTolerance(), primaryC->getAlwaysSnap(), true, \r
-                                          secondaryC->getDistance(), secondaryC->getTolerance(), secondaryC->getAlwaysSnap());\r
-    }\r
-    \r
-    // No intersection\r
-    return SnappedPoint(Geom::Point(NR_HUGE, NR_HUGE), SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false, NR_HUGE, 0, false);\r
-}\r
-\r
-// search for the closest snapped line\r
-bool getClosestCurve(std::list<Inkscape::SnappedCurve> const &list, Inkscape::SnappedCurve &result) \r
-{\r
-    bool success = false;\r
-    \r
-    for (std::list<Inkscape::SnappedCurve>::const_iterator i = list.begin(); i != list.end(); i++) {\r
-        if ((i == list.begin()) || (*i).getDistance() < result.getDistance()) {\r
-            result = *i;\r
-            success = true;\r
-        }   \r
-    }\r
-    \r
-    return success; \r
-}\r
-\r
-// search for the closest intersection of two snapped curves, which are both member of the same collection\r
-bool getClosestIntersectionCS(std::list<Inkscape::SnappedCurve> const &list, Geom::Point const &p, Inkscape::SnappedPoint &result)\r
-{\r
-    bool success = false;\r
-    \r
-    for (std::list<Inkscape::SnappedCurve>::const_iterator i = list.begin(); i != list.end(); i++) {\r
-        std::list<Inkscape::SnappedCurve>::const_iterator j = i;\r
-        j++;\r
-        for (; j != list.end(); j++) {\r
-            Inkscape::SnappedPoint sp = (*i).intersect(*j, p);\r
-            if (sp.getAtIntersection()) {\r
-                // if it's the first point\r
-                bool const c1 = !success;\r
-                // or, if it's closer             \r
-                bool const c2 = sp.getDistance() < result.getDistance();\r
-                // or, if it's just then look at the other distance \r
-                // (only relevant for snapped points which are at an intersection\r
-                bool const c3 = (sp.getDistance() == result.getDistance()) && (sp.getSecondDistance() < result.getSecondDistance()); \r
-                // then prefer this point over the previous one\r
-                if (c1 || c2 || c3) {  \r
-                    result = sp;\r
-                    success = true;\r
-                }\r
-            }               \r
-        }\r
-    }\r
-    \r
-    return success; \r
-}\r
-/*\r
-  Local Variables:\r
-  mode:c++\r
-  c-file-style:"stroustrup"\r
-  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
-  indent-tabs-mode:nil\r
-  fill-column:99\r
-  End:\r
-*/\r
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r
+/**
+ *    \file src/snapped-curve.cpp
+ *    \brief SnappedCurve class.
+ *
+ *    Authors:
+ *      Diederik van Lierop <mail@diedenrezi.nl>
+ *
+ *    Released under GNU GPL, read the file 'COPYING' for more information.
+ */
+
+#include "snapped-curve.h"
+#include "libnr/nr-values.h"
+#include <2geom/crossing.h>
+#include <2geom/path-intersection.h>
+#include <libnr/nr-convert2geom.h>
+
+// These two are needed for SP_ACTIVE_DESKTOP; this is a dirty hack
+#include "desktop.h"
+#include "inkscape.h"
+
+Inkscape::SnappedCurve::SnappedCurve(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, Geom::Coord const &snapped_tolerance, bool const &always_snap, bool const &fully_constrained, Geom::Curve const *curve)
+{
+       _distance = snapped_distance;
+    _tolerance = snapped_tolerance;
+    _always_snap = always_snap;
+    _curve = curve;
+    _second_distance = NR_HUGE;
+    _second_tolerance = 0;
+    _second_always_snap = false;
+    _point = snapped_point;
+    _at_intersection = false;
+    _fully_constrained = fully_constrained;
+}
+
+Inkscape::SnappedCurve::SnappedCurve() 
+{
+    _distance = NR_HUGE;
+    _tolerance = 0;
+    _always_snap = false;
+    _curve = NULL;
+    _second_distance = NR_HUGE;
+    _second_tolerance = 0;
+    _second_always_snap = false;
+    _point = Geom::Point(0,0);
+    _at_intersection = false;
+    _fully_constrained = false;
+}
+
+Inkscape::SnappedCurve::~SnappedCurve()
+{
+}
+
+Inkscape::SnappedPoint Inkscape::SnappedCurve::intersect(SnappedCurve const &curve, Geom::Point const &p) const 
+{
+    // Calculate the intersections of two curves, which are both within snapping range, and
+    // return only the closest intersection
+    // The point of intersection should be considered for snapping, but might be outside the snapping range
+    // PS: We need p (the location of the mouse pointer) for find out which intersection is the
+    // closest, as there might be multiple intersections of two curves
+    Geom::Crossings cs = crossings(*(this->_curve), *(curve._curve));
+     
+    if (cs.size() > 0) {
+        // There might be multiple intersections: find the closest
+        Geom::Coord best_dist = NR_HUGE;
+        Geom::Point best_p = Geom::Point(NR_HUGE, NR_HUGE);
+        for (std::vector<Geom::Crossing>::const_iterator i = cs.begin(); i != cs.end(); i++) {
+            Geom::Point p_ix = this->_curve->pointAt((*i).ta);
+            Geom::Coord dist = Geom::distance(p_ix, p);
+            if (dist < best_dist) {
+                best_dist = dist;
+                best_p = p_ix;
+            }
+        }
+        
+        // Now we've found the closests intersection, return it as a SnappedPoint
+        bool const use_this_as_primary = _distance < curve.getDistance();
+        Inkscape::SnappedCurve const *primaryC = use_this_as_primary ? this : &curve;
+        Inkscape::SnappedCurve const *secondaryC = use_this_as_primary ? &curve : this;
+        // The intersection should in fact be returned in desktop coordinates, but for this
+        // we need a desktop: this is a dirty hack
+        SPDesktop const *desktop = SP_ACTIVE_DESKTOP;
+        best_p = desktop->dt2doc(best_p);
+        // TODO: Investigate whether it is possible to use document coordinates everywhere
+        // in the snapper code. Only the mouse position should be in desktop coordinates, I guess.
+        // All paths are already in document coords and we are certainly not going to change THAT.
+        return SnappedPoint(from_2geom(best_p), Inkscape::SNAPTARGET_PATH_INTERSECTION, primaryC->getDistance(), primaryC->getTolerance(), primaryC->getAlwaysSnap(), true, true,
+                                          secondaryC->getDistance(), secondaryC->getTolerance(), secondaryC->getAlwaysSnap());
+    }
+    
+    // No intersection
+    return SnappedPoint(Geom::Point(NR_HUGE, NR_HUGE), SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false, false, NR_HUGE, 0, false);
+}
+
+// search for the closest snapped line
+bool getClosestCurve(std::list<Inkscape::SnappedCurve> const &list, Inkscape::SnappedCurve &result) 
+{
+    bool success = false;
+    
+    for (std::list<Inkscape::SnappedCurve>::const_iterator i = list.begin(); i != list.end(); i++) {
+        if ((i == list.begin()) || (*i).getDistance() < result.getDistance()) {
+            result = *i;
+            success = true;
+        }   
+    }
+    
+    return success; 
+}
+
+// search for the closest intersection of two snapped curves, which are both member of the same collection
+bool getClosestIntersectionCS(std::list<Inkscape::SnappedCurve> const &list, Geom::Point const &p, Inkscape::SnappedPoint &result)
+{
+    bool success = false;
+    
+    for (std::list<Inkscape::SnappedCurve>::const_iterator i = list.begin(); i != list.end(); i++) {
+        std::list<Inkscape::SnappedCurve>::const_iterator j = i;
+        j++;
+        for (; j != list.end(); j++) {
+            Inkscape::SnappedPoint sp = (*i).intersect(*j, p);
+            if (sp.getAtIntersection()) {
+                // if it's the first point
+                bool const c1 = !success;
+                // or, if it's closer             
+                bool const c2 = sp.getDistance() < result.getDistance();
+                // or, if it's just then look at the other distance 
+                // (only relevant for snapped points which are at an intersection
+                bool const c3 = (sp.getDistance() == result.getDistance()) && (sp.getSecondDistance() < result.getSecondDistance()); 
+                // then prefer this point over the previous one
+                if (c1 || c2 || c3) {  
+                    result = sp;
+                    success = true;
+                }
+            }               
+        }
+    }
+    
+    return success; 
+}
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
index e6e71e6481dc3f906f63378e7c2d9d1a62ad933e..814777b68953c2f97470e37fb1282552ea9d4190 100644 (file)
@@ -27,7 +27,7 @@ class SnappedCurve : public SnappedPoint
 {\r
 public:\r
     SnappedCurve();\r
-    SnappedCurve(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, Geom::Coord const &snapped_tolerance, bool const &always_snap, Geom::Curve const *curve);\r
+    SnappedCurve(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, Geom::Coord const &snapped_tolerance, bool const &always_snap, bool const &fully_constrained, Geom::Curve const *curve);\r
     ~SnappedCurve();\r
     Inkscape::SnappedPoint intersect(SnappedCurve const &curve, Geom::Point const &p) const; //intersect with another SnappedCurve\r
     \r
index f35c7a81ccf1566eb9106ee47bee178491b438f2..fc2d059dca4c82e81529344d8a22346b9f0f8056 100644 (file)
@@ -67,12 +67,12 @@ Inkscape::SnappedPoint Inkscape::SnappedLineSegment::intersect(SnappedLineSegmen
         bool const use_this_as_primary = c1 || c2;
         Inkscape::SnappedLineSegment const *primarySLS = use_this_as_primary ? this : &line;
         Inkscape::SnappedLineSegment const *secondarySLS = use_this_as_primary ? &line : this;
-        return SnappedPoint(intersection, SNAPTARGET_PATH_INTERSECTION, primarySLS->getDistance(), primarySLS->getTolerance(), primarySLS->getAlwaysSnap(), true, 
+        return SnappedPoint(intersection, SNAPTARGET_PATH_INTERSECTION, primarySLS->getDistance(), primarySLS->getTolerance(), primarySLS->getAlwaysSnap(), true, true,
                                           secondarySLS->getDistance(), secondarySLS->getTolerance(), secondarySLS->getAlwaysSnap());
        }
     
     // No intersection
-    return SnappedPoint(intersection, SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false, NR_HUGE, 0, false);
+    return SnappedPoint(intersection, SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false, false, NR_HUGE, 0, false);
 };
 
 
@@ -142,14 +142,14 @@ Inkscape::SnappedPoint Inkscape::SnappedLine::intersect(SnappedLine const &line)
         bool const use_this_as_primary = c1 || c2;
         Inkscape::SnappedLine const *primarySL = use_this_as_primary ? this : &line;
         Inkscape::SnappedLine const *secondarySL = use_this_as_primary ? &line : this;
-        return SnappedPoint(intersection, Inkscape::SNAPTARGET_UNDEFINED, primarySL->getDistance(), primarySL->getTolerance(), primarySL->getAlwaysSnap(), true, 
+        return SnappedPoint(intersection, Inkscape::SNAPTARGET_UNDEFINED, primarySL->getDistance(), primarySL->getTolerance(), primarySL->getAlwaysSnap(), true, true,
                                           secondarySL->getDistance(), secondarySL->getTolerance(), secondarySL->getAlwaysSnap());
         // The type of the snap target is yet undefined, as we cannot tell whether 
         // we're snapping to grid or the guide lines; must be set by on a higher level                                          
     }
     
     // No intersection
-    return SnappedPoint(intersection, SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false, NR_HUGE, 0, false);
+    return SnappedPoint(intersection, SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false, false, NR_HUGE, 0, false);
 }
 
 // search for the closest snapped line segment
index 3a3595a809c7021c4b987e44282c6e5514772bc0..4176d4a3d40bade1cce8e966deb78259f0c62c47 100644 (file)
 #include "snapped-point.h"
 
 // overloaded constructor
-Inkscape::SnappedPoint::SnappedPoint(Geom::Point const &p, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a)
+Inkscape::SnappedPoint::SnappedPoint(Geom::Point const &p, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a, bool const &fully_constrained)
     : _point(p), _target(target), _distance(d), _tolerance(t), _always_snap(a)
 {
     _at_intersection = false;
+    _fully_constrained = fully_constrained;
     _second_distance = NR_HUGE;
     _second_tolerance = 0;
     _second_always_snap = false;
     _transformation = Geom::Point(1,1);
 }
 
-Inkscape::SnappedPoint::SnappedPoint(Geom::Point const &p, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a, bool const &at_intersection, Geom::Coord const &d2, Geom::Coord const &t2, bool const &a2)
-    : _point(p), _target(target), _at_intersection(at_intersection), _distance(d), _tolerance(t), _always_snap(a),
+Inkscape::SnappedPoint::SnappedPoint(Geom::Point const &p, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a, bool const &at_intersection, bool const &fully_constrained, Geom::Coord const &d2, Geom::Coord const &t2, bool const &a2)
+    : _point(p), _target(target), _at_intersection(at_intersection), _fully_constrained(fully_constrained), _distance(d), _tolerance(t), _always_snap(a),
     _second_distance(d2), _second_tolerance(t2), _second_always_snap(a2)
 {
     _transformation = Geom::Point(1,1);
@@ -104,6 +105,27 @@ bool getClosestSP(std::list<Inkscape::SnappedPoint> &list, Inkscape::SnappedPoin
     return success;
 }
 
+bool Inkscape::SnappedPoint::isOtherOneBetter(Inkscape::SnappedPoint const &other_one) const
+{
+    // If it's closer
+    bool c2 = other_one.getDistance() < getDistance();
+    // or, if it's for a snapper with "always snap" turned on, and the previous wasn't
+    bool c3 = other_one.getAlwaysSnap() && !getAlwaysSnap();
+    // But in no case fall back from a snapper with "always snap" on to one with "always snap" off
+    bool c3n = !other_one.getAlwaysSnap() && getAlwaysSnap();
+    // or, if we have a fully constrained snappoint (e.g. to a node), while the previous one was only partly constrained (e.g. to a line)
+    bool c4 = other_one.getFullyConstrained() && !getFullyConstrained();
+    // But in no case fall back; (has less priority than c3n, so it is allowed to fall back when c3 is true, see below)       
+    bool c4n = !other_one.getFullyConstrained() && getFullyConstrained(); 
+    // or, if it's just as close then consider the second distance
+    // (which is only relevant for points at an intersection)
+    bool c5a = (other_one.getDistance() == getDistance()); 
+    bool c5b = other_one.getSecondDistance() < getSecondDistance();
+    
+    // std::cout << "c2 = " << c2 << " | c3 = " << c3 << " | c3n = " << c3n << " | c4 = " << c4 << " | c4n = " << c4n << " | c5a = " << c5a << " | c5b = " << c5b;
+    return (c2 || c3 || c4 || (c5a && c5b)) && !c3n && (!c4n || c3);       
+}
+
 /*
   Local Variables:
   mode:c++
index 4999a2aaee16b2f121269a1e2c2395e856d7f097..575fd0e1cd3d6a650cba0974ab6bfabe92e3fc96 100644 (file)
@@ -42,8 +42,8 @@ class SnappedPoint
 
 public:
     SnappedPoint();
-    SnappedPoint(Geom::Point const &p, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a, bool const &at_intersection, Geom::Coord const &d2, Geom::Coord const &t2, bool const &a2);
-    SnappedPoint(Geom::Point const &p, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a);
+    SnappedPoint(Geom::Point const &p, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a, bool const &at_intersection, bool const &fully_constrained, Geom::Coord const &d2, Geom::Coord const &t2, bool const &a2);
+    SnappedPoint(Geom::Point const &p, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a, bool const &fully_constrained);
     ~SnappedPoint();
 
     Geom::Coord getDistance() const;
@@ -51,11 +51,12 @@ public:
     Geom::Coord getTolerance() const;
     bool getAlwaysSnap() const;
     Geom::Coord getSecondDistance() const;
+    void setSecondDistance(Geom::Coord const d) {_second_distance = d;}
     Geom::Coord getSecondTolerance() const;
     bool getSecondAlwaysSnap() const;
     
-    /* This is the preferred method to find out which point we have snapped,
-     * to because it only returns a point if snapping has actually occured
+    /* This is the preferred method to find out which point we have snapped
+     * to, because it only returns a point if snapping has actually occured
      * (by overwriting p)
      */ 
     void getPoint(Geom::Point &p) const;
@@ -69,16 +70,21 @@ public:
     Geom::Point getPoint() const {return _point;}
      
     bool getAtIntersection() const {return _at_intersection;}
+    bool getFullyConstrained() const {return _fully_constrained;}        
     bool getSnapped() const {return _distance < NR_HUGE;}
     Geom::Point getTransformation() const {return _transformation;}
     void setTransformation(Geom::Point const t) {_transformation = t;}
     void setTarget(SnapTargetType const target) {_target = target;}
     SnapTargetType getTarget() {return _target;}
     
+    bool isOtherOneBetter(SnappedPoint const &other_one) const;
+    
 protected:
     Geom::Point _point; // Location of the snapped point
     SnapTargetType _target; // Describes to what we've snapped to
-    bool _at_intersection; // If true, the snapped point is at an intersection 
+    bool _at_intersection; // If true, the snapped point is at an intersection
+    bool _fully_constrained; // When snapping for example to a node, then the snap will be "fully constrained". 
+                            // When snapping to a line however, the snap is only partly constrained (i.e. only in one dimension)
     
     /* Distance from original point to snapped point. If the snapped point is at
        an intersection of e.g. two lines, then this is the distance to the closest