From b7313eac22048276ef96257c67805fc94ad7d6ab Mon Sep 17 00:00:00 2001 From: cth103 Date: Fri, 5 May 2006 14:22:41 +0000 Subject: [PATCH] Various snapping cleanups and bug fixes. --- src/context-fns.cpp | 11 ++-- src/draw-context.cpp | 2 +- src/line-snapper.cpp | 10 ++- src/line-snapper.h | 2 +- src/nodepath.cpp | 2 +- src/object-snapper.cpp | 2 +- src/object-snapper.h | 2 +- src/seltrans.cpp | 103 +++++++++++++++++------------ src/snap.cpp | 146 +++++++++++++++++++++++++++-------------- src/snap.h | 39 +++++++++-- src/snapper.cpp | 4 +- src/snapper.h | 31 ++++++++- 12 files changed, 236 insertions(+), 118 deletions(-) diff --git a/src/context-fns.cpp b/src/context-fns.cpp index 5db98ec16..f11b2fcfa 100644 --- a/src/context-fns.cpp +++ b/src/context-fns.cpp @@ -108,12 +108,12 @@ NR::Rect Inkscape::snap_rectangular_box(SPDesktop const *desktop, SPItem *item, Inkscape::SnappedPoint s[2]; /* Try to snap p[0] (the opposite corner) along the constraint vector */ - s[0] = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, - p[0], p[0] - p[1], item); + s[0] = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, p[0], + Inkscape::Snapper::ConstraintLine(p[0] - p[1]), item); /* Try to snap p[1] (the dragged corner) along the constraint vector */ - s[1] = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, - p[1], p[1] - p[0], item); + s[1] = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, p[1], + Inkscape::Snapper::ConstraintLine(p[1] - p[0]), item); /* Choose the best snap and update points accordingly */ if (s[0].getDistance() < s[1].getDistance()) { @@ -128,7 +128,8 @@ NR::Rect Inkscape::snap_rectangular_box(SPDesktop const *desktop, SPItem *item, /* Our origin is the opposite corner. Snap the drag point along the constraint vector */ p[0] = center; - p[1] = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, p[1], p[1] - p[0], item).getPoint(); + p[1] = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, p[1], + Inkscape::Snapper::ConstraintLine(p[1] - p[0]), item).getPoint(); } } else if (shift) { diff --git a/src/draw-context.cpp b/src/draw-context.cpp index aa1480c67..75ded20f5 100644 --- a/src/draw-context.cpp +++ b/src/draw-context.cpp @@ -374,7 +374,7 @@ void spdc_endpoint_snap_rotation(SPEventContext const *const ec, NR::Point &p, N /* Snap it along best vector */ SnapManager const &m = SP_EVENT_CONTEXT_DESKTOP(ec)->namedview->snap_manager; p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT | Inkscape::Snapper::BBOX_POINT, - p, best, NULL).getPoint(); + p, Inkscape::Snapper::ConstraintLine(best), NULL).getPoint(); } } diff --git a/src/line-snapper.cpp b/src/line-snapper.cpp index 383c1fb96..a2c6b944b 100644 --- a/src/line-snapper.cpp +++ b/src/line-snapper.cpp @@ -35,23 +35,21 @@ Inkscape::SnappedPoint Inkscape::LineSnapper::_doFreeSnap(NR::Point const &p, } Inkscape::SnappedPoint Inkscape::LineSnapper::_doConstrainedSnap(NR::Point const &p, - NR::Point const &c, + ConstraintLine const &c, std::list const &it) const { Inkscape::SnappedPoint s = SnappedPoint(p, NR_HUGE); - NR::Point const v = NR::unit_vector(c); - /* 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(v)); + NR::Point const n(NR::rot90(NR::unit_vector(c.getDirection()))); - /* Hence constant term of the line we're trying to snap along */ - NR::Coord const q = dot(n, p); + /* Constant term of the line we're trying to snap along */ + NR::Coord const q = dot(n, c.hasPoint() ? c.getPoint() : p); /* Try to intersect this line with the target line */ NR::Point t = p; diff --git a/src/line-snapper.h b/src/line-snapper.h index 581466d33..2816b2ec0 100644 --- a/src/line-snapper.h +++ b/src/line-snapper.h @@ -29,7 +29,7 @@ private: std::list const &it) const; SnappedPoint _doConstrainedSnap(NR::Point const &p, - NR::Point const &c, + ConstraintLine const &c, std::list const &it) const; /** diff --git a/src/nodepath.cpp b/src/nodepath.cpp index 64611f12c..7eb68d7d6 100644 --- a/src/nodepath.cpp +++ b/src/nodepath.cpp @@ -2852,7 +2852,7 @@ static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpo NR::Coord const scal = dot(delta, ndelta) / linelen; (*p) = n->pos + (scal / linelen) * ndelta; } - *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, ndelta, NULL).getPoint(); + *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint(); } else { *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint(); } diff --git a/src/object-snapper.cpp b/src/object-snapper.cpp index 9a3f61525..e7a975d01 100644 --- a/src/object-snapper.cpp +++ b/src/object-snapper.cpp @@ -167,7 +167,7 @@ Inkscape::SnappedPoint Inkscape::ObjectSnapper::_doFreeSnap(NR::Point const &p, Inkscape::SnappedPoint Inkscape::ObjectSnapper::_doConstrainedSnap(NR::Point const &p, - NR::Point const &c, + ConstraintLine const &c, std::list const &it) const { /* FIXME: this needs implementing properly; I think we have to do the diff --git a/src/object-snapper.h b/src/object-snapper.h index 189e96e3e..4e74b2f74 100644 --- a/src/object-snapper.h +++ b/src/object-snapper.h @@ -48,7 +48,7 @@ private: std::list const &it) const; SnappedPoint _doConstrainedSnap(NR::Point const &p, - NR::Point const &c, + ConstraintLine const &c, std::list const &it) const; void _findCandidates(std::list& c, diff --git a/src/seltrans.cpp b/src/seltrans.cpp index d6628ea72..2d4201fac 100644 --- a/src/seltrans.cpp +++ b/src/seltrans.cpp @@ -769,6 +769,8 @@ gboolean Inkscape::SelTrans::scaleRequest(NR::Point &pt, guint state) } } + SnapManager const &m = _desktop->namedview->snap_manager; + /* Get a STL list of the selected items. ** FIXME: this should probably be done by Inkscape::Selection. */ @@ -778,50 +780,60 @@ gboolean Inkscape::SelTrans::scaleRequest(NR::Point &pt, guint state) } if ((state & GDK_CONTROL_MASK) || _desktop->isToolboxButtonActive ("lock")) { - /* Scale is locked to a 1:1 aspect ratio, so that s[X] must be made to equal s[Y] */ - - NR::Dim2 locked_dim; - - /* Lock aspect ratio, using the smaller of the x and y factors */ - if (fabs(s[NR::X]) > fabs(s[NR::Y])) { - s[NR::X] = fabs(s[NR::Y]) * sign(s[NR::X]); - locked_dim = NR::X; - } else { - s[NR::Y] = fabs(s[NR::X]) * sign(s[NR::Y]); - locked_dim = NR::Y; - } - - /* Snap the scale factor */ - std::pair bb = namedview_vector_snap_list(_desktop->namedview, - Snapper::BBOX_POINT, _bbox_points, - _origin, s, it); - std::pair sn = namedview_vector_snap_list(_desktop->namedview, - Snapper::SNAP_POINT, _snap_points, - _origin, s, it); - - double bd = bb.second ? fabs(bb.first - s[locked_dim]) : NR_HUGE; - double sd = sn.second ? fabs(sn.first - s[locked_dim]) : NR_HUGE; - double r = (bd < sd) ? bb.first : sn.first; - - for ( unsigned int i = 0 ; i < 2 ; i++ ) { - s[i] = r * sign(s[i]); - } + /* Scale is locked to a 1:1 aspect ratio, so that s[X] must be made to equal s[Y]. + ** To do this, we snap along a suitable constraint vector from the origin. + */ + NR::Point const cv = NR::Point( + pt[NR::X] > _origin[NR::X] ? 1 : -1, + pt[NR::Y] > _origin[NR::Y] ? 1 : -1 + ); + + std::pair bb = m.constrainedSnapScale(Snapper::BBOX_POINT, + _bbox_points, + it, + Snapper::ConstraintLine(_origin, cv), + s, + _origin); + + std::pair sn = m.constrainedSnapScale(Snapper::SNAP_POINT, + _snap_points, + it, + Snapper::ConstraintLine(_origin, cv), + s, + _origin); + + /* Choose the smaller difference in scale. Since s[X] == s[Y] we can + ** just compare difference in s[X]. + */ + double const bd = bb.second ? fabs(bb.first[NR::X] - s[NR::X]) : NR_HUGE; + double const sd = sn.second ? fabs(sn.first[NR::X] - s[NR::X]) : NR_HUGE; + s = (bd < sd) ? bb.first : sn.first; + } else { /* Scale aspect ratio is unlocked */ - for ( unsigned int i = 0 ; i < 2 ; i++ ) { - std::pair bb = namedview_dim_snap_list_scale(_desktop->namedview, - Snapper::BBOX_POINT, _bbox_points, - _origin, s[i], NR::Dim2(i), it); - std::pair sn = namedview_dim_snap_list_scale(_desktop->namedview, - Snapper::SNAP_POINT, _snap_points, - _origin, s[i], NR::Dim2(i), it); - - /* Pick the snap that puts us closest to the original scale */ - NR::Coord bd = bb.second ? fabs(bb.first - s[i]) : NR_HUGE; - NR::Coord sd = sn.second ? fabs(sn.first - s[i]) : NR_HUGE; - s[i] = (bd < sd) ? bb.first : sn.first; - } + + std::pair bb = m.freeSnapScale(Snapper::BBOX_POINT, + _bbox_points, + it, + s, + _origin); + std::pair sn = m.freeSnapScale(Snapper::SNAP_POINT, + _snap_points, + it, + s, + _origin); + + /* Pick the snap that puts us closest to the original scale */ + NR::Coord bd = bb.second ? + fabs(NR::L2(NR::Point(bb.first[NR::X], bb.first[NR::Y])) - + NR::L2(NR::Point(s[NR::X], s[NR::Y]))) + : NR_HUGE; + NR::Coord sd = sn.second ? + fabs(NR::L2(NR::Point(sn.first[NR::X], sn.first[NR::Y])) - + NR::L2(NR::Point(s[NR::X], s[NR::Y]))) + : NR_HUGE; + s = (bd < sd) ? bb.first : sn.first; } /* Update the knot position */ @@ -1287,10 +1299,15 @@ void Inkscape::SelTrans::moveTo(NR::Point const &xy, guint state) for (unsigned int dim = 0; dim < 2; dim++) { s.push_back(m.constrainedSnapTranslation(Inkscape::Snapper::BBOX_POINT, _bbox_points, - component_vectors[dim], it, dxy)); + it, + Inkscape::Snapper::ConstraintLine(component_vectors[dim]), + dxy)); + s.push_back(m.constrainedSnapTranslation(Inkscape::Snapper::SNAP_POINT, _snap_points, - component_vectors[dim], it, dxy)); + it, + Inkscape::Snapper::ConstraintLine(component_vectors[dim]), + dxy)); } } else { diff --git a/src/snap.cpp b/src/snap.cpp index 452a1e496..ca7efde73 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -21,7 +21,7 @@ #include #include -SnapManager::SnapManager(SPNamedView* v) : grid(v, 0), guide(v, 0), object(v, 0) +SnapManager::SnapManager(SPNamedView const *v) : grid(v, 0), guide(v, 0), object(v, 0) { } @@ -85,7 +85,7 @@ Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t, Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t, NR::Point const &p, - NR::Point const &c, + Inkscape::Snapper::ConstraintLine const &c, SPItem const *it) const { std::list lit; @@ -96,7 +96,7 @@ Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t, NR::Point const &p, - NR::Point const &c, + Inkscape::Snapper::ConstraintLine const &c, std::list const &it) const { Inkscape::SnappedPoint r(p, NR_HUGE); @@ -113,74 +113,120 @@ Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType } -std::pair SnapManager::freeSnapTranslation(Inkscape::Snapper::PointType t, - std::vector const &p, - std::list const &it, - NR::Point const &tr) const +std::pair SnapManager::_snapTransformed(Inkscape::Snapper::PointType type, + std::vector const &points, + std::list const &ignore, + bool constrained, + Inkscape::Snapper::ConstraintLine const &constraint, + Transformation transformation_type, + NR::Point const &transformation, + NR::Point const &origin) const { + /* We have a list of points, which we are proposing to transform in some way. We need to see + ** if any of these points, when transformed, snap to anything. If they do, we return the + ** appropriate transformation with `true'; otherwise we return the original scale with `false'. + */ + + /* Quick check to see if we have any snappers that are enabled */ if (willSnapSomething() == false) { - return std::make_pair(tr, false); + return std::make_pair(transformation, false); } - NR::Point best_translation = tr; - NR::Coord best_distance = NR_HUGE; - - for (std::vector::const_iterator i = p.begin(); i != p.end(); i++) { - /* Translated version of this point */ - NR::Point const q = *i + tr; + /* The current best transformation */ + NR::Point best_transformation = transformation; + /* The current best metric for the best transformation; lower is better, NR_HUGE + ** means that we haven't snapped anything. + */ + NR::Coord best_metric = NR_HUGE; + + for (std::vector::const_iterator i = points.begin(); i != points.end(); i++) { + + /* Work out the transformed version of this point */ + NR::Point transformed; + switch (transformation_type) { + case TRANSLATION: + transformed = *i + transformation; + break; + case SCALE: + transformed = ((*i - origin) * NR::scale(transformation[NR::X], transformation[NR::Y])) + origin; + break; + default: + g_assert_not_reached(); + } + /* Snap it */ - Inkscape::SnappedPoint s = freeSnap(t, q, it); - if (s.getDistance() < NR_HUGE) { - /* Resulting translation */ - NR::Point const r = s.getPoint() - *i; - NR::Coord const d = NR::L2(r); - if (d < best_distance) { - best_distance = d; - best_translation = r; + Inkscape::SnappedPoint const snapped = constrained ? + constrainedSnap(type, transformed, constraint, ignore) : freeSnap(type, transformed, ignore); + + if (snapped.getDistance() < NR_HUGE) { + /* We snapped. Find the transformation that describes where the snapped point has + ** ended up, and also the metric for this transformation. + */ + NR::Point result; + NR::Coord metric; + switch (transformation_type) { + case TRANSLATION: + result = snapped.getPoint() - *i; + metric = NR::L2(result); + break; + case SCALE: + NR::Point const a = (snapped.getPoint() - origin); + NR::Point const b = (*i - origin); + result = NR::Point(a[NR::X] / b[NR::X], a[NR::Y] / b[NR::Y]); + metric = std::abs(NR::L2(result) - NR::L2(transformation)); + break; + } + + /* Note it if it's the best so far */ + if (metric < best_metric && metric != 0) { + best_transformation = result; + best_metric = metric; } } } + + return std::make_pair(best_transformation, best_metric < NR_HUGE); +} + - return std::make_pair(best_translation, best_distance < NR_HUGE); +std::pair SnapManager::freeSnapTranslation(Inkscape::Snapper::PointType t, + std::vector const &p, + std::list const &it, + NR::Point const &tr) const +{ + return _snapTransformed(t, p, it, false, NR::Point(), TRANSLATION, tr, NR::Point(0, 0)); } std::pair SnapManager::constrainedSnapTranslation(Inkscape::Snapper::PointType t, std::vector const &p, - NR::Point const &c, std::list const &it, + Inkscape::Snapper::ConstraintLine const &c, NR::Point const &tr) const { - if (willSnapSomething() == false) { - return std::make_pair(tr, false); - } - - NR::Point best_translation = tr; - NR::Coord best_distance = NR_HUGE; - - for (std::vector::const_iterator i = p.begin(); i != p.end(); i++) { - /* Translated version of this point */ - NR::Point const q = *i + tr; - /* Snap it */ - Inkscape::SnappedPoint s = constrainedSnap(t, q, c, it); - if (s.getDistance() < NR_HUGE) { - /* Resulting translation */ - NR::Point const r = s.getPoint() - *i; - NR::Coord const d = NR::L2(r); - if (d < best_distance) { - best_distance = d; - best_translation = r; - } - } - } - - return std::make_pair(best_translation, best_distance < NR_HUGE); + return _snapTransformed(t, p, it, true, c, TRANSLATION, tr, NR::Point(0, 0)); } +std::pair SnapManager::freeSnapScale(Inkscape::Snapper::PointType t, + std::vector const &p, + std::list const &it, + NR::scale const &s, + NR::Point const &o) const +{ + return _snapTransformed(t, p, it, false, NR::Point(0, 0), SCALE, NR::Point(s[NR::X], s[NR::Y]), o); +} - +std::pair SnapManager::constrainedSnapScale(Inkscape::Snapper::PointType t, + std::vector const &p, + std::list const &it, + Inkscape::Snapper::ConstraintLine const &c, + NR::scale const &s, + NR::Point const &o) const +{ + return _snapTransformed(t, p, it, true, c, SCALE, NR::Point(s[NR::X], s[NR::Y]), o); +} /// Minimal distance to norm before point is considered for snap. @@ -292,7 +338,7 @@ std::pair namedview_vector_snap_list(SPNamedView const *nv, Inksca return std::make_pair(ratio, dist < NR_HUGE); } - + /** * Try to snap points in \a p after they have been scaled by \a sx with respect to diff --git a/src/snap.h b/src/snap.h index c77e8df41..a37878f9c 100644 --- a/src/snap.h +++ b/src/snap.h @@ -29,7 +29,7 @@ class SPNamedView; class SnapManager { public: - SnapManager(SPNamedView* v); + SnapManager(SPNamedView const *v); bool willSnapSomething() const; @@ -43,12 +43,12 @@ public: Inkscape::SnappedPoint constrainedSnap(Inkscape::Snapper::PointType t, NR::Point const &p, - NR::Point const &c, + Inkscape::Snapper::ConstraintLine const &c, SPItem const *it) const; Inkscape::SnappedPoint constrainedSnap(Inkscape::Snapper::PointType t, NR::Point const &p, - NR::Point const &c, + Inkscape::Snapper::ConstraintLine const &c, std::list const &it) const; std::pair freeSnapTranslation(Inkscape::Snapper::PointType t, @@ -58,16 +58,46 @@ public: std::pair constrainedSnapTranslation(Inkscape::Snapper::PointType t, std::vector const &p, - NR::Point const &c, std::list const &it, + Inkscape::Snapper::ConstraintLine const &c, NR::Point const &tr) const; + std::pair freeSnapScale(Inkscape::Snapper::PointType t, + std::vector const &p, + std::list const &it, + NR::scale const &s, + NR::Point const &o) const; + + std::pair constrainedSnapScale(Inkscape::Snapper::PointType t, + std::vector const &p, + std::list const &it, + Inkscape::Snapper::ConstraintLine const &c, + NR::scale const &s, + NR::Point const &o) const; + Inkscape::GridSnapper grid; Inkscape::GuideSnapper guide; Inkscape::ObjectSnapper object; typedef std::list SnapperList; SnapperList getSnappers() const; + +private: + + enum Transformation + { + TRANSLATION, + SCALE + }; + + std::pair _snapTransformed(Inkscape::Snapper::PointType type, + std::vector const &points, + std::list const &ignore, + bool constrained, + Inkscape::Snapper::ConstraintLine const &constraint, + Transformation transformation_type, + NR::Point const &transformation, + NR::Point const &origin) const; }; @@ -89,6 +119,7 @@ std::pair namedview_vector_snap_list(SPNamedView const *nv, std::list const &it ); + std::pair namedview_dim_snap_list_scale(SPNamedView const *nv, Inkscape::Snapper::PointType t, const std::vector &p, NR::Point const &norm, double const sx, diff --git a/src/snapper.cpp b/src/snapper.cpp index fd05809c1..963cf7c9d 100644 --- a/src/snapper.cpp +++ b/src/snapper.cpp @@ -136,7 +136,7 @@ Inkscape::SnappedPoint Inkscape::Snapper::freeSnap(PointType t, Inkscape::SnappedPoint Inkscape::Snapper::constrainedSnap(PointType t, NR::Point const &p, - NR::Point const &c, + ConstraintLine const &c, SPItem const *it) const { std::list lit; @@ -158,7 +158,7 @@ Inkscape::SnappedPoint Inkscape::Snapper::constrainedSnap(PointType t, Inkscape::SnappedPoint Inkscape::Snapper::constrainedSnap(PointType t, NR::Point const &p, - NR::Point const &c, + ConstraintLine const &c, std::list const &it) const { if (getSnapTo(t) == false) { diff --git a/src/snapper.h b/src/snapper.h index 2f3c487d6..33a15bd04 100644 --- a/src/snapper.h +++ b/src/snapper.h @@ -54,14 +54,39 @@ public: NR::Point const &p, std::list const &it) const; + class ConstraintLine + { + public: + ConstraintLine(NR::Point const &d) : _has_point(false), _direction(d) {} + ConstraintLine(NR::Point const &p, NR::Point const &d) : _has_point(true), _point(p), _direction(d) {} + + bool hasPoint() const { + return _has_point; + } + + NR::Point getPoint() const { + return _point; + } + + NR::Point getDirection() const { + return _direction; + } + + private: + + bool _has_point; + NR::Point _point; + NR::Point _direction; + }; + SnappedPoint constrainedSnap(PointType t, NR::Point const &p, - NR::Point const &c, + ConstraintLine const &c, SPItem const *it) const; SnappedPoint constrainedSnap(PointType t, NR::Point const &p, - NR::Point const &c, + ConstraintLine const &c, std::list const &it) const; protected: SPNamedView const *_named_view; @@ -91,7 +116,7 @@ private: * \return Snapped point. */ virtual SnappedPoint _doConstrainedSnap(NR::Point const &p, - NR::Point const &c, + ConstraintLine const &c, std::list const &it) const = 0; ::NR::Coord _distance; ///< snap distance (desktop coordinates) -- 2.39.5