From 1813949bb185a61d7a363ec4fd3d0374afca9afd Mon Sep 17 00:00:00 2001 From: dvlierop2 Date: Thu, 27 Dec 2007 19:45:52 +0000 Subject: [PATCH] Snapping a guide to nodes (while dragging it across the canvas) now also works for angled guides --- src/libnr/nr-point-fns.cpp | 2 + src/object-snapper.cpp | 94 ++++++++++++++++++++++++++------------ src/object-snapper.h | 19 ++++++-- src/snap.cpp | 23 ++-------- 4 files changed, 86 insertions(+), 52 deletions(-) diff --git a/src/libnr/nr-point-fns.cpp b/src/libnr/nr-point-fns.cpp index 4428ed9b3..a7b128bdb 100644 --- a/src/libnr/nr-point-fns.cpp +++ b/src/libnr/nr-point-fns.cpp @@ -111,6 +111,8 @@ project_on_linesegment(NR::Point const p, NR::Point const p1, NR::Point const p2 // calculate u according to "Minimum Distance between a Point and a Line" // see http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/ + // Warning: projected points will not necessarily be in between the endpoints of the linesegments! + if (p1 == p2) { // to avoid div. by zero below return p; } diff --git a/src/object-snapper.cpp b/src/object-snapper.cpp index 71c790c17..c03884ffd 100644 --- a/src/object-snapper.cpp +++ b/src/object-snapper.cpp @@ -101,7 +101,14 @@ void Inkscape::ObjectSnapper::_findCandidates(SPObject* r, double d = getDistance(); bool withinX = ((*i)[NR::X] >= b_min[NR::X] - d) && ((*i)[NR::X] <= b_max[NR::X] + d); bool withinY = ((*i)[NR::Y] >= b_min[NR::Y] - d) && ((*i)[NR::Y] <= b_max[NR::Y] + d); - if (snap_dim == SNAP_X && withinX || snap_dim == SNAP_Y && withinY || snap_dim == SNAP_XY && withinX && withinY) { + bool c1 = snap_dim == GUIDE_TRANSL_SNAP_X && withinX; + bool c2 = snap_dim == GUIDE_TRANSL_SNAP_Y && withinY; + bool c3 = snap_dim == TRANSL_SNAP_XY && withinX && withinY; + // For an angled guide at e.g 45 deg., the bbox is very large and might even span + // the full desktop. Therefore we will simply return _any_ candidate, without looking at + // the bbox and the snapping distance. + bool c4 = snap_dim == ANGLED_GUIDE_TRANSL_SNAP || snap_dim == ANGLED_GUIDE_ROT_SNAP; + if ( c1 || c2 || c3 || c4) { //We've found a point that is within snapping range //of this object, so record it as a candidate _candidates->push_back(SP_ITEM(o)); @@ -173,36 +180,19 @@ void Inkscape::ObjectSnapper::_collectNodes(Inkscape::Snapper::PointType const & void Inkscape::ObjectSnapper::_snapNodes(SnappedConstraints &sc, Inkscape::Snapper::PointType const &t, NR::Point const &p, - bool const &first_point, - DimensionToSnap const snap_dim) const + bool const &first_point) const { + // Iterate through all nodes, find out which one is the closest to p, and snap to it! + _collectNodes(t, first_point); - //Do the snapping, using all the nodes and corners collected before - NR::Point snapped_point; SnappedPoint s; bool success = false; for (std::vector::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); k++) { - /* Try to snap to this node of the path */ - NR::Coord dist = NR_HUGE; - switch (snap_dim) { - case SNAP_X: - dist = fabs((*k)[NR::X] - p[NR::X]); - snapped_point = NR::Point((*k)[NR::X], p[NR::Y]); - break; - case SNAP_Y: - dist = fabs((*k)[NR::Y] - p[NR::Y]); - snapped_point = NR::Point(p[NR::X], (*k)[NR::Y]); - break; - case SNAP_XY: - dist = NR::L2(*k - p); - snapped_point = *k; - break; - } - + NR::Coord dist = NR::L2(*k - p); if (dist < getDistance() && dist < s.getDistance()) { - s = SnappedPoint(snapped_point, dist); + s = SnappedPoint(*k, dist); success = true; } } @@ -212,6 +202,31 @@ void Inkscape::ObjectSnapper::_snapNodes(SnappedConstraints &sc, } } +void Inkscape::ObjectSnapper::_snapTranslatingGuideToNodes(SnappedConstraints &sc, + Inkscape::Snapper::PointType const &t, + NR::Point const &p, + NR::Point const &guide_normal) const +{ + // Iterate through all nodes, find out which one is the closest to this guide, and snap to it! + _collectNodes(t, true); + + SnappedPoint s; + bool success = false; + + for (std::vector::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); k++) { + // Project each node (*k) on the guide line (running through point p) + NR::Point p_proj = project_on_linesegment(*k, p, p + NR::rot90(guide_normal)); + NR::Coord dist = NR::L2(*k - p_proj); + if (dist < getDistance() && dist < s.getDistance()) { + s = SnappedPoint(*k, dist); + success = true; + } + } + + if (success) { + sc.points.push_back(s); + } +} void Inkscape::ObjectSnapper::_collectPaths(Inkscape::Snapper::PointType const &t, bool const &first_point) const @@ -436,11 +451,11 @@ void Inkscape::ObjectSnapper::_doFreeSnap(SnappedConstraints &sc, /* Get a list of all the SPItems that we will try to snap to */ if (first_point) { - _findCandidates(sp_document_root(_named_view->document), it, first_point, points_to_snap, SNAP_XY); + _findCandidates(sp_document_root(_named_view->document), it, first_point, points_to_snap, TRANSL_SNAP_XY); } if (_snap_to_itemnode || _snap_to_bboxnode) { - _snapNodes(sc, t, p, first_point, SNAP_XY); + _snapNodes(sc, t, p, first_point); } if (_snap_to_itempath || _snap_to_bboxpath) { _snapPaths(sc, t, p, first_point); @@ -463,7 +478,7 @@ void Inkscape::ObjectSnapper::_doConstrainedSnap( SnappedConstraints &sc, /* Get a list of all the SPItems that we will try to snap to */ if (first_point) { - _findCandidates(sp_document_root(_named_view->document), it, first_point, points_to_snap, SNAP_XY); + _findCandidates(sp_document_root(_named_view->document), it, first_point, points_to_snap, TRANSL_SNAP_XY); } // A constrained snap, is a snap in only one degree of freedom (specified by the constraint line). @@ -483,8 +498,8 @@ void Inkscape::ObjectSnapper::_doConstrainedSnap( SnappedConstraints &sc, void Inkscape::ObjectSnapper::guideSnap(SnappedConstraints &sc, - NR::Point const &p, - DimensionToSnap const snap_dim) const + NR::Point const &p, + NR::Point const &guide_normal) const { if ( NULL == _named_view ) { return; @@ -497,8 +512,29 @@ void Inkscape::ObjectSnapper::guideSnap(SnappedConstraints &sc, std::vector points_to_snap; points_to_snap.push_back(p); + // This method is used to snap a guide to nodes, while dragging the guide around + DimensionToSnap snap_dim; + if (guide_normal == component_vectors[NR::Y]) { + snap_dim = GUIDE_TRANSL_SNAP_Y; + } else if (guide_normal == component_vectors[NR::X]) { + snap_dim = GUIDE_TRANSL_SNAP_X; + } else { + snap_dim = ANGLED_GUIDE_TRANSL_SNAP; + } + // We don't support ANGLED_GUIDE_ROT_SNAP yet. + + // It would be cool to allow the user to rotate a guide by dragging it, instead of + // only translating it. (For example when CTRL is pressed). We will need an UI part + // for that first; and some important usability choices need to be made: + // E.g. which point should be used for pivoting? A previously snapped point, + // or a transformation center (which can be moved after clicking for the + // second time on an object; but should this point then be constrained to the + // line, or can it be located anywhere?) + _findCandidates(sp_document_root(_named_view->document), it, true, points_to_snap, snap_dim); - _snapNodes(sc, Inkscape::Snapper::SNAPPOINT_GUIDE, p, true, snap_dim); + _snapTranslatingGuideToNodes(sc, Inkscape::Snapper::SNAPPOINT_GUIDE, p, guide_normal); + + // _snapRotatingGuideToNodes has not been implemented yet. } /** diff --git a/src/object-snapper.h b/src/object-snapper.h index c16b42580..a2d199c87 100644 --- a/src/object-snapper.h +++ b/src/object-snapper.h @@ -32,7 +32,12 @@ public: ObjectSnapper(SPNamedView const *nv, NR::Coord const d); ~ObjectSnapper(); - enum DimensionToSnap {SNAP_X, SNAP_Y, SNAP_XY}; + enum DimensionToSnap { + GUIDE_TRANSL_SNAP_X, // For snapping a vertical guide (normal in the X-direction) to objects, + GUIDE_TRANSL_SNAP_Y, // For snapping a horizontal guide (normal in the Y-direction) to objects + ANGLED_GUIDE_TRANSL_SNAP, // For snapping an angled guide, while translating it accross the desktop + ANGLED_GUIDE_ROT_SNAP, // For snapping an angled guide, while rotating it around some pivot point + TRANSL_SNAP_XY}; // All other cases; for snapping to objects, other than guides void setSnapToItemNode(bool s) { _snap_to_itemnode = s; @@ -80,7 +85,7 @@ public: void guideSnap(SnappedConstraints &sc, NR::Point const &p, - DimensionToSnap const snap_dim) const; + NR::Point const &guide_normal) const; bool ThisSnapperMightSnap() const; @@ -114,9 +119,13 @@ private: void _snapNodes(SnappedConstraints &sc, Inkscape::Snapper::PointType const &t, NR::Point const &p, - bool const &first_point, - DimensionToSnap const snap_dim) const; - + bool const &first_point) const; + + void _snapTranslatingGuideToNodes(SnappedConstraints &sc, + Inkscape::Snapper::PointType const &t, + NR::Point const &p, + NR::Point const &guide_normal) const; + void _collectNodes(Inkscape::Snapper::PointType const &t, bool const &first_point) const; diff --git a/src/snap.cpp b/src/snap.cpp index b4e284271..c6fa3fc25 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -285,29 +285,16 @@ Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType } Inkscape::SnappedPoint SnapManager::guideSnap(NR::Point const &p, - NR::Point const &guide_normal) const + NR::Point const &guide_normal) const { - if (!object.ThisSnapperMightSnap()) { - return Inkscape::SnappedPoint(p, NR_HUGE); - } - // This method is used to snap a guide to nodes, while dragging the guide around - Inkscape::ObjectSnapper::DimensionToSnap snap_dim; - if (guide_normal == component_vectors[NR::Y]) { - snap_dim = Inkscape::ObjectSnapper::SNAP_Y; - } else if (guide_normal == component_vectors[NR::X]) { - snap_dim = Inkscape::ObjectSnapper::SNAP_X; - } else { - g_warning("WARNING: snapping of angled guides is not supported yet!"); - // this is because _snapnodes, called in object.guideSnap, cannot only handle - // vertical or horizontal lines for now.... - // Rotating an agled guide will require some additional code, as it would be great to - // have it rotate around a snapped point - snap_dim = Inkscape::ObjectSnapper::SNAP_XY; + + if (!(object.ThisSnapperMightSnap() && _snap_enabled_globally)) { + return Inkscape::SnappedPoint(p, NR_HUGE); } SnappedConstraints sc; - object.guideSnap(sc, p, snap_dim); + object.guideSnap(sc, p, guide_normal); return findBestSnap(p, sc, false); } -- 2.30.2