From 84504bd815a6ef3c5b7b5958ce46429cbab56a66 Mon Sep 17 00:00:00 2001 From: Diederik van Lierop Date: Sat, 3 Nov 2007 14:48:50 +0000 Subject: 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. (bzr r4016) --- src/snap.cpp | 164 ++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 111 insertions(+), 53 deletions(-) (limited to 'src/snap.cpp') 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++ -- cgit v1.2.3