Code

1) fix snapping while scaling
authordvlierop2 <dvlierop2@users.sourceforge.net>
Thu, 3 Jan 2008 21:37:41 +0000 (21:37 +0000)
committerdvlierop2 <dvlierop2@users.sourceforge.net>
Thu, 3 Jan 2008 21:37:41 +0000 (21:37 +0000)
2) fix constrained snapping
3) improve snapping logic (again), or more specifically: better obey "always snap"
4) refactoring to reduce risk of bugs: renaming variables and methods for better readability, adding class members instead of using std::pairs, etc.

13 files changed:
src/desktop.cpp
src/display/canvas-axonomgrid.cpp
src/display/canvas-grid.cpp
src/guide-snapper.cpp
src/line-snapper.cpp
src/object-snapper.cpp
src/snap.cpp
src/snapped-line.cpp
src/snapped-line.h
src/snapped-point.cpp
src/snapped-point.h
src/snapper.cpp
src/snapper.h

index feb3f5cb0287ece3a51a8a05af074332473f98c4..e52d2b50c2a7c1f95e2c669e8a53456a36d3111b 100644 (file)
@@ -1535,15 +1535,15 @@ _update_snap_distances (SPDesktop *desktop)
     //tell all grid snappers
     for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
         Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
-        grid->snapper->setDistance(sp_convert_distance_full(nv.gridtolerance,
+        grid->snapper->setSnapperTolerance(sp_convert_distance_full(nv.gridtolerance,
                                                                       *nv.gridtoleranceunit,
                                                                       px));
     }
 
-    nv.snap_manager.guide.setDistance(sp_convert_distance_full(nv.guidetolerance,
+    nv.snap_manager.guide.setSnapperTolerance(sp_convert_distance_full(nv.guidetolerance,
                                                                        *nv.guidetoleranceunit,
                                                                        px));
-    nv.snap_manager.object.setDistance(sp_convert_distance_full(nv.objecttolerance,
+    nv.snap_manager.object.setSnapperTolerance(sp_convert_distance_full(nv.objecttolerance,
                                                                         *nv.objecttoleranceunit,
                                                                         px));
 }
index 3d9375d2d1f0001538d57cc9c89c119e218341a1..c64887d1c91e104faf19e79b3778f55345c1d150 100644 (file)
@@ -709,7 +709,7 @@ CanvasAxonomGridSnapper::_getSnapLines(NR::Point const &p) const
 
 void CanvasAxonomGridSnapper::_addSnappedLine(SnappedConstraints &sc, NR::Point const snapped_point, NR::Coord const snapped_distance, NR::Point const normal_to_line, NR::Point const point_on_line) const 
 {
-    SnappedLine dummy = SnappedLine(snapped_point, snapped_distance, normal_to_line, point_on_line);
+    SnappedLine dummy = SnappedLine(snapped_point, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), normal_to_line, point_on_line);
     sc.grid_lines.push_back(dummy);
 }
 
index 15a6452d3c94765cf295522b62c63be847ffbf85..20856709cfdfcd3251004f1ed1ec597289724cfa 100644 (file)
@@ -921,7 +921,7 @@ CanvasXYGridSnapper::_getSnapLines(NR::Point const &p) const
 
 void CanvasXYGridSnapper::_addSnappedLine(SnappedConstraints &sc, NR::Point const snapped_point, NR::Coord const snapped_distance, NR::Point const normal_to_line, NR::Point const point_on_line) const 
 {
-    SnappedLine dummy = SnappedLine(snapped_point, snapped_distance, normal_to_line, point_on_line);
+    SnappedLine dummy = SnappedLine(snapped_point, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), normal_to_line, point_on_line);
     sc.grid_lines.push_back(dummy);
 }
 
index fb16382d26eddc2862e6171be9d895e286991065..41e2cdb0708bc480366cda9317277bb9570ec661 100644 (file)
@@ -48,7 +48,7 @@ bool Inkscape::GuideSnapper::ThisSnapperMightSnap() const
 
 void Inkscape::GuideSnapper::_addSnappedLine(SnappedConstraints &sc, NR::Point const snapped_point, NR::Coord const snapped_distance, NR::Point const normal_to_line, NR::Point const point_on_line) const
 {
-    SnappedLine dummy = SnappedLine(snapped_point, snapped_distance, normal_to_line, point_on_line);
+    SnappedLine dummy = SnappedLine(snapped_point, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), normal_to_line, point_on_line);
     sc.guide_lines.push_back(dummy);
 }
 
index 7fc6a32a473db511b5c3e49a2c82a10d628010a2..a84a802961fe04ed40a6bd9411bcb9e51bb637d5 100644 (file)
@@ -30,8 +30,6 @@ void Inkscape::LineSnapper::_doFreeSnap(SnappedConstraints &sc,
                                                     std::vector<NR::Point> &points_to_snap,
                                                     std::list<SPItem const *> const &it) const
 {
-    Inkscape::SnappedPoint s = SnappedPoint(p, NR_HUGE);
-
     /* Get the lines that we will try to snap to */
     const LineList lines = _getSnapLines(p);
 
@@ -46,7 +44,7 @@ void Inkscape::LineSnapper::_doFreeSnap(SnappedConstraints &sc,
         NR::Point const p_proj = project_on_linesegment(p, p1, p2);
         NR::Coord const dist = NR::L2(p_proj - p);
         //Store any line that's within snapping range
-        if (dist < getDistance()) {
+        if (dist < getSnapperTolerance()) {
             _addSnappedLine(sc, p_proj, dist, i->first, i->second);
             // std::cout << " -> distance = " << dist; 
         }     
@@ -63,36 +61,35 @@ void Inkscape::LineSnapper::_doConstrainedSnap(SnappedConstraints &sc,
                                                std::list<SPItem const *> const &/*it*/) const
 
 {
-    Inkscape::SnappedPoint s = SnappedPoint(p, NR_HUGE);
-
     /* Get the lines that we will try to snap to */
     const LineList lines = _getSnapLines(p);
 
     for (LineList::const_iterator i = lines.begin(); i != lines.end(); i++) {
-
-        /* Normal to the line we're trying to snap along */
-        NR::Point const n(NR::rot90(NR::unit_vector(c.getDirection())));
-
-        NR::Point const point_on_line = c.hasPoint() ? c.getPoint() : p;
-
-        /* Constant term of the line we're trying to snap along */
-        NR::Coord const q0 = dot(n, point_on_line);
-        /* Constant term of the grid or guide line */
-        NR::Coord const q1 = dot(i->first, i->second);        
-
-        /* Try to intersect this line with the target line */
-        Geom::Point t_2geom(NR_HUGE, NR_HUGE);
-        Geom::IntersectorKind const k = Geom::line_intersection(n.to_2geom(), q0, i->first.to_2geom(), q1, t_2geom);
-        NR::Point t(t_2geom);
-
-        if (k == Geom::intersects) {
-            const NR::Coord dist = L2(t - p);
-            if (dist < getDistance()) {
-                               // 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
-                sc.points.push_back(SnappedPoint(t, dist)); 
+        if (NR::L2(c.getDirection()) > 0) { // Can't do a constrained snap without a constraint
+            /* Normal to the line we're trying to snap along */
+            NR::Point const n(NR::rot90(NR::unit_vector(c.getDirection())));
+    
+            NR::Point const point_on_line = c.hasPoint() ? c.getPoint() : p;
+    
+            /* Constant term of the line we're trying to snap along */
+            NR::Coord const q0 = dot(n, point_on_line);
+            /* Constant term of the grid or guide line */
+            NR::Coord const q1 = dot(i->first, i->second);        
+    
+            /* Try to intersect this line with the target line */
+            Geom::Point t_2geom(NR_HUGE, NR_HUGE);
+            Geom::IntersectorKind const k = Geom::line_intersection(n.to_2geom(), q0, i->first.to_2geom(), q1, t_2geom);
+            NR::Point t(t_2geom);
+    
+            if (k == Geom::intersects) {
+                const NR::Coord dist = L2(t - p);
+                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
+                    sc.points.push_back(SnappedPoint(t, dist, getSnapperTolerance(), getSnapperAlwaysSnap())); 
+                }
             }
         }
     }
index c03884ffd05880a14a89e6981262c1232aea2ae3..9ffdc764cbde7a79358143911758f764591f052e 100644 (file)
@@ -98,9 +98,9 @@ void Inkscape::ObjectSnapper::_findCandidates(SPObject* r,
                             for (std::vector<NR::Point>::const_iterator i = points_to_snap.begin(); i != points_to_snap.end(); i++) {
                                 NR::Point b_min = b->min();
                                 NR::Point b_max = b->max();
-                                double d = getDistance();
-                                bool withinX = ((*i)[NR::X] >= b_min[NR::X] - d) && ((*i)[NR::X] <= b_max[NR::X] + d);
-                                bool withinY = ((*i)[NR::Y] >= b_min[NR::Y] - d) && ((*i)[NR::Y] <= b_max[NR::Y] + d);
+                                NR::Coord t = getSnapperTolerance();
+                                bool withinX = ((*i)[NR::X] >= b_min[NR::X] - t) && ((*i)[NR::X] <= b_max[NR::X] + t);
+                                bool withinY = ((*i)[NR::Y] >= b_min[NR::Y] - t) && ((*i)[NR::Y] <= b_max[NR::Y] + t);
                                 bool c1 = snap_dim == GUIDE_TRANSL_SNAP_X && withinX;
                                 bool c2 = snap_dim == GUIDE_TRANSL_SNAP_Y && withinY;
                                 bool c3 = snap_dim == TRANSL_SNAP_XY && withinX && withinY;
@@ -191,8 +191,8 @@ void Inkscape::ObjectSnapper::_snapNodes(SnappedConstraints &sc,
     
     for (std::vector<NR::Point>::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); k++) {
         NR::Coord dist = NR::L2(*k - p);        
-        if (dist < getDistance() && dist < s.getDistance()) {
-            s = SnappedPoint(*k, dist);
+        if (dist < getSnapperTolerance() && dist < s.getDistance()) {
+            s = SnappedPoint(*k, dist, getSnapperTolerance(), getSnapperAlwaysSnap());
             success = true;
         }
     }
@@ -217,8 +217,8 @@ void Inkscape::ObjectSnapper::_snapTranslatingGuideToNodes(SnappedConstraints &s
         // Project each node (*k) on the guide line (running through point p)
         NR::Point p_proj = project_on_linesegment(*k, p, p + NR::rot90(guide_normal));
         NR::Coord dist = NR::L2(*k - p_proj);         
-        if (dist < getDistance() && dist < s.getDistance()) {
-            s = SnappedPoint(*k, dist);
+        if (dist < getSnapperTolerance() && dist < s.getDistance()) {
+            s = SnappedPoint(*k, dist, getSnapperTolerance(), getSnapperAlwaysSnap());
             success = true;
         }
     }
@@ -351,7 +351,7 @@ void Inkscape::ObjectSnapper::_snapPaths(SnappedConstraints &sc,
                 NR::Point const o_dt = desktop->doc2dt(o_it);                
                 NR::Coord const dist = NR::L2(o_dt - p);
 
-                if (dist < getDistance()) {
+                if (dist < getSnapperTolerance()) {
                        // if we snap to a straight line segment (within a path), then return this line segment
                        if ((*k)->IsLineSegment(o->piece)) {
                            NR::Point start_point;
@@ -360,11 +360,11 @@ void Inkscape::ObjectSnapper::_snapPaths(SnappedConstraints &sc,
                            (*k)->PointAt(o->piece, 1, end_point);
                            start_point = desktop->doc2dt(start_point);
                            end_point = desktop->doc2dt(end_point);
-                           sc.lines.push_back(Inkscape::SnappedLineSegment(o_dt, dist, start_point, end_point));    
+                           sc.lines.push_back(Inkscape::SnappedLineSegment(o_dt, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), start_point, end_point));    
                        } else {                
                            // for segments other than straight lines of a path, we'll return just the closest snapped point
                            if (dist < s.getDistance()) {
-                               s = SnappedPoint(o_dt, dist);
+                               s = SnappedPoint(o_dt, dist, getSnapperTolerance(), getSnapperAlwaysSnap());
                                success = true;
                            }
                        }
@@ -404,10 +404,10 @@ void Inkscape::ObjectSnapper::_snapPathsConstrained(SnappedConstraints &sc,
     
     // 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 max. snapping distance
+    // The distance between those points is twice the snapping tolerance
     NR::Point const p_proj_on_cl = project_on_linesegment(p, p1_on_cl, p2_on_cl);
-    NR::Point const p_min_on_cl = desktop->dt2doc(p_proj_on_cl - getDistance() * direction_vector);    
-    NR::Point const p_max_on_cl = desktop->dt2doc(p_proj_on_cl + getDistance() * direction_vector);
+    NR::Point const p_min_on_cl = desktop->dt2doc(p_proj_on_cl - getSnapperTolerance() * direction_vector);    
+    NR::Point const p_max_on_cl = desktop->dt2doc(p_proj_on_cl + getSnapperTolerance() * direction_vector);
     
     Geom::Path cl;
     cl.start(p_min_on_cl.to_2geom());
@@ -416,7 +416,7 @@ void Inkscape::ObjectSnapper::_snapPathsConstrained(SnappedConstraints &sc,
     for (std::vector<NArtBpath*>::const_iterator k = _bpaths_to_snap_to->begin(); k != _bpaths_to_snap_to->end(); k++) {
         if (*k) {                        
             // convert a Path object (see src/livarot/Path.h) to a 2geom's path object (see 2geom/path.h)
-            // TODO (Diederik) Only do this once for the first point, needs some storage of pointers in a member variable
+            // TODO: (Diederik) Only do this once for the first point, needs some storage of pointers in a member variable
             std::vector<Geom::Path> path_2geom = BPath_to_2GeomPath(*k);  
             
             for (std::vector<Geom::Path>::const_iterator l = path_2geom.begin(); l != path_2geom.end(); l++) {
@@ -428,7 +428,8 @@ 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 < tb < 1)
                     if ((*m).tb >= 0 && (*m).tb <= 1 ) {
-                        SnappedPoint s(desktop->doc2dt(p_inters), NR::L2(p_proj_on_cl - p_inters));
+                        NR::Coord dist = NR::L2(desktop->dt2doc(p_proj_on_cl) - p_inters);
+                        SnappedPoint s(desktop->doc2dt(p_inters), dist, getSnapperTolerance(), getSnapperAlwaysSnap());
                         sc.points.push_back(s);    
                     }  
                 } 
index 62449f37dcedab68906bb5d4519c1cc1362bcc3f..a6e165d5030b14d88a4a429e9722186e1fb72f5f 100644 (file)
@@ -209,7 +209,7 @@ Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t,
                                              std::list<SPItem const *> const &it) const
 {
     if (!SomeSnapperMightSnap()) {
-        return Inkscape::SnappedPoint(p, NR_HUGE);
+        return Inkscape::SnappedPoint(p, NR_HUGE, 0, false);
     }
     
     SnappedConstraints sc;        
@@ -271,7 +271,7 @@ Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType
                                                     std::list<SPItem const *> const &it) const
 {
     if (!SomeSnapperMightSnap()) {
-        return Inkscape::SnappedPoint(p, NR_HUGE);
+        return Inkscape::SnappedPoint(p, NR_HUGE, 0, false);
     }
     
     SnappedConstraints sc;
@@ -290,7 +290,7 @@ Inkscape::SnappedPoint SnapManager::guideSnap(NR::Point const &p,
     // This method is used to snap a guide to nodes, while dragging the guide around
     
     if (!(object.ThisSnapperMightSnap() && _snap_enabled_globally)) {
-        return Inkscape::SnappedPoint(p, NR_HUGE);
+        return Inkscape::SnappedPoint(p, NR_HUGE, 0, false);
     }
     
     SnappedConstraints sc;
@@ -386,7 +386,9 @@ std::pair<NR::Point, bool> SnapManager::_snapTransformed(
     */
     NR::Coord best_metric = NR_HUGE;
     NR::Coord best_second_metric = NR_HUGE;
+    NR::Point best_scale_metric(NR_HUGE, NR_HUGE);
     bool best_at_intersection = false;
+    bool best_always_snap = false;
 
     std::vector<NR::Point>::const_iterator j = transformed_points.begin();
 
@@ -416,6 +418,7 @@ std::pair<NR::Point, bool> SnapManager::_snapTransformed(
         NR::Point result;
         NR::Coord metric = NR_HUGE;
         NR::Coord second_metric = NR_HUGE;
+        NR::Point scale_metric(NR_HUGE, NR_HUGE);
         
         if (snapped.getDistance() < NR_HUGE) {
             /* We snapped.  Find the transformation that describes where the snapped point has
@@ -438,48 +441,38 @@ std::pair<NR::Point, bool> SnapManager::_snapTransformed(
                     break;
                 case SCALE:
                 {
-                    NR::Point const a = (snapped.getPoint() - origin);
-                    NR::Point const b = (*i - origin);
-                    result = transformation;
-                    if (fabs(b[NR::X]) > 1e-6 && fabs(b[NR::Y]) > 1e-6) {
-                        // This is the default scaling that results after snapping
-                        result = NR::Point(a[NR::X] / b[NR::X], a[NR::Y] / b[NR::Y]);
-                    } else {
-                        // If this point *i is horizontally or vertically aligned with
-                        // the origin of the scaling, then it will scale purely in X or Y 
-                        // We can therefore only calculate the scaling in this direction
-                        // and the scaling factor for the other direction should remain
-                        // untouched (unless scaling is uniform ofcourse)
-                        for (int index = 0; index < 2; index++) {
-                            if (fabs(b[index]) > 1e-6) {
-                                result[index] = a[index] / b[index];
-                                if (uniform) {
-                                    result[1-index] = result[index];
-                                }
+                    NR::Point const a = (snapped.getPoint() - origin); // vector to snapped point
+                    NR::Point const b = (*i - origin); // vector to original point
+                    result = NR::Point(NR_HUGE, NR_HUGE);
+                    // If this point *i is horizontally or vertically aligned with
+                    // the origin of the scaling, then it will scale purely in X or Y 
+                    // We can therefore only calculate the scaling in this direction
+                    // and the scaling factor for the other direction should remain
+                    // untouched (unless scaling is uniform ofcourse)
+                    for (int index = 0; index < 2; index++) {
+                        if (fabs(b[index]) > 1e-6) { // if SCALING CAN occur in this direction
+                            if (fabs(fabs(a[index]/b[index]) - fabs(transformation[index])) > 1e-12) { // if SNAPPING DID occur in this direction
+                                result[index] = a[index] / b[index]; // then calculate it!
                             }
+                            // we might leave result[1-index] = NR_HUGE
+                            // if scaling didn't occur in the other direction
                         }
                     }
                     
-                    if (fabs(b[NR::X]) <= 1e-6 && fabs(b[NR::Y]) <= 1e-6) {
-                        metric = NR_HUGE;
-                    } else {                        
-                        // Compare the resulting scaling with the desired scaling
-                        metric = std::abs(NR::L2(result) - NR::L2(transformation));
-                    }
+                    // Compare the resulting scaling with the desired scaling
+                    scale_metric = result - transformation; // One or both of its components might be NR_HUGE
                     break;
                 }
                 case STRETCH:
-                {
-                    for (int a = 0; a < 2; a++) {
-                        if (uniform || a == dim) {
-                            result[a] = (snapped.getPoint()[dim] - origin[dim]) / ((*i)[dim] - origin[dim]);
+                    for (int index = 0; index < 2; index++) {
+                        if (uniform || index == dim) {
+                            result[index] = (snapped.getPoint()[dim] - origin[dim]) / ((*i)[dim] - origin[dim]);
                         } else {
-                            result[a] = 1;
+                            result[index] = 1;
                         }
                     }
                     metric = std::abs(result[dim] - transformation[dim]);
                     break;
-                }
                 case SKEW:
                     result[dim] = (snapped.getPoint()[dim] - (*i)[dim]) / ((*i)[1 - dim] - origin[1 - dim]);
                     metric = std::abs(result[dim] - transformation[dim]);
@@ -489,24 +482,63 @@ std::pair<NR::Point, bool> SnapManager::_snapTransformed(
             }
             
             /* Note it if it's the best so far */
-            bool const c1 = metric < best_metric;
-            bool const c2 = metric == best_metric && snapped.getAtIntersection() == true && best_at_intersection == false;
-                       bool const c3a = metric == best_metric && snapped.getAtIntersection() == true && best_at_intersection == true;
-            bool const c3b = second_metric < best_second_metric;
-            
-            if (c1 || c2 || c3a && c3b) {
-                best_transformation = result;
-                best_metric = metric;
-                best_second_metric = second_metric;
-                best_at_intersection = snapped.getAtIntersection(); 
-                //std::cout << "SEL ";
-            } //else { std::cout << "    ";}
+            if (transformation_type == SCALE) {
+                for (int index = 0; index < 2; index++) {
+                    if (fabs(scale_metric[index]) < fabs(best_scale_metric[index])) {
+                        best_transformation[index] = result[index];
+                        best_scale_metric[index] = fabs(scale_metric[index]);
+                        //std::cout << "SEL ";
+                    } //else { std::cout << "    ";}   
+                }
+                if (uniform) {
+                    if (best_scale_metric[0] < best_scale_metric[1]) {
+                        best_transformation[1] = best_transformation[0];
+                        best_scale_metric[1] = best_scale_metric[0]; 
+                    } else {
+                        best_transformation[0] = best_transformation[1];
+                        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.getDistance() << " | P_snap = " << snapped.getPoint() << std::endl;
+            } else {
+                bool const c1 = metric < best_metric;
+                bool const c2 = metric == best_metric && snapped.getAtIntersection() == true && best_at_intersection == false;
+                       bool const c3a = metric == best_metric && snapped.getAtIntersection() == true && best_at_intersection == true;
+                bool const c3b = second_metric < best_second_metric;
+                bool const c4 = snapped.getAlwaysSnap() == true && best_always_snap == false;
+                bool const c4n = snapped.getAlwaysSnap() == false && best_always_snap == true;
+                
+                if ((c1 || c2 || (c3a && c3b) || c4) && !c4n) {
+                    best_transformation = result;
+                    best_metric = metric;
+                    best_second_metric = second_metric;
+                    best_at_intersection = snapped.getAtIntersection();
+                    best_always_snap = snapped.getAlwaysSnap(); 
+                    //std::cout << "SEL ";
+                } //else { std::cout << "    ";}
+                //std::cout << "P_orig = " << (*i) << " | metric = " << metric << " | distance = " << snapped.getDistance() << " | second metric = " << second_metric << " | P_snap = " << snapped.getPoint() << std::endl;
+            }
         }
         
-        //std::cout << "P_orig = " << (*i) << " | metric = " << metric << " | distance = " << snapped.getDistance() << " | second metric = " << second_metric << " | P_snap = " << snapped.getPoint() << std::endl;
+        
         j++;
     }
     
+    if (transformation_type == SCALE) {
+        // When scaling, don't ever exit with one of scaling components set to NR_HUGE
+        if (best_transformation == NR::Point(NR_HUGE, NR_HUGE)) {
+            best_transformation == transformation; // return the original (i.e. un-snapped) transformation        
+        } else {
+            // Still one of the transformation components could be NR_HUGE
+            for (int index = 0; index < 2; index++) {
+                if (best_transformation[index] == NR_HUGE) {
+                    best_transformation[index] == uniform ? best_transformation[1-index] : transformation[index];
+                }
+            }
+        }
+    }
+    
     // Using " < 1e6" instead of " < NR_HUGE" for catching some rounding errors
     // These rounding errors might be caused by NRRects, see bug #1584301
     return std::make_pair(best_transformation, best_metric < 1e6);
@@ -669,54 +701,54 @@ std::pair<NR::Coord, bool> SnapManager::freeSnapSkew(Inkscape::Snapper::PointTyp
 
 Inkscape::SnappedPoint SnapManager::findBestSnap(NR::Point const &p, SnappedConstraints &sc, bool constrained) const
 {
-    NR::Coord const guide_sens = guide.getDistance();
-    NR::Coord grid_sens = 0;
+    NR::Coord const guide_tol = guide.getSnapperTolerance();
+    NR::Coord grid_tol = 0;
     
     SnapManager::SnapperList const gs = getGridSnappers();
     SnapperList::const_iterator i = gs.begin();
     if (i != gs.end()) {        
-        grid_sens = (*i)->getDistance();
+        grid_tol = (*i)->getSnapperTolerance(); // there's only a single tolerance, equal for all grids
     }
     
-    // Store all snappoints, optionally together with their specific snapping range
-    std::list<std::pair<Inkscape::SnappedPoint, NR::Coord> > sp_list;
+    // Store all snappoints
+    std::list<Inkscape::SnappedPoint> sp_list;
     // Most of these snapped points are already within the snapping range, because
     // they have already been filtered by their respective snappers. In that case
     // we can set the snapping range to NR_HUGE here. If however we're looking at
     // intersections of e.g. a grid and guide line, then we'll have to determine 
     // once again whether we're within snapping range. In this case we will set
-    // the snapping range to e.g. min(guide_sens, grid_sens)
+    // the snapping range to e.g. min(guide_sens, grid_tol)
     
     // search for the closest snapped point
     Inkscape::SnappedPoint closestPoint;
     if (getClosestSP(sc.points, closestPoint)) {
-        sp_list.push_back(std::make_pair(closestPoint, NR_HUGE));
+        sp_list.push_back(closestPoint);
     } 
     
     // search for the closest snapped line segment
     Inkscape::SnappedLineSegment closestLineSegment;
     if (getClosestSLS(sc.lines, closestLineSegment)) {    
-        sp_list.push_back(std::make_pair(Inkscape::SnappedPoint(closestLineSegment), NR_HUGE));
+        sp_list.push_back(Inkscape::SnappedPoint(closestLineSegment));
     }
     
     if (_intersectionLS) {
            // search for the closest snapped intersection of line segments
            Inkscape::SnappedPoint closestLineSegmentIntersection;
            if (getClosestIntersectionSLS(sc.lines, closestLineSegmentIntersection)) {
-               sp_list.push_back(std::make_pair(closestLineSegmentIntersection, NR_HUGE));
+               sp_list.push_back(closestLineSegmentIntersection);
            }
     }    
 
     // search for the closest snapped grid line
     Inkscape::SnappedLine closestGridLine;
     if (getClosestSL(sc.grid_lines, closestGridLine)) {    
-        sp_list.push_back(std::make_pair(Inkscape::SnappedPoint(closestGridLine), NR_HUGE));
+        sp_list.push_back(Inkscape::SnappedPoint(closestGridLine));
     }
     
     // search for the closest snapped guide line
     Inkscape::SnappedLine closestGuideLine;
     if (getClosestSL(sc.guide_lines, closestGuideLine)) {
-        sp_list.push_back(std::make_pair(Inkscape::SnappedPoint(closestGuideLine), NR_HUGE));
+        sp_list.push_back(Inkscape::SnappedPoint(closestGuideLine));
     }
     
     // When freely snapping to a grid/guide/path, only one degree of freedom is eliminated
@@ -729,44 +761,49 @@ Inkscape::SnappedPoint SnapManager::findBestSnap(NR::Point const &p, SnappedCons
         // search for the closest snapped intersection of grid lines
         Inkscape::SnappedPoint closestGridPoint;
         if (getClosestIntersectionSL(sc.grid_lines, closestGridPoint)) {
-            sp_list.push_back(std::make_pair(closestGridPoint, NR_HUGE));
+            sp_list.push_back(closestGridPoint);
         }
         
         // search for the closest snapped intersection of guide lines
         Inkscape::SnappedPoint closestGuidePoint;
         if (getClosestIntersectionSL(sc.guide_lines, closestGuidePoint)) {
-            sp_list.push_back(std::make_pair(closestGuidePoint, NR_HUGE));
+            sp_list.push_back(closestGuidePoint);
         }
         
         // 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)) {
-               sp_list.push_back(std::make_pair(closestGridGuidePoint, std::min(guide_sens, grid_sens)));
+               sp_list.push_back(closestGridGuidePoint);
            }
         }
     }
     
     // now let's see which snapped point gets a thumbs up
-    Inkscape::SnappedPoint bestPoint(p, NR_HUGE);
-    for (std::list<std::pair<Inkscape::SnappedPoint, NR::Coord> >::const_iterator i = sp_list.begin(); i != sp_list.end(); i++) {
+    Inkscape::SnappedPoint bestSnappedPoint = Inkscape::SnappedPoint(p, NR_HUGE, 0, false);
+    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
-           if ((*i).first.getDistance() <= (*i).second) {
+        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).first.getDistance() < bestPoint.getDistance(); 
-               // or, if it's just as close then consider the second distance
+               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 c3a = ((*i).first.getDistance() == bestPoint.getDistance()); 
-               bool c3b = (*i).first.getSecondDistance() < bestPoint.getSecondDistance();
+               bool c4a = ((*i).getDistance() == bestSnappedPoint.getDistance()); 
+               bool c4b = (*i).getSecondDistance() < bestSnappedPoint.getSecondDistance();
                // then prefer this point over the previous one
-               if (c1 || c2 || c3a && c3b) {
-                bestPoint = (*i).first;
+            if ((c1 || c2 || c3 || (c4a && c4b)) && !c3n) {
+                bestSnappedPoint = *i;
             }
         }
     }
-    return bestPoint;         
+    
+    return bestSnappedPoint;         
 }
 
 /*
index 67fec627a85670a71a093d3e70d43880c76785f7..b6a5c2f9f5986f5b71692a808fad6a525cf26a80 100644 (file)
 #include <2geom/geom.h>
 #include "libnr/nr-values.h"
 
-Inkscape::SnappedLineSegment::SnappedLineSegment(NR::Point snapped_point, NR::Coord snapped_distance, NR::Point start_point_of_line, NR::Point end_point_of_line)
+Inkscape::SnappedLineSegment::SnappedLineSegment(NR::Point snapped_point, NR::Coord snapped_distance, NR::Coord snapped_tolerance, bool always_snap, NR::Point start_point_of_line, NR::Point end_point_of_line)
     : _start_point_of_line(start_point_of_line), _end_point_of_line(end_point_of_line) 
 {
-       _distance = snapped_distance;
        _point = snapped_point;
+    _distance = snapped_distance;
+       _tolerance = snapped_tolerance;
+    _always_snap = always_snap;
        _at_intersection = false;
        _second_distance = NR_HUGE;
+    _second_tolerance = 0;
+    _second_always_snap = false;
 }
 
 Inkscape::SnappedLineSegment::SnappedLineSegment() 
 {
        _start_point_of_line = NR::Point(0,0);
        _end_point_of_line = NR::Point(0,0);
-       _distance = NR_HUGE;
        _point = NR::Point(0,0);
+    _distance = NR_HUGE;
+       _tolerance = 0;
+    _always_snap = false;
        _at_intersection = false;
        _second_distance = NR_HUGE;
+    _second_tolerance = 0;
+    _second_always_snap = false;
 }
 
 
@@ -39,31 +47,45 @@ Inkscape::SnappedLineSegment::~SnappedLineSegment()
 Inkscape::SnappedPoint Inkscape::SnappedLineSegment::intersect(SnappedLineSegment const &line) const 
 {
        Geom::Point intersection_2geom(NR_HUGE, NR_HUGE);
-       NR::Coord distance = NR_HUGE;
-       NR::Coord second_distance = NR_HUGE;
-        
        Geom::IntersectorKind result = segment_intersect(_start_point_of_line.to_2geom(), _end_point_of_line.to_2geom(),
                                                                         line._start_point_of_line.to_2geom(), line._end_point_of_line.to_2geom(),
                                                                         intersection_2geom);
        NR::Point intersection(intersection_2geom);
        
        if (result == Geom::intersects) {
-               /* The relevant snapped distance is the distance to the closest snapped line, not the
-               distance to the intersection. See the comment in Inkscape::SnappedLine::intersect
+               /* If a snapper has been told to "always snap", then this one should be preferred
+         * over the other, if that other one has not been told so. (The preferred snapper
+         * will be labelled "primary" below)
+        */
+        bool const c1 = this->getAlwaysSnap() && !line.getAlwaysSnap(); //do not use _tolerance directly!
+        /* If neither or both have been told to "always snap", then cast a vote based on
+         * the snapped distance. For this we should consider the distance to the snapped
+         * line, not the distance to the intersection. 
+         * See the comment in Inkscape::SnappedLine::intersect
                */
-               distance = std::min(_distance, line.getDistance());
-               second_distance = std::max(_distance, line.getDistance());
+        bool const c2 = _distance < line.getDistance();
+        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, primarySLS->getDistance(), primarySLS->getTolerance(), primarySLS->getAlwaysSnap(), true, 
+                                          secondarySLS->getDistance(), secondarySLS->getTolerance(), secondarySLS->getAlwaysSnap());
        }
-       return SnappedPoint(intersection, distance, result == Geom::intersects, second_distance);
+    
+    // No intersection
+    return SnappedPoint(intersection, NR_HUGE, 0, false, false, NR_HUGE, 0, false);
 };
 
 
 
-Inkscape::SnappedLine::SnappedLine(NR::Point snapped_point, NR::Coord snapped_distance, NR::Point normal_to_line, NR::Point point_on_line)
+Inkscape::SnappedLine::SnappedLine(NR::Point snapped_point, NR::Coord snapped_distance, NR::Coord snapped_tolerance, bool always_snap, NR::Point normal_to_line, NR::Point point_on_line)
     : _normal_to_line(normal_to_line), _point_on_line(point_on_line)
 {
        _distance = snapped_distance;
+    _tolerance = snapped_tolerance;
+    _always_snap = always_snap;
        _second_distance = NR_HUGE;
+    _second_tolerance = 0;
+    _second_always_snap = false;
        _point = snapped_point;
        _at_intersection = false;
 }
@@ -73,7 +95,11 @@ Inkscape::SnappedLine::SnappedLine()
        _normal_to_line = NR::Point(0,0);
        _point_on_line = NR::Point(0,0);
        _distance = NR_HUGE;
+    _tolerance = 0;
+    _always_snap = false;
        _second_distance = NR_HUGE;
+    _second_tolerance = 0;
+    _second_always_snap = false;
        _point = NR::Point(0,0);
        _at_intersection = false;
 }
@@ -84,31 +110,44 @@ Inkscape::SnappedLine::~SnappedLine()
 
 Inkscape::SnappedPoint Inkscape::SnappedLine::intersect(SnappedLine const &line) const 
 {
-       // Calculate the intersection of to lines, which are both within snapping range
+       // Calculate the intersection of two lines, which are both within snapping range
+    // One could be a grid line, whereas the other could be a guide line
        // The point of intersection should be considered for snapping, but might be outside the snapping range
        
        Geom::Point intersection_2geom(NR_HUGE, NR_HUGE);
-       NR::Coord distance = NR_HUGE;
-       NR::Coord second_distance = NR_HUGE;
-       
-    Geom::IntersectorKind result = Geom::line_intersection(getNormal().to_2geom(), getConstTerm(), 
+       Geom::IntersectorKind result = Geom::line_intersection(getNormal().to_2geom(), getConstTerm(), 
                                    line.getNormal().to_2geom(), line.getConstTerm(), intersection_2geom);
        NR::Point intersection(intersection_2geom);
         
        if (result == Geom::intersects) {
-               /* The relevant snapped distance is the distance to the closest snapped line, not the
-               distance to the intersection. For example, when a box is almost aligned with a grid
-               in both horizontal and vertical directions, the distance to the intersection of the
-               grid lines will always be larger then the distance to a grid line. We will be snapping
-               to the closest snapped point however, so if we ever want to snap to the intersection
-               then the distance to it should at least be equal to the other distance, not greater 
-               than it, as that would rule the intersection out
-               */
-               distance = std::min(_distance, line.getDistance());
-               second_distance = std::max(_distance, line.getDistance());
-       }
-
-    return SnappedPoint(intersection, distance, result == Geom::intersects, second_distance);
+        /* If a snapper has been told to "always snap", then this one should be preferred
+         * over the other, if that other one has not been told so. (The preferred snapper
+         * will be labelled "primary" below)
+        */
+        bool const c1 = this->getAlwaysSnap() && !line.getAlwaysSnap();
+        /* If neither or both have been told to "always snap", then cast a vote based on
+         * the snapped distance. For this we should consider the distance to the snapped
+         * line, not the distance to the intersection.
+         * 
+         * The relevant snapped distance is the distance to the closest snapped line, not the
+         * distance to the intersection. For example, when a box is almost aligned with a grid
+         * in both horizontal and vertical directions, the distance to the intersection of the
+         * grid lines will always be larger then the distance to a grid line. We will be snapping
+         * to the closest snapped point however, so if we ever want to snap to the intersection
+         * then the distance to it should at least be equal to the other distance, not greater
+         * than it, as that would rule the intersection out when comparing it with regular snappoint,
+         * as the latter will always be closer
+        */
+        bool const c2 = _distance < line.getDistance();
+        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, primarySL->getDistance(), primarySL->getTolerance(), primarySL->getAlwaysSnap(), true, 
+                                          secondarySL->getDistance(), secondarySL->getTolerance(), secondarySL->getAlwaysSnap());
+    }
+    
+    // No intersection
+    return SnappedPoint(intersection, NR_HUGE, 0, false, false, NR_HUGE, 0, false);
 }
 
 // search for the closest snapped line segment
index be2a792b72302407cab27e2069ed6667e5785707..3616058b0cb0d6e8a9195d68230fb501c998e953 100644 (file)
@@ -26,7 +26,7 @@ class SnappedLineSegment : public SnappedPoint
 {
 public:
     SnappedLineSegment();
-    SnappedLineSegment(NR::Point snapped_point, NR::Coord snapped_distance, NR::Point start_point_of_line, NR::Point end_point_of_line);
+    SnappedLineSegment(NR::Point snapped_point, NR::Coord snapped_distance, NR::Coord snapped_tolerance, bool always_snap, NR::Point start_point_of_line, NR::Point end_point_of_line);
     ~SnappedLineSegment();
     Inkscape::SnappedPoint intersect(SnappedLineSegment const &line) const; //intersect with another SnappedLineSegment
     
@@ -41,13 +41,13 @@ class SnappedLine : public SnappedPoint
 {
 public:
     SnappedLine();
-    SnappedLine(NR::Point snapped_point, NR::Coord snapped_distance, NR::Point normal_to_line, NR::Point point_on_line);
+    SnappedLine(NR::Point snapped_point, NR::Coord snapped_distance, NR::Coord snapped_tolerance, bool always_snap, NR::Point normal_to_line, NR::Point point_on_line);
     ~SnappedLine();
     Inkscape::SnappedPoint intersect(SnappedLine const &line) const; //intersect with another SnappedLine
     // This line is described by this equation:
     //        a*x + b*y = c  <->  nx*px + ny+py = c  <->  n.p = c
     NR::Point getNormal() const {return _normal_to_line;}                             // n = (nx, ny)
-    NR::Point getPointOnLine() const {return _point_on_line;}                        // p = (px, py)
+    NR::Point getPointOnLine() const {return _point_on_line;}                         // p = (px, py)
     NR::Coord getConstTerm() const {return dot(_normal_to_line, _point_on_line);}     // c = n.p = nx*px + ny*py;
     
 private:
index d97abda1d0a7567b952a2e49c9aa741a2ac44a69..8a0aea3c7594687e876fe7bdd14259313e7c9d3c 100644 (file)
 
 #include "snapped-point.h"
 
-Inkscape::SnappedPoint::SnappedPoint(NR::Point p, NR::Coord d, bool at_intersection, NR::Coord d2)
-    : _distance(d), _point(p), _at_intersection(at_intersection), _second_distance(d2)
+// overloaded constructor
+Inkscape::SnappedPoint::SnappedPoint(NR::Point p, NR::Coord d, NR::Coord t, bool a)
+    : _point(p), _distance(d), _tolerance(t), _always_snap(a)
+{
+    _at_intersection = false;
+    _second_distance = NR_HUGE;
+    _second_tolerance = 0;
+    _second_always_snap = false;
+}
+
+Inkscape::SnappedPoint::SnappedPoint(NR::Point p, NR::Coord d, NR::Coord t, bool a, bool at_intersection, NR::Coord d2, NR::Coord t2, bool a2)
+    : _point(p), _distance(d), _tolerance(t), _always_snap(a), _at_intersection(at_intersection), 
+    _second_distance(d2), _second_tolerance(t2), _second_always_snap(a2)
 {
 }
 
 Inkscape::SnappedPoint::SnappedPoint()
 {
-    _distance = NR_HUGE;
     _point = NR::Point(0,0);
+    _distance = NR_HUGE;
+    _tolerance = 0;
+    _always_snap = false;
     _at_intersection = false;
     _second_distance = NR_HUGE;
+    _second_tolerance = 0;
+    _second_always_snap = false;
 }
 
 
@@ -35,11 +50,31 @@ NR::Coord Inkscape::SnappedPoint::getDistance() const
     return _distance;
 }
 
+NR::Coord Inkscape::SnappedPoint::getTolerance() const
+{
+    return _tolerance;
+}
+
+bool Inkscape::SnappedPoint::getAlwaysSnap() const
+{
+    return _always_snap;
+}
+
 NR::Coord Inkscape::SnappedPoint::getSecondDistance() const
 {
     return _second_distance;
 }
 
+NR::Coord Inkscape::SnappedPoint::getSecondTolerance() const
+{
+    return _second_tolerance;
+}
+
+bool Inkscape::SnappedPoint::getSecondAlwaysSnap() const
+{
+    return _second_always_snap;
+}
+
 
 NR::Point Inkscape::SnappedPoint::getPoint() const
 {
index e4b08a302624d2122bfc38bc854b41323d44446e..f0584812d8a9814b6106067b01e87b06c9528880 100644 (file)
@@ -26,11 +26,16 @@ class SnappedPoint
 {
 public:
     SnappedPoint();
-    SnappedPoint(::NR::Point p, ::NR::Coord d, bool at_intersection = false, NR::Coord d2 = NR_HUGE);
+    SnappedPoint(NR::Point p, NR::Coord d, NR::Coord t, bool a, bool at_intersection, NR::Coord d2, NR::Coord t2, bool a2);
+    SnappedPoint(NR::Point p, NR::Coord d, NR::Coord t, bool a);
     ~SnappedPoint();
 
     NR::Coord getDistance() const;
+    NR::Coord getTolerance() const;
+    bool getAlwaysSnap() const;
     NR::Coord getSecondDistance() const;
+    NR::Coord getSecondTolerance() const;
+    bool getSecondAlwaysSnap() const;
     NR::Point getPoint() const;
     bool getAtIntersection() const {return _at_intersection;}
     
@@ -42,12 +47,18 @@ protected:
        an intersection of e.g. two lines, then this is the distance to the closest
        line */    
     NR::Coord _distance; 
+    /* The snapping tolerance in screen pixels (depends on zoom)*/  
+    NR::Coord _tolerance;
+    /* If true then "Always snap" is on */
+    bool _always_snap;
     
     /* If the snapped point is at an intersection of e.g. two lines, then this is
        the distance to the fartest line */    
-    NR::Coord _second_distance; 
-    
-    
+    NR::Coord _second_distance;
+    /* The snapping tolerance in screen pixels (depends on zoom)*/
+    NR::Coord _second_tolerance;
+    /* If true then "Always snap" is on */
+    bool _second_always_snap;
 };    
 
 }
index 1daa1d15183574d90839e1f668ec014b11155d3c..efdc6bcbea8a4bebcd8630f56108a284187cb2bf 100644 (file)
@@ -20,9 +20,9 @@ Inkscape::Snapper::PointType const Inkscape::Snapper::SNAPPOINT_GUIDE = 0x4;
 /**
  *  Construct new Snapper for named view.
  *  \param nv Named view.
- *  \param d Snap distance.
+ *  \param d Snap tolerance.
  */
-Inkscape::Snapper::Snapper(SPNamedView const *nv, NR::Coord const d) : _named_view(nv), _snap_enabled(true), _distance(d)
+Inkscape::Snapper::Snapper(SPNamedView const *nv, NR::Coord const t) : _named_view(nv), _snap_enabled(true), _snapper_tolerance(t)
 {
     g_assert(_named_view != NULL);
     g_assert(SP_IS_NAMEDVIEW(_named_view));
@@ -31,20 +31,25 @@ Inkscape::Snapper::Snapper(SPNamedView const *nv, NR::Coord const d) : _named_vi
 }
 
 /**
- *  Set snap distance.
- *  \param d New snap distance (desktop coordinates)
+ *  Set snap tolerance.
+ *  \param d New snap tolerance (desktop coordinates)
  */
-void Inkscape::Snapper::setDistance(NR::Coord const d)
+void Inkscape::Snapper::setSnapperTolerance(NR::Coord const d)
 {
-    _distance = d;
+    _snapper_tolerance = d;
 }
 
 /**
- *  \return Snap distance (desktop coordinates); depends on current zoom so that it's always the same in screen pixels
+ *  \return Snap tolerance (desktop coordinates); depends on current zoom so that it's always the same in screen pixels
  */
-NR::Coord Inkscape::Snapper::getDistance() const
+NR::Coord Inkscape::Snapper::getSnapperTolerance() const
 {
-    return _distance / SP_ACTIVE_DESKTOP->current_zoom();
+    return _snapper_tolerance / SP_ACTIVE_DESKTOP->current_zoom();
+}
+
+bool Inkscape::Snapper::getSnapperAlwaysSnap() const
+{
+    return _snapper_tolerance == 10000; //TODO: Replace this threshold of 10000 by a constant; see also tolerance-slider.cpp
 }
 
 /**
index cf2533f22fb3ebb1898c1ac33517b2ac99305ba9..8aeea0a26c60bf15eacc170604df0ecf46f1c6c1 100644 (file)
@@ -7,6 +7,7 @@
  *
  *    Authors:
  *      Carl Hetherington <inkscape@carlh.net>
+ *      Diederik van Lierop <mail@diedenrezi.nl>
  *
  *    Released under GNU GPL, read the file 'COPYING' for more information.
  */
@@ -47,11 +48,12 @@ public:
     static const PointType SNAPPOINT_GUIDE;
 
     void setSnapFrom(PointType t, bool s);
-    void setDistance(::NR::Coord d);
-
     bool getSnapFrom(PointType t) const;
-    ::NR::Coord getDistance() const;
-
+    
+    void setSnapperTolerance(NR::Coord t);    
+    NR::Coord getSnapperTolerance() const; //returns the tolerance of the snapper in screen pixels (i.e. independent of zoom)
+    bool getSnapperAlwaysSnap() const; //if true, then the snapper will always snap, regardless of its tolerance
+    
     /**
     *  \return true if this Snapper will snap at least one kind of point.
     */
@@ -121,6 +123,10 @@ protected:
     bool _snap_enabled; ///< true if this snapper is enabled, otherwise false
     
 private:
+    NR::Coord _snapper_tolerance;   ///< snap tolerance in desktop coordinates 
+                                    // must be private to enforce the usage of getTolerance(), which retrieves 
+                                    // the tolerance in screen pixels (making it zoom independent)
+
 
     /**
      *  Try to snap a point to whatever this snapper is interested in.  Any
@@ -155,8 +161,6 @@ private:
                                             std::vector<NR::Point> &points_to_snap,
                                             ConstraintLine const &c,
                                             std::list<SPItem const *> const &it) const = 0;
-    
-    NR::Coord _distance; ///< snap distance (desktop coordinates)
 };
 
 }