X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fsnap.cpp;h=3dda92ecbcd73504c982a36737f9205cdc57a693;hb=0e2a2cc7bb4f35dbc5150de387b2e651714b6d5b;hp=ca7efde73ee8ed46b235d265494564bc2e9c39fe;hpb=b7313eac22048276ef96257c67805fc94ad7d6ab;p=inkscape.git diff --git a/src/snap.cpp b/src/snap.cpp index ca7efde73..3dda92ecb 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -2,14 +2,14 @@ /** * \file snap.cpp - * - * \brief Various snapping methods + * \brief SnapManager class. * * Authors: * Lauris Kaplinski * Frank Felfe * Carl Hetherington * + * Copyright (C) 2006-2007 Johan Engelen * Copyright (C) 1999-2002 Authors * * Released under GNU GPL, read the file 'COPYING' for more information @@ -21,38 +21,87 @@ #include #include -SnapManager::SnapManager(SPNamedView const *v) : grid(v, 0), guide(v, 0), object(v, 0) +#include "display/canvas-grid.h" + +#include "inkscape.h" +#include "desktop.h" + +/** + * Construct a SnapManager for a SPNamedView. + * + * \param v `Owning' SPNamedView. + */ + +SnapManager::SnapManager(SPNamedView const *v) : + guide(v, 0), + object(v, 0), + _named_view(v) { } -SnapManager::SnapperList SnapManager::getSnappers() const + +/** + * \return List of snappers that we use. + */ +SnapManager::SnapperList +SnapManager::getSnappers() const { SnapManager::SnapperList s; - s.push_back(&grid); s.push_back(&guide); s.push_back(&object); + + SnapManager::SnapperList gs = getGridSnappers(); + s.splice(s.begin(), gs); + + return s; +} + +/** + * \return List of gridsnappers that we use. + */ +SnapManager::SnapperList +SnapManager::getGridSnappers() const +{ + SnapperList s; + + //FIXME: this code should actually do this: add new grid snappers that are active for this desktop. now it just adds all gridsnappers + SPDesktop* desktop = SP_ACTIVE_DESKTOP; + if (desktop && desktop->gridsEnabled()) { + for ( GSList const *l = _named_view->grids; l != NULL; l = l->next) { + Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data; + s.push_back(grid->snapper); + } + } + return s; } /** * \return true if one of the snappers will try to snap something. */ -bool SnapManager::willSnapSomething() const + +bool SnapManager::SomeSnapperMightSnap() const { SnapperList const s = getSnappers(); SnapperList::const_iterator i = s.begin(); - while (i != s.end() && (*i)->willSnapSomething() == false) { + while (i != s.end() && (*i)->ThisSnapperMightSnap() == false) { i++; } + return (i != s.end()); } -/* FIXME: lots of cut-and-paste here. This needs some -** functor voodoo to cut it all down a bit. -*/ +/** + * Try to snap a point to any interested snappers. + * + * \param t Type of point. + * \param p Point. + * \param it Item to ignore when snapping. + * \return Snapped point. + */ Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t, NR::Point const &p, @@ -65,13 +114,41 @@ Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t, } +/** + * Try to snap a point to any interested snappers. + * + * \param t Type of point. + * \param p Point. + * \param it List of items to ignore when snapping. + * \return Snapped point. + */ + Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t, NR::Point const &p, std::list const &it) const +{ + SnapperList const snappers = getSnappers(); + + return freeSnap(t, p, it, snappers); +} + +/** + * Try to snap a point to any of the specified snappers. + * + * \param t Type of point. + * \param p Point. + * \param it List of items to ignore when snapping. + * \param snappers List of snappers to try to snap to + * \return Snapped point. + */ + +Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t, + NR::Point const &p, + std::list const &it, + SnapperList const &snappers) const { Inkscape::SnappedPoint r(p, NR_HUGE); - SnapperList const snappers = getSnappers(); for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) { Inkscape::SnappedPoint const s = (*i)->freeSnap(t, p, it); if (s.getDistance() < r.getDistance()) { @@ -82,6 +159,71 @@ Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t, return r; } +/** + * Try to snap a point to any of the specified snappers. Snap always, ignoring the snap-distance + * + * \param t Type of point. + * \param p Point. + * \param it Item to ignore when snapping. + * \param snappers List of snappers to try to snap to + * \return Snapped point. + */ + +Inkscape::SnappedPoint +SnapManager::freeSnapAlways( Inkscape::Snapper::PointType t, + NR::Point const &p, + SPItem const *it, + SnapperList &snappers ) +{ + std::list lit; + lit.push_back(it); + return freeSnapAlways(t, p, lit, snappers); +} + +/** + * Try to snap a point to any of the specified snappers. Snap always, ignoring the snap-distance + * + * \param t Type of point. + * \param p Point. + * \param it List of items to ignore when snapping. + * \param snappers List of snappers to try to snap to + * \return Snapped point. + */ + +Inkscape::SnappedPoint +SnapManager::freeSnapAlways( Inkscape::Snapper::PointType t, + NR::Point const &p, + std::list const &it, + SnapperList &snappers ) +{ + Inkscape::SnappedPoint r(p, NR_HUGE); + + for (SnapperList::iterator i = snappers.begin(); i != snappers.end(); i++) { + gdouble const curr_gridsnap = (*i)->getDistance(); + const_cast (*i)->setDistance(NR_HUGE); + Inkscape::SnappedPoint const s = (*i)->freeSnap(t, p, it); + const_cast (*i)->setDistance(curr_gridsnap); + + if (s.getDistance() < r.getDistance()) { + r = s; + } + } + + return r; +} + + + +/** + * Try to snap a point to any interested snappers. A snap will only occur along + * a line described by a Inkscape::Snapper::ConstraintLine. + * + * \param t Type of point. + * \param p Point. + * \param c Constraint line. + * \param it Item to ignore when snapping. + * \return Snapped point. + */ Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t, NR::Point const &p, @@ -94,6 +236,18 @@ Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType } + +/** + * Try to snap a point to any interested snappers. A snap will only occur along + * a line described by a Inkscape::Snapper::ConstraintLine. + * + * \param t Type of point. + * \param p Point. + * \param c Constraint line. + * \param it List of items to ignore when snapping. + * \return Snapped point. + */ + Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t, NR::Point const &p, Inkscape::Snapper::ConstraintLine const &c, @@ -113,14 +267,35 @@ Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType } -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 + +/** + * Main internal snapping method, which is called by the other, friendlier, public + * methods. It's a bit hairy as it has lots of parameters, but it saves on a lot + * of duplicated code. + * + * \param type Type of points being snapped. + * \param points List of points to snap. + * \param ignore List of items to ignore while snapping. + * \param constrained true if the snap is constrained. + * \param constraint Constraint line to use, if `constrained' is true, otherwise undefined. + * \param transformation_type Type of transformation to apply to points before trying to snap them. + * \param transformation Description of the transformation; details depend on the type. + * \param origin Origin of the transformation, if applicable. + * \param dim Dimension of the transformation, if applicable. + * \param uniform true if the transformation should be uniform, if applicable. + */ + +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, + NR::Dim2 dim, + bool uniform) 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 @@ -128,16 +303,17 @@ std::pair SnapManager::_snapTransformed(Inkscape::Snapper::Poin */ /* Quick check to see if we have any snappers that are enabled */ - if (willSnapSomething() == false) { + if (SomeSnapperMightSnap() == false) { return std::make_pair(transformation, false); } /* 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; + double best_metric = NR_HUGE; for (std::vector::const_iterator i = points.begin(); i != points.end(); i++) { @@ -150,10 +326,26 @@ std::pair SnapManager::_snapTransformed(Inkscape::Snapper::Poin case SCALE: transformed = ((*i - origin) * NR::scale(transformation[NR::X], transformation[NR::Y])) + origin; break; + case STRETCH: + { + NR::scale s(1, 1); + if (uniform) + s[NR::X] = s[NR::Y] = transformation[dim]; + else { + s[dim] = transformation[dim]; + s[1 - dim] = 1; + } + transformed = ((*i - origin) * s) + origin; + break; + } + case SKEW: + transformed = *i; + transformed[dim] += transformation[dim] * ((*i)[1 - dim] - origin[1 - dim]); + break; default: g_assert_not_reached(); } - + /* Snap it */ Inkscape::SnappedPoint const snapped = constrained ? constrainedSnap(type, transformed, constraint, ignore) : freeSnap(type, transformed, ignore); @@ -170,11 +362,31 @@ std::pair SnapManager::_snapTransformed(Inkscape::Snapper::Poin 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; + } + case STRETCH: + { + for (int j = 0; j < 2; j++) { + if (uniform || j == dim) { + result[j] = (snapped.getPoint()[dim] - origin[dim]) / ((*i)[dim] - origin[dim]); + } else { + result[j] = 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]); + break; + default: + g_assert_not_reached(); } /* Note it if it's the best so far */ @@ -184,20 +396,47 @@ std::pair SnapManager::_snapTransformed(Inkscape::Snapper::Poin } } } - - return std::make_pair(best_transformation, best_metric < NR_HUGE); + + // 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); } +/** + * Try to snap a list of points to any interested snappers after they have undergone + * a translation. + * + * \param t Type of points. + * \param p Points. + * \param it List of items to ignore when snapping. + * \param tr Proposed translation. + * \return Snapped translation, if a snap occurred, and a flag indicating whether a snap occurred. + */ + 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)); + return _snapTransformed( + t, p, it, false, NR::Point(), TRANSLATION, tr, NR::Point(), NR::X, false + ); } +/** + * Try to snap a list of points to any interested snappers after they have undergone a + * translation. A snap will only occur along a line described by a + * Inkscape::Snapper::ConstraintLine. + * + * \param t Type of points. + * \param p Points. + * \param it List of items to ignore when snapping. + * \param c Constraint line. + * \param tr Proposed translation. + * \return Snapped translation, if a snap occurred, and a flag indicating whether a snap occurred. + */ std::pair SnapManager::constrainedSnapTranslation(Inkscape::Snapper::PointType t, std::vector const &p, @@ -205,19 +444,49 @@ std::pair SnapManager::constrainedSnapTranslation(Inkscape::Sna Inkscape::Snapper::ConstraintLine const &c, NR::Point const &tr) const { - return _snapTransformed(t, p, it, true, c, TRANSLATION, tr, NR::Point(0, 0)); + return _snapTransformed( + t, p, it, true, c, TRANSLATION, tr, NR::Point(), NR::X, false + ); } + +/** + * Try to snap a list of points to any interested snappers after they have undergone + * a scale. + * + * \param t Type of points. + * \param p Points. + * \param it List of items to ignore when snapping. + * \param s Proposed scale. + * \param o Origin of proposed scale. + * \return Snapped scale, if a snap occurred, and a flag indicating whether a snap occurred. + */ + 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); + return _snapTransformed( + t, p, it, false, NR::Point(), SCALE, NR::Point(s[NR::X], s[NR::Y]), o, NR::X, false + ); } +/** + * Try to snap a list of points to any interested snappers after they have undergone + * a scale. A snap will only occur along a line described by a + * Inkscape::Snapper::ConstraintLine. + * + * \param t Type of points. + * \param p Points. + * \param it List of items to ignore when snapping. + * \param s Proposed scale. + * \param o Origin of proposed scale. + * \return Snapped scale, if a snap occurred, and a flag indicating whether a snap occurred. + */ + std::pair SnapManager::constrainedSnapScale(Inkscape::Snapper::PointType t, std::vector const &p, std::list const &it, @@ -225,204 +494,69 @@ std::pair SnapManager::constrainedSnapScale(Inkscape::Snapper:: 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. -static const double MIN_DIST_NORM = 1.0; - -/** - * Try to snap \a req in one dimension. - * - * \param nv NamedView to use. - * \param req Point to snap; updated to the snapped point if a snap occurred. - * \param dim Dimension to snap in. - * \return Distance to the snap point along the \a dim axis, or \c NR_HUGE - * if no snap occurred. - */ -NR::Coord namedview_dim_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t, NR::Point &req, - NR::Dim2 const dim, SPItem const *it) -{ - return namedview_vector_snap(nv, t, req, component_vectors[dim], it); + return _snapTransformed( + t, p, it, true, c, SCALE, NR::Point(s[NR::X], s[NR::Y]), o, NR::X, false + ); } -NR::Coord namedview_dim_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t, NR::Point &req, - NR::Dim2 const dim, std::list const &it) -{ - return namedview_vector_snap(nv, t, req, component_vectors[dim], it); -} - - -NR::Coord namedview_vector_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t, - NR::Point &req, NR::Point const &d, - SPItem const *it) -{ - std::list lit; - lit.push_back(it); - return namedview_vector_snap(nv, t, req, d, lit); -} /** - * Look for snap point along the line described by the point \a req - * and the direction vector \a d. - * Modifies req to the snap point, if one is found. - * \return The distance from \a req to the snap point along the vector \a d, - * or \c NR_HUGE if no snap point was found. + * Try to snap a list of points to any interested snappers after they have undergone + * a stretch. * - * \pre d â‰  (0, 0). + * \param t Type of points. + * \param p Points. + * \param it List of items to ignore when snapping. + * \param s Proposed stretch. + * \param o Origin of proposed stretch. + * \param d Dimension in which to apply proposed stretch. + * \param u true if the stretch should be uniform (ie to be applied equally in both dimensions) + * \return Snapped stretch, if a snap occurred, and a flag indicating whether a snap occurred. */ -NR::Coord namedview_vector_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t, - NR::Point &req, NR::Point const &d, - std::list const &it) -{ - g_assert(nv != NULL); - g_assert(SP_IS_NAMEDVIEW(nv)); - SnapManager::SnapperList const snappers = nv->snap_manager.getSnappers(); - - NR::Coord best = NR_HUGE; - for (SnapManager::SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) { - Inkscape::SnappedPoint const s = (*i)->constrainedSnap(t, req, d, it); - if (s.getDistance() < best) { - req = s.getPoint(); - best = s.getDistance(); - } - } - - return best; -} - - -/* - * functions for lists of points - * - * All functions take a list of NR::Point and parameter indicating the proposed transformation. - * They return the updated transformation parameter. - */ - -/** - * Snap list of points in two dimensions. - */ -std::pair namedview_vector_snap_list(SPNamedView const *nv, Inkscape::Snapper::PointType t, - const std::vector &p, NR::Point const &norm, - NR::scale const &s, std::list const &it) +std::pair SnapManager::freeSnapStretch(Inkscape::Snapper::PointType t, + std::vector const &p, + std::list const &it, + NR::Coord const &s, + NR::Point const &o, + NR::Dim2 d, + bool u) const { - using NR::X; - using NR::Y; + std::pair const r = _snapTransformed( + t, p, it, false, NR::Point(), STRETCH, NR::Point(s, s), o, d, u + ); - SnapManager const &m = nv->snap_manager; - - if (m.willSnapSomething() == false) { - return std::make_pair(s[X], false); - } - - NR::Coord dist = NR_HUGE; - double ratio = fabs(s[X]); - for (std::vector::const_iterator i = p.begin(); i != p.end(); i++) { - NR::Point const &q = *i; - NR::Point check = ( q - norm ) * s + norm; - if (NR::LInfty( q - norm ) > MIN_DIST_NORM) { - NR::Coord d = namedview_vector_snap(nv, t, check, check - norm, it); - if (d < dist) { - dist = d; - NR::Dim2 const dominant = ( ( fabs( q[X] - norm[X] ) > - fabs( q[Y] - norm[Y] ) ) - ? X - : Y ); - ratio = ( ( check[dominant] - norm[dominant] ) - / ( q[dominant] - norm[dominant] ) ); - } - } - } - - return std::make_pair(ratio, dist < NR_HUGE); + return std::make_pair(r.first[d], r.second); } - + /** - * Try to snap points in \a p after they have been scaled by \a sx with respect to - * the origin \a norm. The best snap is the one that changes the scale least. + * Try to snap a list of points to any interested snappers after they have undergone + * a skew. * - * \return Pair containing snapped scale and a flag which is true if a snap was made. + * \param t Type of points. + * \param p Points. + * \param it List of items to ignore when snapping. + * \param s Proposed skew. + * \param o Origin of proposed skew. + * \param d Dimension in which to apply proposed skew. + * \return Snapped skew, if a snap occurred, and a flag indicating whether a snap occurred. */ -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, NR::Dim2 dim, - std::list const &it) -{ - SnapManager const &m = nv->snap_manager; - if (m.willSnapSomething() == false) { - return std::make_pair(sx, false); - } - g_assert(dim < 2); - - NR::Coord dist = NR_HUGE; - double scale = sx; - - for (std::vector::const_iterator i = p.begin(); i != p.end(); i++) { - NR::Point q = *i; - NR::Point check = q; - - /* Scaled version of the point we are looking at */ - check[dim] = (sx * (q - norm) + norm)[dim]; - - if (fabs (q[dim] - norm[dim]) > MIN_DIST_NORM) { - /* Snap this point */ - const NR::Coord d = namedview_dim_snap (nv, t, check, dim, it); - /* Work out the resulting scale factor */ - double snapped_scale = (check[dim] - norm[dim]) / (q[dim] - norm[dim]); - - if (dist == NR_HUGE || fabs(snapped_scale - sx) < fabs(scale - sx)) { - /* This is either the first point, or the snapped scale - ** is the closest yet to the original. - */ - scale = snapped_scale; - dist = d; - } - } - } - - return std::make_pair(scale, dist < NR_HUGE); -} - -/** - * Try to snap points after they have been skewed. - */ -double namedview_dim_snap_list_skew(SPNamedView const *nv, Inkscape::Snapper::PointType t, - const std::vector &p, NR::Point const &norm, - double const sx, NR::Dim2 const dim) +std::pair SnapManager::freeSnapSkew(Inkscape::Snapper::PointType t, + std::vector const &p, + std::list const &it, + NR::Coord const &s, + NR::Point const &o, + NR::Dim2 d) const { - SnapManager const &m = nv->snap_manager; - - if (m.willSnapSomething() == false) { - return sx; - } + std::pair const r = _snapTransformed( + t, p, it, false, NR::Point(), SKEW, NR::Point(s, s), o, d, false + ); - g_assert(dim < 2); - - gdouble dist = NR_HUGE; - gdouble skew = sx; - - for (std::vector::const_iterator i = p.begin(); i != p.end(); i++) { - NR::Point q = *i; - NR::Point check = q; - // apply shear - check[dim] += sx * (q[!dim] - norm[!dim]); - if (fabs (q[!dim] - norm[!dim]) > MIN_DIST_NORM) { - const gdouble d = namedview_dim_snap (nv, t, check, dim, NULL); - if (d < fabs (dist)) { - dist = d; - skew = (check[dim] - q[dim]) / (q[!dim] - norm[!dim]); - } - } - } - - return skew; + return std::make_pair(r.first[d], r.second); } - /* Local Variables: mode:c++