From: Diederik van Lierop Date: Tue, 10 Aug 2010 22:18:26 +0000 (+0200) Subject: Implement constrained snapping to nodes X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=4216b5e8c1e820cadcb9fa34f651d87a2d404e87;p=inkscape.git Implement constrained snapping to nodes --- diff --git a/src/draw-context.cpp b/src/draw-context.cpp index a531b88d1..c0ae626d5 100644 --- a/src/draw-context.cpp +++ b/src/draw-context.cpp @@ -511,7 +511,7 @@ void spdc_endpoint_snap_rotation(SPEventContext const *const ec, Geom::Point &p, /* Snap it along best vector */ SnapManager &m = SP_EVENT_CONTEXT_DESKTOP(ec)->namedview->snap_manager; m.setup(SP_EVENT_CONTEXT_DESKTOP(ec)); - m.constrainedSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE, Inkscape::Snapper::SnapConstraint(best)); + m.constrainedSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE, Inkscape::Snapper::SnapConstraint(o, best)); } } } diff --git a/src/line-snapper.cpp b/src/line-snapper.cpp index 19e6c0fe6..0a1567a47 100644 --- a/src/line-snapper.cpp +++ b/src/line-snapper.cpp @@ -64,7 +64,8 @@ void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc, Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &/*bbox_to_snap*/, SnapConstraint const &c, - std::vector const */*it*/) const + std::vector const */*it*/, + std::vector */*unselected_nodes*/) const { if (_snap_enabled == false || _snapmanager->snapprefs.getSnapFrom(p.getSourceType()) == false) { diff --git a/src/line-snapper.h b/src/line-snapper.h index 4f3d17998..cdc45c286 100644 --- a/src/line-snapper.h +++ b/src/line-snapper.h @@ -35,7 +35,8 @@ public: Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &bbox_to_snap, SnapConstraint const &c, - std::vector const *it) const; + std::vector const *it, + std::vector *unselected_nodes) const; protected: typedef std::list > LineList; diff --git a/src/object-snapper.cpp b/src/object-snapper.cpp index d84ee9c4f..23af26d47 100644 --- a/src/object-snapper.cpp +++ b/src/object-snapper.cpp @@ -257,7 +257,8 @@ void Inkscape::ObjectSnapper::_collectNodes(Inkscape::SnapSourceType const &t, void Inkscape::ObjectSnapper::_snapNodes(SnappedConstraints &sc, Inkscape::SnapCandidatePoint const &p, - std::vector *unselected_nodes) const + std::vector *unselected_nodes, + SnapConstraint const &c) const { // Iterate through all nodes, find out which one is the closest to p, and snap to it! @@ -271,9 +272,20 @@ void Inkscape::ObjectSnapper::_snapNodes(SnappedConstraints &sc, bool success = false; for (std::vector::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); k++) { - Geom::Coord dist = Geom::L2((*k).getPoint() - p.getPoint()); + Geom::Point target_pt = (*k).getPoint(); + if (!c.isUndefined()) { + // We're snapping to nodes along a constraint only, so find out if this node + // is at the constraint, while allowing for a small margin + if (Geom::L2(target_pt - c.projection(target_pt)) > 1e-9) { + // The distance from the target point to its projection on the constraint + // is too large, so this point is not on the constraint. Skip it! + continue; + } + } + + Geom::Coord dist = Geom::L2(target_pt - p.getPoint()); if (dist < getSnapperTolerance() && dist < s.getSnapDistance()) { - s = SnappedPoint((*k).getPoint(), p.getSourceType(), p.getSourceNum(), (*k).getTargetType(), dist, getSnapperTolerance(), getSnapperAlwaysSnap(), false, true, (*k).getTargetBBox()); + s = SnappedPoint(target_pt, p.getSourceType(), p.getSourceNum(), (*k).getTargetType(), dist, getSnapperTolerance(), getSnapperAlwaysSnap(), false, true, (*k).getTargetBBox()); success = true; } } @@ -300,13 +312,13 @@ void Inkscape::ObjectSnapper::_snapTranslatingGuide(SnappedConstraints &sc, Geom::Coord tol = getSnapperTolerance(); for (std::vector::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); k++) { - + Geom::Point target_pt = (*k).getPoint(); // Project each node (*k) on the guide line (running through point p) - Geom::Point p_proj = Geom::projection((*k).getPoint(), Geom::Line(p, p + Geom::rot90(guide_normal))); - Geom::Coord dist = Geom::L2((*k).getPoint() - p_proj); // distance from node to the guide + Geom::Point p_proj = Geom::projection(target_pt, Geom::Line(p, p + Geom::rot90(guide_normal))); + Geom::Coord dist = Geom::L2(target_pt - p_proj); // distance from node to the guide Geom::Coord dist2 = Geom::L2(p - p_proj); // distance from projection of node on the guide, to the mouse location if ((dist < tol && dist2 < tol) || getSnapperAlwaysSnap()) { - s = SnappedPoint((*k).getPoint(), SNAPSOURCE_GUIDE, 0, (*k).getTargetType(), dist, tol, getSnapperAlwaysSnap(), false, true, (*k).getTargetBBox()); + s = SnappedPoint(target_pt, SNAPSOURCE_GUIDE, 0, (*k).getTargetType(), dist, tol, getSnapperAlwaysSnap(), false, true, (*k).getTargetBBox()); sc.points.push_back(s); } } @@ -608,7 +620,7 @@ void Inkscape::ObjectSnapper::freeSnap(SnappedConstraints &sc, _findCandidates(sp_document_root(_snapmanager->getDocument()), it, p.getSourceNum() == 0, local_bbox_to_snap, false, Geom::identity()); } - + // TODO: Argh, UGLY! Get rid of this here, move this logic to the snap manager bool snap_nodes = (_snapmanager->snapprefs.getSnapModeNode() && ( _snapmanager->snapprefs.getSnapToItemNode() || _snapmanager->snapprefs.getSnapSmoothNodes() || @@ -655,7 +667,8 @@ void Inkscape::ObjectSnapper::constrainedSnap( SnappedConstraints &sc, Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &bbox_to_snap, SnapConstraint const &c, - std::vector const *it) const + std::vector const *it, + std::vector *unselected_nodes) const { if (_snap_enabled == false || _snapmanager->snapprefs.getSnapFrom(p.getSourceType()) == false) { return; @@ -671,10 +684,24 @@ void Inkscape::ObjectSnapper::constrainedSnap( SnappedConstraints &sc, // This is useful for example when scaling an object while maintaining a fixed aspect ratio. It's // nodes are only allowed to move in one direction (i.e. in one degree of freedom). - // When snapping to objects, we either snap to their nodes or their paths. It is however very - // unlikely that any node will be exactly at the constrained line, so for a constrained snap - // to objects we will only consider the object's paths. Beside, the nodes will be at these paths, - // so we will more or less snap to them anyhow. + // TODO: Argh, UGLY! Get rid of this here, move this logic to the snap manager + bool snap_nodes = (_snapmanager->snapprefs.getSnapModeNode() && ( + _snapmanager->snapprefs.getSnapToItemNode() || + _snapmanager->snapprefs.getSnapSmoothNodes() || + _snapmanager->snapprefs.getSnapLineMidpoints() || + _snapmanager->snapprefs.getSnapObjectMidpoints() + )) || (_snapmanager->snapprefs.getSnapModeBBox() && ( + _snapmanager->snapprefs.getSnapToBBoxNode() || + _snapmanager->snapprefs.getSnapBBoxEdgeMidpoints() || + _snapmanager->snapprefs.getSnapBBoxMidpoints() + )) || (_snapmanager->snapprefs.getSnapModeBBoxOrNodes() && ( + _snapmanager->snapprefs.getIncludeItemCenter() || + _snapmanager->snapprefs.getSnapToPageBorder() + )); + + if (snap_nodes) { + _snapNodes(sc, p, unselected_nodes, c); + } if (_snapmanager->snapprefs.getSnapToItemPath() || _snapmanager->snapprefs.getSnapToBBoxPath() || _snapmanager->snapprefs.getSnapToPageBorder()) { _snapPathsConstrained(sc, p, c); diff --git a/src/object-snapper.h b/src/object-snapper.h index 99c8a077e..4933d8459 100644 --- a/src/object-snapper.h +++ b/src/object-snapper.h @@ -57,7 +57,8 @@ public: Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &bbox_to_snap, SnapConstraint const &c, - std::vector const *it) const; + std::vector const *it, + std::vector *unselected_nodes) const; private: //store some lists of candidates, points and paths, so we don't have to rebuild them for each point we want to snap @@ -74,7 +75,8 @@ private: void _snapNodes(SnappedConstraints &sc, Inkscape::SnapCandidatePoint const &p, - std::vector *unselected_nodes) const; // in desktop coordinates + std::vector *unselected_nodes, + SnapConstraint const &c = SnapConstraint()) const; // in desktop coordinates void _snapTranslatingGuide(SnappedConstraints &sc, Geom::Point const &p, diff --git a/src/snap.cpp b/src/snap.cpp index bcacb81e2..7ba85a9aa 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -374,7 +374,7 @@ Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::SnapCandidatePoint SnappedConstraints sc; SnapperList const snappers = getSnappers(); for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) { - (*i)->constrainedSnap(sc, p, bbox_to_snap, constraint, &_items_to_ignore); + (*i)->constrainedSnap(sc, p, bbox_to_snap, constraint, &_items_to_ignore, _unselected_nodes); } Inkscape::SnappedPoint result = findBestSnap(p, sc, true); @@ -422,7 +422,7 @@ Inkscape::SnappedPoint SnapManager::multipleConstrainedSnaps(Inkscape::SnapCandi // Try to snap to the constraint if (!snapping_is_futile) { for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) { - (*i)->constrainedSnap(sc, p, bbox_to_snap, *c, &_items_to_ignore); + (*i)->constrainedSnap(sc, p, bbox_to_snap, *c, &_items_to_ignore,_unselected_nodes); } } } @@ -526,14 +526,14 @@ void SnapManager::guideConstrainedSnap(Geom::Point &p, SPGuide const &guideline) SnappedConstraints sc; Inkscape::Snapper::SnapConstraint cl(guideline.point_on_line, Geom::rot90(guideline.normal_to_line)); if (object.ThisSnapperMightSnap()) { - object.constrainedSnap(sc, candidate, Geom::OptRect(), cl, NULL); + object.constrainedSnap(sc, candidate, Geom::OptRect(), cl, NULL, NULL); } // Snap to guides & grid lines SnapperList snappers = getGridSnappers(); snappers.push_back(&guide); for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) { - (*i)->constrainedSnap(sc, candidate, Geom::OptRect(), cl, NULL); + (*i)->constrainedSnap(sc, candidate, Geom::OptRect(), cl, NULL, NULL); } Inkscape::SnappedPoint const s = findBestSnap(candidate, sc, false); diff --git a/src/snapper.h b/src/snapper.h index d8214db80..91784d3ae 100644 --- a/src/snapper.h +++ b/src/snapper.h @@ -72,7 +72,7 @@ public: class SnapConstraint { private: - enum SnapConstraintType {LINE, DIRECTION, CIRCLE}; + enum SnapConstraintType {LINE, DIRECTION, CIRCLE, UNDEFINED}; public: // Constructs a direction constraint, e.g. horizontal or vertical but without a specified point @@ -82,11 +82,13 @@ public: SnapConstraint(Geom::Line const &l) : _point(l.origin()), _direction(l.versor()), _type(LINE) {} // Constructs a circular constraint SnapConstraint(Geom::Point const &p, Geom::Point const &d, Geom::Coord const &r) : _point(p), _direction(d), _radius(r), _type(CIRCLE) {} + // Undefined, or empty constraint + SnapConstraint() : _type(UNDEFINED) {} - bool hasPoint() const {return _type != DIRECTION;} + bool hasPoint() const {return _type != DIRECTION && _type != UNDEFINED;} Geom::Point getPoint() const { - g_assert(_type != DIRECTION); + g_assert(_type != DIRECTION && _type != UNDEFINED); return _point; } @@ -102,6 +104,7 @@ public: bool isCircular() const { return _type == CIRCLE; } bool isLinear() const { return _type == LINE; } bool isDirection() const { return _type == DIRECTION; } + bool isUndefined() const { return _type == UNDEFINED; } Geom::Point projection(Geom::Point const &p) const { // returns the projection of p on this constraint if (_type == CIRCLE) { @@ -114,11 +117,14 @@ public: // point to be projected is exactly at the center of the circle, so any point on the circle is a projection return _point + Geom::Point(_radius, 0); } - } else { + } else if (_type != UNDEFINED){ // project on to a linear constraint Geom::Point const p1_on_cl = (_type == LINE) ? _point : p; Geom::Point const p2_on_cl = p1_on_cl + _direction; return Geom::projection(p, Geom::Line(p1_on_cl, p2_on_cl)); + } else { + g_warning("Bug: trying to find the projection onto an undefined constraint"); + return Geom::Point(); } } @@ -133,7 +139,8 @@ public: Inkscape::SnapCandidatePoint const &/*p*/, Geom::OptRect const &/*bbox_to_snap*/, SnapConstraint const &/*c*/, - std::vector const */*it*/) const {}; + std::vector const */*it*/, + std::vector */*unselected_nodes*/) const {}; protected: SnapManager *_snapmanager;