From 6b8572be4bdae9ee79fc42c9d18fd9882dec21aa Mon Sep 17 00:00:00 2001 From: Diederik van Lierop Date: Sun, 23 Nov 2008 13:55:26 +0000 Subject: 1) Improve the way the distance to the pointer is taken into account when finding the best snap 2) Use this distance also when snapping nodes in the path editor 3) Add a slider in the preferences dialog to control the weighing of this distance (bzr r6895) --- src/snapped-point.cpp | 68 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 19 deletions(-) (limited to 'src/snapped-point.cpp') diff --git a/src/snapped-point.cpp b/src/snapped-point.cpp index 1177e5f14..d03968a94 100644 --- a/src/snapped-point.cpp +++ b/src/snapped-point.cpp @@ -9,27 +9,31 @@ * Released under GNU GPL, read the file 'COPYING' for more information. */ +#include #include "snapped-point.h" +#include "preferences.h" // overloaded constructor 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) + : _point(p), _target(target), _distance(d), _tolerance(std::max(t,1.0)), _always_snap(a) { - _at_intersection = false; + // tolerance should never be smaller than 1 px, as it is used for normalization in isOtherOneBetter. We don't want a division by zero. _fully_constrained = fully_constrained; _second_distance = NR_HUGE; - _second_tolerance = 0; + _second_tolerance = 1; _second_always_snap = false; _transformation = Geom::Point(1,1); - _pointer_distance = 0; + _pointer_distance = NR_HUGE; } 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) + : _point(p), _target(target), _at_intersection(at_intersection), _fully_constrained(fully_constrained), _distance(d), _tolerance(std::max(t,1.0)), _always_snap(a), + _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. _transformation = Geom::Point(1,1); - _pointer_distance = 0; + _pointer_distance = NR_HUGE; } Inkscape::SnappedPoint::SnappedPoint() @@ -37,14 +41,14 @@ Inkscape::SnappedPoint::SnappedPoint() _point = Geom::Point(0,0); _target = SNAPTARGET_UNDEFINED, _distance = NR_HUGE; - _tolerance = 0; + _tolerance = 1; _always_snap = false; _at_intersection = false; _second_distance = NR_HUGE; - _second_tolerance = 0; + _second_tolerance = 1; _second_always_snap = false; _transformation = Geom::Point(1,1); - _pointer_distance = 0; + _pointer_distance = NR_HUGE; } Inkscape::SnappedPoint::~SnappedPoint() @@ -66,7 +70,7 @@ bool getClosestSP(std::list &list, Inkscape::SnappedPoin bool success = false; for (std::list::const_iterator i = list.begin(); i != list.end(); i++) { - if ((i == list.begin()) || (*i).getDistance() < result.getDistance()) { + if ((i == list.begin()) || (*i).getSnapDistance() < result.getSnapDistance()) { result = *i; success = true; } @@ -75,13 +79,39 @@ bool getClosestSP(std::list &list, Inkscape::SnappedPoin return success; } -bool Inkscape::SnappedPoint::isOtherOneBetter(Inkscape::SnappedPoint const &other_one) const +bool Inkscape::SnappedPoint::isOtherOneBetter(Inkscape::SnappedPoint const &other_one, bool weighted) const { - double const w = 0.25; // 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) - // If it's closer - bool c1 = (w * other_one.getPointerDistance() + (1-w) * other_one.getDistance()) < (w * getPointerDistance() + (1-w) * getDistance()); + double dist_other = other_one.getSnapDistance(); + double dist_this = getSnapDistance(); + + // The distance to the pointer should only be taken into account when finding the best snapped source node (when + // there's more than one). It is not useful when trying to find the best snapped target point. + // (both the snap distance and the pointer distance are measured in document pixels, not in screen pixels) + if (weighted) { + // 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); + 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 + // mouse pointer distance must be set. + g_assert(getPointerDistance() != NR_HUGE || other_one.getPointerDistance() != NR_HUGE); + // The snap distance will always be smaller than the tolerance set for the snapper. The pointer distance can + // however be very large. To compare these in a fair way, we will have to normalize these metrics first + // The closest pointer distance will be normalized to 1.0; the other one will be > 1.0 + // The snap distance will be normalized to 1.0 if it's equal to the snapper tolerance + double const norm_p = std::min(getPointerDistance(), other_one.getPointerDistance()); + double const norm_t_other = std::min(50.0, other_one.getTolerance()); + double const norm_t_this = std::min(50.0, getTolerance()); + dist_other = w * other_one.getPointerDistance() / norm_p + (1-w) * dist_other / norm_t_other; + dist_this = w * getPointerDistance() / norm_p + (1-w) * dist_this / norm_t_this; + } + } + + // If it's closer + bool c1 = dist_other < dist_this; // or, if it's for a snapper with "always snap" turned on, and the previous wasn't bool c2 = other_one.getAlwaysSnap() && !getAlwaysSnap(); // But in no case fall back from a snapper with "always snap" on to one with "always snap" off @@ -92,10 +122,10 @@ bool Inkscape::SnappedPoint::isOtherOneBetter(Inkscape::SnappedPoint const &othe bool c3n = !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 c4a = (other_one.getDistance() == getDistance()); - bool c4b = other_one.getSecondDistance() < getSecondDistance(); + bool c4a = (dist_other == dist_this); + bool c4b = other_one.getSecondSnapDistance() < getSecondSnapDistance(); - // std::cout << "c1 = " << c1 << " | c2 = " << c2 << " | c2n = " << c2n << " | c3 = " << c3 << " | c3n = " << c3n << " | c4a = " << c4a << " | c4b = " << c4b; + // std::cout << "c1 = " << c1 << " | c2 = " << c2 << " | c2n = " << c2n << " | c3 = " << c3 << " | c3n = " << c3n << " | c4a = " << c4a << " | c4b = " << c4b << std::endl; return (c1 || c2 || c3 || (c4a && c4b)) && !c2n && (!c3n || c2); } -- cgit v1.2.3