From e1da05d7d6b8a6d4ddbca0fd1d7e633a84b2c1cf Mon Sep 17 00:00:00 2001 From: dvlierop2 Date: Sat, 3 Nov 2007 14:48:50 +0000 Subject: [PATCH] Groundwork to snap to intersections, e.g. intersections of gridlines with guidelines, and of objects with other objects. The snappers used to return only SnappedPoints, but now also SnappedLines and SnappedInfiniteLines can be returned. SnappedPaths will be implemented later. --- src/display/canvas-axonomgrid.cpp | 7 ++ src/display/canvas-axonomgrid.h | 3 +- src/display/canvas-grid.cpp | 5 + src/display/canvas-grid.h | 2 +- src/geom.cpp | 2 +- src/geom.h | 5 + src/guide-snapper.cpp | 6 + src/guide-snapper.h | 6 +- src/line-snapper.cpp | 53 ++++----- src/line-snapper.h | 8 +- src/object-snapper.cpp | 54 +++++---- src/object-snapper.h | 11 +- src/snap.cpp | 164 ++++++++++++++++++--------- src/snap.h | 14 +-- src/snapped-line.cpp | 180 ++++++++++++++++++++++++++++++ src/snapped-line.h | 76 +++++++++++++ src/snapped-point.cpp | 35 +++--- src/snapped-point.h | 32 +++--- src/snapper.cpp | 28 +++-- src/snapper.h | 30 +++-- 20 files changed, 545 insertions(+), 176 deletions(-) create mode 100644 src/snapped-line.cpp create mode 100644 src/snapped-line.h diff --git a/src/display/canvas-axonomgrid.cpp b/src/display/canvas-axonomgrid.cpp index c00e2ea8f..66c9cc1fa 100644 --- a/src/display/canvas-axonomgrid.cpp +++ b/src/display/canvas-axonomgrid.cpp @@ -650,6 +650,13 @@ CanvasAxonomGridSnapper::_getSnapLines(NR::Point const &p) const return s; } +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 +{ + SnappedInfiniteLine dummy = SnappedInfiniteLine(snapped_point, snapped_distance, normal_to_line, point_on_line); + sc.grid_lines.push_back(dummy); +} + + }; // namespace Inkscape diff --git a/src/display/canvas-axonomgrid.h b/src/display/canvas-axonomgrid.h index 157982416..68c011d30 100644 --- a/src/display/canvas-axonomgrid.h +++ b/src/display/canvas-axonomgrid.h @@ -92,7 +92,8 @@ public: private: LineList _getSnapLines(NR::Point const &p) const; - + void _addSnappedLine(SnappedConstraints &sc, NR::Point const snapped_point, NR::Coord const snapped_distance, NR::Point const normal_to_line, const NR::Point point_on_line) const; + CanvasAxonomGrid *grid; }; diff --git a/src/display/canvas-grid.cpp b/src/display/canvas-grid.cpp index e9eabad10..d3cbf9153 100644 --- a/src/display/canvas-grid.cpp +++ b/src/display/canvas-grid.cpp @@ -850,6 +850,11 @@ CanvasXYGridSnapper::_getSnapLines(NR::Point const &p) const return s; } +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 +{ + SnappedInfiniteLine dummy = SnappedInfiniteLine(snapped_point, snapped_distance, normal_to_line, point_on_line); + sc.grid_lines.push_back(dummy); +} diff --git a/src/display/canvas-grid.h b/src/display/canvas-grid.h index 40e37c44f..ec5e52c90 100644 --- a/src/display/canvas-grid.h +++ b/src/display/canvas-grid.h @@ -162,7 +162,7 @@ public: private: LineList _getSnapLines(NR::Point const &p) const; - + void _addSnappedLine(SnappedConstraints &sc, NR::Point const snapped_point, NR::Coord const snapped_distance, NR::Point const normal_to_line, const NR::Point point_on_line) const; CanvasXYGrid *grid; }; diff --git a/src/geom.cpp b/src/geom.cpp index e59b0f302..5072b0c0e 100644 --- a/src/geom.cpp +++ b/src/geom.cpp @@ -17,7 +17,7 @@ * intersection; otherwise, \a result remains unchanged. * * This function finds the intersection of the two lines (infinite) - * defined by n0.X = d0 and x1.X = d1. The algorithm is as follows: + * defined by n0.X = d0 and n1.X = d1. The algorithm is as follows: * To compute the intersection point use Cramer's rule: * (see http://en.wikipedia.org/wiki/Cramer%27s_rule) * \verbatim diff --git a/src/geom.h b/src/geom.h index d00a4e37c..27e2533a2 100644 --- a/src/geom.h +++ b/src/geom.h @@ -1,3 +1,6 @@ +#ifndef SEEN_GEOM_H +#define SEEN_GEOM_H + /** * \file geom.h * \brief Various geometrical calculations @@ -28,3 +31,5 @@ enum IntersectorKind { IntersectorKind intersector_line_intersection(NR::Point const &n0, double const d0, NR::Point const &n1, double const d1, NR::Point &result); + +#endif /* !SEEN_GEOM_H */ diff --git a/src/guide-snapper.cpp b/src/guide-snapper.cpp index decfaf3cf..51b5e7680 100644 --- a/src/guide-snapper.cpp +++ b/src/guide-snapper.cpp @@ -52,6 +52,12 @@ bool Inkscape::GuideSnapper::ThisSnapperMightSnap() const return _named_view == NULL ? false : (_enabled && _snap_from != 0 && _named_view->showguides); } +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 +{ + SnappedInfiniteLine dummy = SnappedInfiniteLine(snapped_point, snapped_distance, normal_to_line, point_on_line); + sc.guide_lines.push_back(dummy); +} + /* Local Variables: mode:c++ diff --git a/src/guide-snapper.h b/src/guide-snapper.h index 15f484711..0605bdb97 100644 --- a/src/guide-snapper.h +++ b/src/guide-snapper.h @@ -28,12 +28,12 @@ namespace Inkscape class GuideSnapper : public LineSnapper { public: - GuideSnapper(SPNamedView const *nv, NR::Coord const d); - + GuideSnapper(SPNamedView const *nv, NR::Coord const d); bool ThisSnapperMightSnap() const; private: - LineList _getSnapLines(NR::Point const &p) const; + LineList _getSnapLines(NR::Point const &p) const; + void _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; }; } diff --git a/src/line-snapper.cpp b/src/line-snapper.cpp index cf46671c8..3b91fb015 100644 --- a/src/line-snapper.cpp +++ b/src/line-snapper.cpp @@ -2,74 +2,65 @@ #include "libnr/nr-point-fns.h" #include "geom.h" #include "line-snapper.h" +#include "snapped-line.cpp" Inkscape::LineSnapper::LineSnapper(SPNamedView const *nv, NR::Coord const d) : Snapper(nv, d) { } -Inkscape::SnappedPoint Inkscape::LineSnapper::_doFreeSnap(Inkscape::Snapper::PointType const &t, +void Inkscape::LineSnapper::_doFreeSnap(SnappedConstraints &sc, + Inkscape::Snapper::PointType const &t, NR::Point const &p, bool const &f, std::vector &points_to_snap, std::list const &it) const { - /* Snap along x (ie to vertical lines) */ - Inkscape::SnappedPoint const v = _doConstrainedSnap(t, p, f, points_to_snap, component_vectors[NR::X], it); - /* Snap along y (ie to horizontal lines) */ - Inkscape::SnappedPoint const h = _doConstrainedSnap(t, p, f, points_to_snap, component_vectors[NR::Y], it); + /* Snap along x (i.e. to vertical lines) */ + _doConstrainedSnap(sc, t, p, f, points_to_snap, component_vectors[NR::X], it); + /* Snap along y (i.e. to horizontal lines) */ + _doConstrainedSnap(sc, t, p, f, points_to_snap, component_vectors[NR::Y], it); - /* If we snapped to both, combine the two results. This is so that, for example, - ** we snap nicely to the intersection of two guidelines. - */ - if (v.getDistance() < NR_HUGE && h.getDistance() < NR_HUGE) { - return SnappedPoint(NR::Point(v.getPoint()[NR::X], h.getPoint()[NR::Y]), hypot(v.getDistance(), h.getDistance())); - } - - /* If we snapped to a vertical line, return that */ - if (v.getDistance() < NR_HUGE) { - return v; - } - - /* Otherwise just return any horizontal snap; if we didn't snap to that either - ** we haven't snapped to anything. - */ - return h; } -Inkscape::SnappedPoint Inkscape::LineSnapper::_doConstrainedSnap(Inkscape::Snapper::PointType const &t, +void Inkscape::LineSnapper::_doConstrainedSnap(SnappedConstraints &sc, + Inkscape::Snapper::PointType const &t, NR::Point const &p, bool const &f, std::vector &points_to_snap, ConstraintLine const &c, std::list 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()))); - /* Constant term of the line we're trying to snap along */ - NR::Coord const q = dot(n, c.hasPoint() ? c.getPoint() : p); + 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 q = dot(n, point_on_line); /* Try to intersect this line with the target line */ - NR::Point t = p; + NR::Point t = NR::Point(NR_HUGE, NR_HUGE); IntersectorKind const k = intersector_line_intersection(n, q, component_vectors[i->first], i->second, t); - + if (k == INTERSECTS) { const NR::Coord dist = L2(t - p); - if (dist < getDistance() && dist < s.getDistance() ) { - s = SnappedPoint(t, dist); + //Store any line that's within snapping range + if (dist < getDistance()) { + _addSnappedLine(sc, t, dist, c.getDirection(), t); + //SnappedInfiniteLine dummy = SnappedInfiniteLine(t, dist, c.getDirection(), t); + //sc.infinite_lines.push_back(dummy); } } } - - return s; } /* diff --git a/src/line-snapper.h b/src/line-snapper.h index 5d93c858e..a15e6ae2d 100644 --- a/src/line-snapper.h +++ b/src/line-snapper.h @@ -25,13 +25,15 @@ protected: typedef std::list > LineList; private: - SnappedPoint _doFreeSnap(Inkscape::Snapper::PointType const &t, + void _doFreeSnap(SnappedConstraints &sc, + Inkscape::Snapper::PointType const &t, NR::Point const &p, bool const &first_point, std::vector &points_to_snap, std::list const &it) const; - SnappedPoint _doConstrainedSnap(Inkscape::Snapper::PointType const &t, + void _doConstrainedSnap(SnappedConstraints &sc, + Inkscape::Snapper::PointType const &t, NR::Point const &p, bool const &first_point, std::vector &points_to_snap, @@ -43,6 +45,8 @@ private: * \return List of lines that we should try snapping to. */ virtual LineList _getSnapLines(NR::Point const &p) const = 0; + + virtual void _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 = 0; }; } diff --git a/src/object-snapper.cpp b/src/object-snapper.cpp index 53f04f8c4..10f75e5c4 100644 --- a/src/object-snapper.cpp +++ b/src/object-snapper.cpp @@ -113,16 +113,13 @@ void Inkscape::ObjectSnapper::_findCandidates(SPObject* r, } -void Inkscape::ObjectSnapper::_snapNodes(Inkscape::Snapper::PointType const &t, +bool Inkscape::ObjectSnapper::_snapNodes(Inkscape::Snapper::PointType const &t, Inkscape::SnappedPoint &s, NR::Point const &p, bool const &first_point, DimensionToSnap const snap_dim) const { - /* FIXME: this seems like a hack. Perhaps Snappers should be - ** in SPDesktop rather than SPNamedView? - */ - SPDesktop const *desktop = SP_ACTIVE_DESKTOP; + bool success = false; // Determine the type of bounding box we should snap to SPItem::BBoxType bbox_type = SPItem::GEOMETRIC_BBOX; @@ -194,16 +191,20 @@ void Inkscape::ObjectSnapper::_snapNodes(Inkscape::Snapper::PointType const &t, if (dist < getDistance() && dist < s.getDistance()) { s = SnappedPoint(snapped_point, dist); + success = true; } } + + return success; } -void Inkscape::ObjectSnapper::_snapPaths(Inkscape::Snapper::PointType const &t, +bool Inkscape::ObjectSnapper::_snapPaths(Inkscape::Snapper::PointType const &t, Inkscape::SnappedPoint &s, NR::Point const &p, bool const &first_point) const { + bool success = false; /* FIXME: this seems like a hack. Perhaps Snappers should be ** in SPDesktop rather than SPNamedView? */ @@ -314,21 +315,25 @@ void Inkscape::ObjectSnapper::_snapPaths(Inkscape::Snapper::PointType const &t, NR::Coord const dist = NR::L2(o_dt - p); if (dist < getDistance() && dist < s.getDistance()) { s = SnappedPoint(o_dt, dist); + success = true; } } } } + + return success; } -Inkscape::SnappedPoint Inkscape::ObjectSnapper::_doFreeSnap(Inkscape::Snapper::PointType const &t, - NR::Point const &p, - bool const &first_point, - std::vector &points_to_snap, - std::list const &it) const +void Inkscape::ObjectSnapper::_doFreeSnap(SnappedConstraints &sc, + Inkscape::Snapper::PointType const &t, + NR::Point const &p, + bool const &first_point, + std::vector &points_to_snap, + std::list const &it) const { if ( NULL == _named_view ) { - return SnappedPoint(p, NR_HUGE); + return; } /* Get a list of all the SPItems that we will try to snap to */ @@ -337,30 +342,35 @@ Inkscape::SnappedPoint Inkscape::ObjectSnapper::_doFreeSnap(Inkscape::Snapper::P } SnappedPoint s(p, NR_HUGE); + bool snapped_to_node = false; + bool snapped_to_path = false; if (_snap_to_itemnode || _snap_to_bboxnode) { - _snapNodes(t, s, p, first_point, SNAP_XY); + snapped_to_node = _snapNodes(t, s, p, first_point, SNAP_XY); } if (_snap_to_itempath || _snap_to_bboxpath) { - _snapPaths(t, s, p, first_point); + snapped_to_path = _snapPaths(t, s, p, first_point); } - return s; + if (snapped_to_node || snapped_to_path) { + sc.points.push_back(s); + } } -Inkscape::SnappedPoint Inkscape::ObjectSnapper::_doConstrainedSnap(Inkscape::Snapper::PointType const &t, - NR::Point const &p, - bool const &first_point, - std::vector &points_to_snap, - ConstraintLine const &c, - std::list const &it) const +void Inkscape::ObjectSnapper::_doConstrainedSnap(SnappedConstraints &sc, + Inkscape::Snapper::PointType const &t, + NR::Point const &p, + bool const &first_point, + std::vector &points_to_snap, + ConstraintLine const &c, + std::list const &it) const { /* FIXME: this needs implementing properly; I think we have to do the ** intersection of c with the objects. */ - return _doFreeSnap(t, p, first_point, points_to_snap, it); + _doFreeSnap(sc, t, p, first_point, points_to_snap, it); } diff --git a/src/object-snapper.h b/src/object-snapper.h index e473e3fe9..5b2e6f37d 100644 --- a/src/object-snapper.h +++ b/src/object-snapper.h @@ -17,7 +17,6 @@ #include "sp-path.h" #include "splivarot.h" - struct SPNamedView; struct SPItem; struct SPObject; @@ -88,13 +87,15 @@ private: std::vector *_candidates; std::vector *_points_to_snap_to; std::vector *_paths_to_snap_to; - SnappedPoint _doFreeSnap(Inkscape::Snapper::PointType const &t, + void _doFreeSnap(SnappedConstraints &sc, + Inkscape::Snapper::PointType const &t, NR::Point const &p, bool const &first_point, std::vector &points_to_snap, std::list const &it) const; - SnappedPoint _doConstrainedSnap(Inkscape::Snapper::PointType const &t, + void _doConstrainedSnap(SnappedConstraints &sc, + Inkscape::Snapper::PointType const &t, NR::Point const &p, bool const &first_point, std::vector &points_to_snap, @@ -107,13 +108,13 @@ private: std::vector &points_to_snap, DimensionToSnap const snap_dim) const; - void _snapNodes(Inkscape::Snapper::PointType const &t, + bool _snapNodes(Inkscape::Snapper::PointType const &t, Inkscape::SnappedPoint &s, NR::Point const &p, bool const &first_point, DimensionToSnap const snap_dim) const; - void _snapPaths(Inkscape::Snapper::PointType const &t, + bool _snapPaths(Inkscape::Snapper::PointType const &t, Inkscape::SnappedPoint &s, NR::Point const &p, bool const &first_point) const; diff --git a/src/snap.cpp b/src/snap.cpp index 1529dc40d..486507ce6 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -17,8 +17,11 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include + #include "sp-namedview.h" #include "snap.h" +#include "snapped-line.h" #include #include @@ -182,29 +185,6 @@ Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t, return freeSnap(t, p, true, points_to_snap, lit); } - -/** - * Try to snap a point to any interested snappers. - * - * \param t Type of point. - * \param p Point. - * \param first_point If true then this point is the first one from a whole bunch of points - * \param points_to_snap The whole bunch of points, all from the same selection and having the same transformation - * \param it List of items to ignore when snapping. - * \return Snapped point. - */ - - Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t, - NR::Point const &p, - bool const &first_point, - std::vector &points_to_snap, - std::list const &it) const -{ - SnapperList const snappers = getSnappers(); - - return freeSnap(t, p, first_point, points_to_snap, it, snappers); -} - /** * Try to snap a point to any of the specified snappers. * @@ -221,19 +201,18 @@ Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t, NR::Point const &p, bool const &first_point, std::vector &points_to_snap, - std::list const &it, - SnapperList const &snappers) const + std::list const &it) const { - Inkscape::SnappedPoint r(p, NR_HUGE); + + SnappedConstraints sc; + + SnapperList const snappers = getSnappers(); for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) { - Inkscape::SnappedPoint const s = (*i)->freeSnap(t, p, first_point, points_to_snap, it); - if (s.getDistance() < r.getDistance()) { - r = s; - } + (*i)->freeSnap(sc, t, p, first_point, points_to_snap, it); } - return r; + return findBestSnap(p, sc); } /** @@ -273,22 +252,19 @@ SnapManager::freeSnapAlways( Inkscape::Snapper::PointType t, std::list const &it, SnapperList &snappers ) { - Inkscape::SnappedPoint r(p, NR_HUGE); + + SnappedConstraints sc; for (SnapperList::iterator i = snappers.begin(); i != snappers.end(); i++) { gdouble const curr_gridsnap = (*i)->getDistance(); const_cast (*i)->setDistance(NR_HUGE); std::vector points_to_snap; points_to_snap.push_back(p); - Inkscape::SnappedPoint const s = (*i)->freeSnap(t, p, true, points_to_snap, it); + (*i)->freeSnap(sc, t, p, true, points_to_snap, it); const_cast (*i)->setDistance(curr_gridsnap); - - if (s.getDistance() < r.getDistance()) { - r = s; - } } - return r; + return findBestSnap(p, sc); } @@ -340,17 +316,15 @@ Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType Inkscape::Snapper::ConstraintLine const &c, std::list const &it) const { - Inkscape::SnappedPoint r(p, NR_HUGE); - + + SnappedConstraints sc; + SnapperList const snappers = getSnappers(); for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) { - Inkscape::SnappedPoint const s = (*i)->constrainedSnap(t, p, first_point, points_to_snap, c, it); - if (s.getDistance() < r.getDistance()) { - r = s; - } + (*i)->constrainedSnap(sc, t, p, first_point, points_to_snap, c, it); } - return r; + return findBestSnap(p, sc); } Inkscape::SnappedPoint SnapManager::guideSnap(NR::Point const &p, @@ -456,7 +430,7 @@ std::pair SnapManager::_snapTransformed( std::vector::const_iterator j = transformed_points.begin(); - for (std::vector::const_iterator i = points.begin(); i != points.end(); i++) { + for (std::vector::const_iterator i = points.begin(); i != points.end(); i++) { /* Snap it */ Inkscape::SnappedPoint const snapped = constrained ? @@ -471,7 +445,16 @@ std::pair SnapManager::_snapTransformed( switch (transformation_type) { case TRANSLATION: result = snapped.getPoint() - *i; - metric = NR::L2(result); + /* Consider the case in which 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 single grid + * line. If we prefer snapping to an intersection instead of to a single + * grid line, then we cannot use "metric = NR::L2(result)". Therefore the + * snapped distance will be used as a metric. Please note that the snapped + * distance is defined as the distance to the nearest line of the intersection, + * and not to the intersection itself! + */ + metric = snapped.getDistance(); //used to be: metric = NR::L2(result); break; case SCALE: { @@ -483,11 +466,11 @@ std::pair SnapManager::_snapTransformed( } case STRETCH: { - for (int j = 0; j < 2; j++) { - if (uniform || j == dim) { - result[j] = (snapped.getPoint()[dim] - origin[dim]) / ((*i)[dim] - origin[dim]); + for (int a = 0; a < 2; a++) { + if (uniform || a == dim) { + result[a] = (snapped.getPoint()[dim] - origin[dim]) / ((*i)[dim] - origin[dim]); } else { - result[j] = 1; + result[a] = 1; } } metric = std::abs(result[dim] - transformation[dim]); @@ -502,7 +485,7 @@ std::pair SnapManager::_snapTransformed( } /* Note it if it's the best so far */ - if (metric < best_metric) { + if ((metric < best_metric) || ((metric == best_metric) && snapped.getAtIntersection() == true)) { best_transformation = result; best_metric = metric; } @@ -510,7 +493,7 @@ std::pair SnapManager::_snapTransformed( j++; } - + // 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); @@ -671,6 +654,81 @@ std::pair SnapManager::freeSnapSkew(Inkscape::Snapper::PointTyp return std::make_pair(r.first[d], r.second); } +Inkscape::SnappedPoint SnapManager::findBestSnap(NR::Point const &p, SnappedConstraints &sc) const +{ + NR::Coord const guide_sens = guide.getDistance(); + NR::Coord grid_sens = 0; + + SnapManager::SnapperList const gs = getGridSnappers(); + SnapperList::const_iterator i = gs.begin(); + if (i != gs.end()) { + grid_sens = (*i)->getDistance(); + } + + // Store all snappoints, optionally together with their specific snapping range + std::list > 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) + + // search for the closest snapped point + Inkscape::SnappedPoint closestPoint; + if (getClosestSP(sc.points, closestPoint)) { + sp_list.push_back(std::make_pair(closestPoint, NR_HUGE)); + } + + // search for the closest snapped grid line + Inkscape::SnappedInfiniteLine closestGridLine; + if (getClosestSIL(sc.grid_lines, closestGridLine)) { + sp_list.push_back(std::make_pair(Inkscape::SnappedPoint(closestGridLine), NR_HUGE)); + } + + // search for the closest snapped guide line + Inkscape::SnappedInfiniteLine closestGuideLine; + if (getClosestSIL(sc.guide_lines, closestGuideLine)) { + sp_list.push_back(std::make_pair(Inkscape::SnappedPoint(closestGuideLine), NR_HUGE)); + } + + // search for the closest snapped intersection of grid lines + Inkscape::SnappedPoint closestGridPoint; + if (getClosestIntersectionSIL(sc.grid_lines, closestGridPoint)) { + sp_list.push_back(std::make_pair(closestGridPoint, NR_HUGE)); + } + + // search for the closest snapped intersection of guide lines + Inkscape::SnappedPoint closestGuidePoint; + if (getClosestIntersectionSIL(sc.guide_lines, closestGuidePoint)) { + sp_list.push_back(std::make_pair(closestGuidePoint, NR_HUGE)); + } + + // search for the closest snapped intersection of grid with guide lines + Inkscape::SnappedPoint closestGridGuidePoint; + if (getClosestIntersectionSIL(sc.grid_lines, sc.guide_lines, closestGridGuidePoint)) { + sp_list.push_back(std::make_pair(closestGridGuidePoint, std::min(guide_sens, grid_sens))); + } + + // now let's see which snapped point gets a thumbs up + Inkscape::SnappedPoint bestPoint(p, NR_HUGE); + for (std::list >::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 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 but at an intersection + bool c3 = ((*i).first.getDistance() == bestPoint.getDistance()) && (*i).first.getAtIntersection(); + // then prefer this point over the previous one + if (c1 || c2 || c3) { + bestPoint = (*i).first; + } + } + } + return bestPoint; +} /* Local Variables: mode:c++ diff --git a/src/snap.h b/src/snap.h index f44df8923..29d22a1cd 100644 --- a/src/snap.h +++ b/src/snap.h @@ -22,6 +22,7 @@ #include #include #include + #include "guide-snapper.h" #include "object-snapper.h" @@ -48,18 +49,11 @@ public: NR::Point const &p, SPItem const *it) const; - Inkscape::SnappedPoint freeSnap(Inkscape::Snapper::PointType t, - NR::Point const &p, - bool const &first_point, - std::vector &points_to_snap, - std::list const &it) const; - - Inkscape::SnappedPoint freeSnap( Inkscape::Snapper::PointType t, + Inkscape::SnappedPoint freeSnap( Inkscape::Snapper::PointType t, NR::Point const &p, bool const &first_point, std::vector &points_to_snap, - std::list const &it, - SnapperList const &snappers ) const; + std::list const &it) const; Inkscape::SnappedPoint freeSnapAlways( Inkscape::Snapper::PointType t, NR::Point const &p, @@ -172,6 +166,8 @@ private: NR::Point const &origin, NR::Dim2 dim, bool uniform) const; + + Inkscape::SnappedPoint findBestSnap(NR::Point const &p, SnappedConstraints &sc) const; }; #endif /* !SEEN_SNAP_H */ diff --git a/src/snapped-line.cpp b/src/snapped-line.cpp new file mode 100644 index 000000000..5e31fc1dd --- /dev/null +++ b/src/snapped-line.cpp @@ -0,0 +1,180 @@ +/** + * \file src/snapped-line.cpp + * \brief SnappedInfiniteLine class. + * + * Authors: + * Diederik van Lierop + * + * Released under GNU GPL, read the file 'COPYING' for more information. + */ + +#include "snapped-line.h" +#include "geom.h" + +Inkscape::SnappedLine::SnappedLine(NR::Point snapped_point, NR::Coord snapped_distance, 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; + _at_intersection = false; +} + +Inkscape::SnappedLine::SnappedLine() +{ + _start_point_of_line = NR::Point(0,0); + _end_point_of_line = NR::Point(0,0); + _distance = NR_HUGE; + _point = NR::Point(0,0); + _at_intersection = false; +} + + +Inkscape::SnappedLine::~SnappedLine() +{ +} + +Inkscape::SnappedPoint Inkscape::SnappedLine::intersect(SnappedLine const &line) const +{ + //TODO: Diederik, implement the intersection + NR::Point const intersection = NR::Point(NR_HUGE, NR_HUGE); + + //if (result == 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 + */ + NR::Coord distance = std::min(_distance, line.getDistance()); + //} + return SnappedPoint(intersection, distance); +}; + + + +Inkscape::SnappedInfiniteLine::SnappedInfiniteLine(NR::Point snapped_point, NR::Coord snapped_distance, 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; + _point = snapped_point; + _at_intersection = false; +} + +Inkscape::SnappedInfiniteLine::SnappedInfiniteLine() +{ + _normal_to_line = NR::Point(0,0); + _point_on_line = NR::Point(0,0); + _distance = NR_HUGE; + _point = NR::Point(0,0); + _at_intersection = false; +} + +Inkscape::SnappedInfiniteLine::~SnappedInfiniteLine() +{ +} + +Inkscape::SnappedPoint Inkscape::SnappedInfiniteLine::intersect(SnappedInfiniteLine const &line) const +{ + // Calculate the intersection of to infinite lines, which are both within snapping range + // The point of intersection should be considered for snapping, but might be outside the snapping range + + NR::Point intersection = NR::Point(NR_HUGE, NR_HUGE); + NR::Coord distance = NR_HUGE; + + IntersectorKind result = intersector_line_intersection(getNormal(), getConstTerm(), + line.getNormal(), line.getConstTerm(), intersection); + + /*std::cout << "n0 = " << getNormal() << std::endl; + std::cout << "n1 = " << line.getNormal() << std::endl; + std::cout << "c0 = " << getConstTerm() << std::endl; + std::cout << "c1 = " << line.getConstTerm() << std::endl;*/ + + if (result == 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()); + //std::cout << "Intersected nicely, now getSIL distance = " << distance << std::endl; + } + + //std::cout << "getSIL distance = " << distance << std::endl; + + return SnappedPoint(intersection, distance, result == INTERSECTS); +} + +// search for the closest snapped infinite line +bool getClosestSIL(std::list &list, Inkscape::SnappedInfiniteLine &result) +{ + bool success = false; + + for (std::list::const_iterator i = list.begin(); i != list.end(); i++) { + if ((i == list.begin()) || (*i).getDistance() < result.getDistance()) { + result = *i; + success = true; + } + } + + return success; +} + +// search for the closest intersection of two snapped infinite lines, which are both member of the same collection +bool getClosestIntersectionSIL(std::list &list, Inkscape::SnappedPoint &result) +{ + bool success = false; + + for (std::list::const_iterator i = list.begin(); i != list.end(); i++) { + std::list::const_iterator j = i; + j++; + for (; j != list.end(); j++) { + Inkscape::SnappedPoint sp = (*i).intersect(*j); + if (sp.getAtIntersection()) { + if (!success || sp.getDistance() < result.getDistance()) { + // !success because the first intersection cannot be compared to a previous one + result = sp; + success = true; + } + } + } + } + + return success; +} + +// search for the closest intersection of two snapped infinite lines, which are in two different collections +bool getClosestIntersectionSIL(std::list &list1, std::list &list2, Inkscape::SnappedPoint &result) +{ + bool success = false; + + for (std::list::const_iterator i = list1.begin(); i != list1.end(); i++) { + for (std::list::const_iterator j = list2.begin(); j != list2.end(); j++) { + Inkscape::SnappedPoint sp = (*i).intersect(*j); + if (sp.getAtIntersection()) { + if (!success || sp.getDistance() < result.getDistance()) { + // !success because the first intersection cannot be compared to a previous one + result = sp; + success = true; + } + } + } + } + + return success; +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/snapped-line.h b/src/snapped-line.h new file mode 100644 index 000000000..0b8c9053f --- /dev/null +++ b/src/snapped-line.h @@ -0,0 +1,76 @@ +#ifndef SEEN_SNAPPEDLINE_H +#define SEEN_SNAPPEDLINE_H + +/** + * \file src/snapped-line.h + * \brief SnappedInfiniteLine class. + * + * Authors: + * Diederik van Lierop + * + * Released under GNU GPL, read the file 'COPYING' for more information. + */ + +#include +#include +#include "libnr/nr-coord.h" +#include "libnr/nr-point.h" +#include +#include "snapped-point.h" + +namespace Inkscape +{ + +/// Class describing the result of an attempt to snap to a line segment. +class SnappedLine : public SnappedPoint +{ +public: + SnappedLine(); + SnappedLine(NR::Point snapped_point, NR::Coord snapped_distance, NR::Point start_point_of_line, NR::Point end_point_of_line); + ~SnappedLine(); + Inkscape::SnappedPoint intersect(SnappedLine const &line) const; //intersect with another SnappedLine + +private: + NR::Point _start_point_of_line; + NR::Point _end_point_of_line; +}; + + +/// Class describing the result of an attempt to snap to an infinite line. +class SnappedInfiniteLine : public SnappedPoint +{ +public: + SnappedInfiniteLine(); + SnappedInfiniteLine(NR::Point snapped_point, NR::Coord snapped_distance, NR::Point normal_to_line, NR::Point point_on_line); + ~SnappedInfiniteLine(); + Inkscape::SnappedPoint intersect(SnappedInfiniteLine const &line) const; //intersect with another SnappedInfiniteLine + // 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::Coord getConstTerm() const {return dot(_normal_to_line, _point_on_line);} // c = n.p = nx*px + ny*py; + +private: + NR::Point _normal_to_line; + NR::Point _point_on_line; +}; + +} + +bool getClosestSIL(std::list &list, Inkscape::SnappedInfiniteLine &result); +bool getClosestIntersectionSIL(std::list &list, Inkscape::SnappedPoint &result); +bool getClosestIntersectionSIL(std::list &list1, std::list &list2, Inkscape::SnappedPoint &result); + + +#endif /* !SEEN_SNAPPEDLINE_H */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/snapped-point.cpp b/src/snapped-point.cpp index 5d0d242dd..9df76b704 100644 --- a/src/snapped-point.cpp +++ b/src/snapped-point.cpp @@ -9,28 +9,24 @@ */ #include "snapped-point.h" +#include -Inkscape::SnappedPoint::SnappedPoint(NR::Point p, NR::Coord d) - : _distance(d), _point(p) +Inkscape::SnappedPoint::SnappedPoint(NR::Point p, NR::Coord d, bool at_intersection) + : _distance(d), _point(p), _at_intersection(at_intersection) { - } -Inkscape::SnappedPoint::~SnappedPoint() +Inkscape::SnappedPoint::SnappedPoint() { - /// TODO : empty the _hightlight_groups vector and destroy the - /// HighlightGroup items it holds + _distance = NR_HUGE; + _point = NR::Point(0,0); + _at_intersection = false; } -void Inkscape::SnappedPoint::addHighlightGroup(HighlightGroup *group) -{ - /// TODO -} -void Inkscape::SnappedPoint::addHighlightGroups(std::vector *groups) +Inkscape::SnappedPoint::~SnappedPoint() { - /// TODO } NR::Coord Inkscape::SnappedPoint::getDistance() const @@ -43,12 +39,21 @@ NR::Point Inkscape::SnappedPoint::getPoint() const return _point; } -std::vector Inkscape::SnappedPoint::getHighlightGroups() const +// search for the closest snapped point +bool getClosestSP(std::list &list, Inkscape::SnappedPoint &result) { - return _hightlight_groups; + bool success = false; + + for (std::list::const_iterator i = list.begin(); i != list.end(); i++) { + if ((i == list.begin()) || (*i).getDistance() < result.getDistance()) { + result = *i; + success = true; + } + } + + return success; } - /* Local Variables: mode:c++ diff --git a/src/snapped-point.h b/src/snapped-point.h index 0669ddd21..147922b2f 100644 --- a/src/snapped-point.h +++ b/src/snapped-point.h @@ -12,36 +12,36 @@ */ #include +#include #include "libnr/nr-coord.h" #include "libnr/nr-point.h" namespace Inkscape { - -class HighlightGroup; - + /// Class describing the result of an attempt to snap. class SnappedPoint { public: - SnappedPoint() {} - SnappedPoint(::NR::Point p, ::NR::Coord d); + SnappedPoint(); + SnappedPoint(::NR::Point p, ::NR::Coord d, bool at_intersection = false); ~SnappedPoint(); - void addHighlightGroup(HighlightGroup *group); - void addHighlightGroups(std::vector *groups); - - ::NR::Coord getDistance() const; + NR::Coord getDistance() const; NR::Point getPoint() const; - std::vector getHighlightGroups() const; - -private: - ::NR::Coord _distance; - ::NR::Point _point; - std::vector _hightlight_groups; -}; + bool getAtIntersection() const {return _at_intersection;} + +protected: + NR::Coord _distance; + NR::Point _point; + bool _at_intersection; +}; } + +bool getClosestSP(std::list &list, Inkscape::SnappedPoint &result); + + #endif /* !SEEN_SNAPPEDPOINT_H */ /* diff --git a/src/snapper.cpp b/src/snapper.cpp index 17d7b7137..5edde2405 100644 --- a/src/snapper.cpp +++ b/src/snapper.cpp @@ -91,7 +91,9 @@ void Inkscape::Snapper::setEnabled(bool s) * \return Snapped point. */ -Inkscape::SnappedPoint Inkscape::Snapper::freeSnap(PointType const &t, +void Inkscape::Snapper::freeSnap(SnappedConstraints &sc, + + PointType const &t, NR::Point const &p, bool const &first_point, std::vector &points_to_snap, @@ -99,7 +101,7 @@ Inkscape::SnappedPoint Inkscape::Snapper::freeSnap(PointType const &t, { std::list lit; lit.push_back(it); - return freeSnap(t, p, first_point, points_to_snap, lit); + freeSnap(sc, t, p, first_point, points_to_snap, lit); } @@ -114,17 +116,19 @@ Inkscape::SnappedPoint Inkscape::Snapper::freeSnap(PointType const &t, * \return Snapped point. */ -Inkscape::SnappedPoint Inkscape::Snapper::freeSnap(PointType const &t, +void Inkscape::Snapper::freeSnap(SnappedConstraints &sc, + + PointType const &t, NR::Point const &p, bool const &first_point, std::vector &points_to_snap, std::list const &it) const { if (_enabled == false || getSnapFrom(t) == false) { - return SnappedPoint(p, NR_HUGE); + return; } - return _doFreeSnap(t, p, first_point, points_to_snap, it); + _doFreeSnap(sc, t, p, first_point, points_to_snap, it); } @@ -141,7 +145,9 @@ Inkscape::SnappedPoint Inkscape::Snapper::freeSnap(PointType const &t, * \return Snapped point. */ -Inkscape::SnappedPoint Inkscape::Snapper::constrainedSnap(PointType const &t, +void Inkscape::Snapper::constrainedSnap(SnappedConstraints &sc, + + PointType const &t, NR::Point const &p, bool const &first_point, std::vector &points_to_snap, @@ -150,7 +156,7 @@ Inkscape::SnappedPoint Inkscape::Snapper::constrainedSnap(PointType const &t, { std::list lit; lit.push_back(it); - return constrainedSnap(t, p, first_point, points_to_snap, c, lit); + constrainedSnap(sc, t, p, first_point, points_to_snap, c, lit); } @@ -165,7 +171,9 @@ Inkscape::SnappedPoint Inkscape::Snapper::constrainedSnap(PointType const &t, * \return Snapped point. */ -Inkscape::SnappedPoint Inkscape::Snapper::constrainedSnap(PointType const &t, +void Inkscape::Snapper::constrainedSnap(SnappedConstraints &sc, + + PointType const &t, NR::Point const &p, bool const &first_point, std::vector &points_to_snap, @@ -173,10 +181,10 @@ Inkscape::SnappedPoint Inkscape::Snapper::constrainedSnap(PointType const &t, std::list const &it) const { if (_enabled == false || getSnapFrom(t) == false) { - return SnappedPoint(p, NR_HUGE); + return; } - return _doConstrainedSnap(t, p, first_point, points_to_snap, c, it); + _doConstrainedSnap(sc, t, p, first_point, points_to_snap, c, it); } /* diff --git a/src/snapper.h b/src/snapper.h index 5b6b4a258..a122db72f 100644 --- a/src/snapper.h +++ b/src/snapper.h @@ -15,7 +15,16 @@ #include #include "libnr/nr-coord.h" #include "libnr/nr-point.h" + #include "snapped-point.h" +#include "snapped-line.h" + +struct SnappedConstraints { + std::list points; + std::list lines; + std::list grid_lines; + std::list guide_lines; +}; struct SPNamedView; struct SPItem; @@ -50,13 +59,15 @@ public: void setEnabled(bool s); - SnappedPoint freeSnap(PointType const &t, + void freeSnap(SnappedConstraints &sc, + PointType const &t, NR::Point const &p, bool const &first_point, std::vector &points_to_snap, SPItem const *it) const; - SnappedPoint freeSnap(PointType const &t, + void freeSnap(SnappedConstraints &sc, + PointType const &t, NR::Point const &p, bool const &first_point, std::vector &points_to_snap, @@ -87,19 +98,22 @@ public: NR::Point _direction; }; - SnappedPoint constrainedSnap(PointType const &t, + void constrainedSnap(SnappedConstraints &sc, + PointType const &t, NR::Point const &p, bool const &first_point, std::vector &points_to_snap, ConstraintLine const &c, SPItem const *it) const; - SnappedPoint constrainedSnap(PointType const &t, + void constrainedSnap(SnappedConstraints &sc, + PointType const &t, NR::Point const &p, bool const &first_point, std::vector &points_to_snap, ConstraintLine const &c, std::list const &it) const; + protected: SPNamedView const *_named_view; int _snap_from; ///< bitmap of point types that we will snap from @@ -116,7 +130,8 @@ private: * \param it Items that should not be snapped to. * \return Snapped point. */ - virtual SnappedPoint _doFreeSnap(PointType const &t, + virtual void _doFreeSnap(SnappedConstraints &sc, + PointType const &t, NR::Point const &p, bool const &first_point, std::vector &points_to_snap, @@ -132,14 +147,15 @@ private: * \param it Items that should not be snapped to. * \return Snapped point. */ - virtual SnappedPoint _doConstrainedSnap(PointType const &t, + virtual void _doConstrainedSnap(SnappedConstraints &sc, + PointType const &t, NR::Point const &p, bool const &first_point, std::vector &points_to_snap, ConstraintLine const &c, std::list const &it) const = 0; - ::NR::Coord _distance; ///< snap distance (desktop coordinates) + NR::Coord _distance; ///< snap distance (desktop coordinates) }; } -- 2.30.2