Code

Add an option to the preferences to _only_ snap the node closest to the mouse pointer
authordvlierop2 <dvlierop2@users.sourceforge.net>
Mon, 24 Nov 2008 19:45:10 +0000 (19:45 +0000)
committerdvlierop2 <dvlierop2@users.sourceforge.net>
Mon, 24 Nov 2008 19:45:10 +0000 (19:45 +0000)
src/nodepath.cpp
src/preferences-skeleton.h
src/seltrans.cpp
src/seltrans.h
src/snap.cpp
src/snapped-curve.cpp
src/snapped-point.cpp
src/snapped-point.h
src/ui/dialog/inkscape-preferences.cpp
src/ui/dialog/inkscape-preferences.h

index 234aba4cc6675e223544937b67f00981d93a1f3f..7165dab98e57a0cc04158a532629ab3024f15645 100644 (file)
@@ -1360,25 +1360,46 @@ static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath,
         
         SnapManager &m = nodepath->desktop->namedview->snap_manager;
         
-        for (GList *l = nodepath->selected; l != NULL; l = l->next) {
+        // When only the node closest to the mouse pointer is to be snapped 
+        // then we will not even try to snap to other points and discard those immediately
+        Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+        bool closest_only = prefs->getBool("/options/snapclosestonly/value", false); 
+        
+        Inkscape::NodePath::Node *closest_node = NULL;
+        Geom::Coord closest_dist = NR_HUGE;
+        
+       if (closest_only) {
+               for (GList *l = nodepath->selected; l != NULL; l = l->next) {
+                       Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
+                       Geom::Coord dist = Geom::L2(nodepath->drag_origin_mouse - n->origin);
+                       if (dist < closest_dist) {
+                               closest_node = n;
+                               closest_dist = dist;
+                       }
+               }                       
+        }
+        
+       // Iterate through all selected nodes
+       m.setup(nodepath->desktop, false, SP_PATH(nodepath->item), &unselected_nodes);
+       for (GList *l = nodepath->selected; l != NULL; l = l->next) {
             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
-            m.setup(nodepath->desktop, false, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
-            Inkscape::SnappedPoint s;
-            
-            if (constrained) {
-                Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
-                dedicated_constraint.setPoint(n->pos);
-                s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), dedicated_constraint);
-            } else {
-                s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta));
-            }            
-            
-            if (s.getSnapped()) {
-               s.setPointerDistance(Geom::L2(nodepath->drag_origin_mouse - n->origin));                            
-               if (!s.isOtherOneBetter(best, true)) {
-                       best = s;
-                       best_pt = from_2geom(s.getPoint()) - n->pos;
-               }
+            if (!closest_only || n == closest_node) { //try to snap either all selected nodes or only the closest one
+                   Inkscape::SnappedPoint s;            
+                   if (constrained) {
+                       Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
+                       dedicated_constraint.setPoint(n->pos);
+                       s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), dedicated_constraint);
+                   } else {
+                       s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta));
+                   }            
+                   
+                   if (s.getSnapped()) {
+                       s.setPointerDistance(Geom::L2(nodepath->drag_origin_mouse - n->origin));                            
+                       if (!s.isOtherSnapBetter(best, true)) {
+                               best = s;
+                               best_pt = from_2geom(s.getPoint()) - n->pos;
+                       }
+                   }
             }
         }
                         
index 1868b9c888dfa58b9ecc8881c6f8a72a7a05e90c..68df4e730965f2a939b0143c28551ab9503bf71f 100644 (file)
@@ -250,6 +250,7 @@ static char const preferences_skeleton[] =
 "    <group id=\"scrollingacceleration\" value=\"0.4\"/>\n"
 "    <group id=\"snapdelay\" value=\"150\"/>\n"        
 "    <group id=\"snapweight\" value=\"0.5\"/>\n"
+"    <group id=\"snapclosestonly\" value=\"0\"/>\n"
 "    <group id=\"snapindicator\" value=\"1\"/>\n"
 "    <group id=\"autoscrollspeed\" value=\"0.7\"/>\n"
 "    <group id=\"autoscrolldistance\" value=\"-10\"/>\n"
index 34ea17a649ef356e10abdb5a067efc97d705aade..4bb1b3d2bc1fdfd64febfe2aa55413f9580ebfff 100644 (file)
@@ -295,7 +295,7 @@ void Inkscape::SelTrans::grab(Geom::Point const &p, gdouble x, gdouble y, bool s
         _snap_points = snap_points_hull;
         // Unfortunately, by now we will have lost the font-baseline snappoints :-(
     }
-
+    
     // Find bbox hulling all special points, which excludes stroke width. Here we need to include the
     // path nodes, for example because a rectangle which has been converted to a path doesn't have
     // any other special points
@@ -326,6 +326,23 @@ void Inkscape::SelTrans::grab(Geom::Point const &p, gdouble x, gdouble y, bool s
         _opposite_for_specpoints = snap_points_bbox.min() + snap_points_bbox.dimensions() * Geom::Scale(1-x, 1-y);
         _opposite = _opposite_for_bboxpoints;
     }
+    
+    // When snapping the node closest to the mouse pointer is absolutely preferred over the closest snap 
+    // (i.e. when weight == 1), then we will not even try to snap to other points and discard those other
+    // points immediately. 
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+       if (prefs->getBool("/options/snapclosestonly/value", false)) {
+       _keepClosestPointOnly(_snap_points, p);
+       _keepClosestPointOnly(_bbox_points, p);
+       if (_snap_points.size() == 1 && _bbox_points.size() == 1) { //both vectors can only have either one or zero elements
+               // So we have exactly one bbox corner and one node left; now find out which is closest and delete the other one
+               if (Geom::L2(_snap_points.at(0) - p) < Geom::L2(_bbox_points.at(0) - p)) {
+                       _bbox_points.clear();
+               } else {
+                       _snap_points.clear();
+               }
+               }
+    }
 
     // The lines below are usefull for debugging any snapping issues, as they'll spit out all points that are considered for snapping
 
@@ -1415,7 +1432,7 @@ void Inkscape::SelTrans::moveTo(Geom::Point const &xy, guint state)
         Inkscape::SnappedPoint best_snapped_point;
         for (std::list<Inkscape::SnappedPoint>::const_iterator i = s.begin(); i != s.end(); i++) {
             if (i->getSnapped()) {
-                if (best_snapped_point.isOtherOneBetter(*i, true)) {
+                if (best_snapped_point.isOtherSnapBetter(*i, true)) {
                     best_snapped_point = *i;
                     dxy = i->getTransformation();
                 }
@@ -1544,6 +1561,25 @@ Geom::Point Inkscape::SelTrans::_calcAbsAffineGeom(Geom::Scale const geom_scale)
     return visual_bbox.min() + visual_bbox.dimensions() * Geom::Scale(_handle_x, _handle_y);
 }
 
+void Inkscape::SelTrans::_keepClosestPointOnly(std::vector<Geom::Point> &points, const Geom::Point &reference)
+{
+       if (points.size() < 2) return;
+       
+       Geom::Point closest_point = Geom::Point(NR_HUGE, NR_HUGE);
+       Geom::Coord closest_dist = NR_HUGE;
+       
+       for(std::vector<Geom::Point>::const_iterator i = points.begin(); i != points.end(); i++) {
+               Geom::Coord dist = Geom::L2(*i - reference);
+               if (i == points.begin() || dist < closest_dist) {
+                       closest_point = *i;
+                       closest_dist = dist;                    
+               }
+    }
+       
+       points.clear();
+       points.push_back(closest_point);        
+}
+
 /*
   Local Variables:
   mode:c++
index 9fafb5a761ebe4d58144ec0d4a366494fa7d11c7..a472f3366ebd4f6dad7f6cd826de9f7bfe0bd15a 100644 (file)
@@ -102,6 +102,7 @@ private:
     Geom::Point _getGeomHandlePos(Geom::Point const &visual_handle_pos);
     Geom::Point _calcAbsAffineDefault(Geom::Scale const default_scale);
     Geom::Point _calcAbsAffineGeom(Geom::Scale const geom_scale);
+    void _keepClosestPointOnly(std::vector<Geom::Point> &points, const Geom::Point &reference);
 
     enum State {
         STATE_SCALE, //scale or stretch
index f6504efe3b16934965afe2d12d84c73060cc9890..e89063b432b3dfae359c9f72f45182a722057bad 100644 (file)
@@ -347,7 +347,6 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed(
     ** Also used to globally disable all snapping 
     */
     if (someSnapperMightSnap() == false) {
-        g_assert(points.size() > 0);
         return Inkscape::SnappedPoint();
     }
     
@@ -551,7 +550,7 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed(
                     }
                 }
             } else { // For all transformations other than scaling
-                if (best_snapped_point.isOtherOneBetter(snapped_point, true)) {
+                if (best_snapped_point.isOtherSnapBetter(snapped_point, true)) {
                        best_transformation = result;
                     best_snapped_point = snapped_point;                    
                 }
@@ -815,7 +814,7 @@ Inkscape::SnappedPoint SnapManager::findBestSnap(Geom::Point const &p, SnappedCo
        // std::cout << "sp = " << from_2geom((*i).getPoint());
         if ((*i).getSnapDistance() <= (*i).getTolerance()) {
             // 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, false)) { 
+            if (i == sp_list.begin() || bestSnappedPoint.isOtherSnapBetter(*i, false)) { 
                 // then prefer this point over the previous one
                 bestSnappedPoint = *i;
             }
index 327de90bd215c99c1def2bc9c7292cafb970fd64..b566bfe34c7704a44282ca184d3165cf0a3a3ab5 100644 (file)
@@ -121,8 +121,8 @@ bool getClosestIntersectionCS(std::list<Inkscape::SnappedCurve> const &list, Geo
                 bool const c1 = !success;
                 // or, if it's closer             
                 bool const c2 = sp.getSnapDistance() < result.getSnapDistance();
-                // or, if it's just then look at the other distance 
-                // (only relevant for snapped points which are at an intersection
+                // or, if it's just as close then look at the other distance 
+                // (only relevant for snapped points which are at an intersection)
                 bool const c3 = (sp.getSnapDistance() == result.getSnapDistance()) && (sp.getSecondSnapDistance() < result.getSecondSnapDistance()); 
                 // then prefer this point over the previous one
                 if (c1 || c2 || c3) {  
index d03968a9444261e305ae948bf0b3c07303d45806..c5eaba74992e3122c6530387613f26f582bf10bd 100644 (file)
@@ -17,7 +17,7 @@
 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(std::max(t,1.0)), _always_snap(a)
 {
-       // tolerance should never be smaller than 1 px, as it is used for normalization in isOtherOneBetter. We don't want a division by zero.
+       // tolerance should never be smaller than 1 px, as it is used for normalization in isOtherSnapBetter. We don't want a division by zero.
     _fully_constrained = fully_constrained;
     _second_distance = NR_HUGE;
     _second_tolerance = 1;
@@ -31,7 +31,7 @@ Inkscape::SnappedPoint::SnappedPoint(Geom::Point const &p, SnapTargetType const
     _second_distance(d2), _second_tolerance(std::max(t2,1.0)), _second_always_snap(a2)
 {
     // tolerance should never be smaller than 1 px, as it is used for normalization in 
-    // isOtherOneBetter. We don't want a division by zero.
+    // isOtherSnapBetter. We don't want a division by zero.
     _transformation = Geom::Point(1,1);
     _pointer_distance = NR_HUGE;
 }
@@ -79,7 +79,7 @@ bool getClosestSP(std::list<Inkscape::SnappedPoint> &list, Inkscape::SnappedPoin
     return success;
 }
 
-bool Inkscape::SnappedPoint::isOtherOneBetter(Inkscape::SnappedPoint const &other_one, bool weighted) const
+bool Inkscape::SnappedPoint::isOtherSnapBetter(Inkscape::SnappedPoint const &other_one, bool weighted) const
 {
     
        double dist_other = other_one.getSnapDistance();
@@ -92,7 +92,10 @@ bool Inkscape::SnappedPoint::isOtherOneBetter(Inkscape::SnappedPoint const &othe
                // weigth factor: controls which node should be preferrerd for snapping, which is either
                // the node with the closest snap (w = 0), or the node closest to the mousepointer (w = 1)
                Inkscape::Preferences *prefs = Inkscape::Preferences::get();
-               double const w = prefs->getDoubleLimited("/options/snapweight/value", 0.5, 0, 1);
+               double w = prefs->getDoubleLimited("/options/snapweight/value", 0.5, 0, 1);
+               if (prefs->getBool("/options/snapclosestonly/value", false)) {
+                       w = 1;
+               }
                if (w > 0) {
                        // When accounting for the distance to the mouse pointer, then at least one of the snapped points should
                        // have that distance set. If not, then this is a bug. Either "weighted" must be set to false, or the
index bc5b2d39ee702d97753c9b9b9ec221b105039820..b50207429ab81c8ee2d6d3b0d87f515348acc10a 100644 (file)
@@ -79,7 +79,7 @@ public:
     void setTarget(SnapTargetType const target) {_target = target;}
     SnapTargetType getTarget() {return _target;}
     
-    bool isOtherOneBetter(SnappedPoint const &other_one, bool weighted) const;
+    bool isOtherSnapBetter(SnappedPoint const &other_one, bool weighted) const;
     
 protected:
     Geom::Point _point; // Location of the snapped point
index e78d51af41f6ec1819e958da9c39c6bd5a5d49b5..e680115fec3e4fed697909f736953689d95ba7e1 100644 (file)
@@ -210,11 +210,14 @@ void InkscapePreferences::initPageSnapping()
        _page_snapping.add_line( false, _("Delay (in msec):"), _snap_delay, "",
                        _("Postpone snapping as long as the mouse is moving, and then wait an additional fraction of a second. This additional delay is specified here. When set to zero or to a very small number, snapping will be immediate"), true);
        
+       _snap_closest_only.init( _("Only snap the node closest to the pointer"), "/options/snapclosestonly/value", false);
+       _page_snapping.add_line( false, "", _snap_closest_only, "",
+       _("Only try to snap the node that is initialy closest to the mouse pointer"));
+       
        _snap_weight.init("/options/snapweight/value", 0, 1, 0.1, 0.2, 0.5, 1);
        _page_snapping.add_line( false, _("Weight factor:"), _snap_weight, "",
                        _("When multiple snap solutions are found, then Inkscape can either prefer the closest transformation (when set to 0), or prefer the node that was initially the closest to the pointer (when set to 1)"), true);
-               
-
+       
        this->AddPage(_page_snapping, _("Snapping"), PREFS_PAGE_SNAPPING);
 }
 
index c62919c45eeafa1ebe2f9cbacd7fa3d1e8c6c098..b439f25dd26ad1b7e425045d4dc2248d8305110f 100644 (file)
@@ -128,7 +128,7 @@ protected:
     PrefCheckButton _wheel_zoom;
     
     Gtk::HScale                *_slider_snapping_delay;
-    PrefCheckButton _snap_indicator;
+    PrefCheckButton _snap_indicator, _snap_closest_only;
 
     PrefCombo       _steps_rot_snap;
     PrefCheckButton _steps_compass;