From 457ae1f251852ceb19549b16cf82feb4f01d9066 Mon Sep 17 00:00:00 2001 From: Diederik van Lierop Date: Mon, 27 Dec 2010 22:18:34 +0100 Subject: [PATCH] Node tool: snap while scaling a selection of nodes. Consider this as experimental; needs cleanup! --- src/seltrans.cpp | 28 ++--------- src/seltrans.h | 1 - src/snap.cpp | 56 ++++++++++++++++++--- src/snap.h | 17 ++++--- src/ui/tool/control-point-selection.cpp | 18 +++++++ src/ui/tool/control-point-selection.h | 5 ++ src/ui/tool/transform-handle-set.cpp | 66 ++++++++++++++++++++++++- 7 files changed, 148 insertions(+), 43 deletions(-) diff --git a/src/seltrans.cpp b/src/seltrans.cpp index b0e19e60c..b1d184986 100644 --- a/src/seltrans.cpp +++ b/src/seltrans.cpp @@ -299,7 +299,7 @@ void Inkscape::SelTrans::grab(Geom::Point const &p, gdouble x, gdouble y, bool s An average user would rarely ever try to snap such a large number of nodes anyway, because (s)he could hardly discern which node would be snapping */ if (prefs->getBool("/options/snapclosestonly/value", false)) { - _keepClosestPointOnly(_snap_points, p); + m.keepClosestPointOnly(_snap_points, p); } else { _snap_points = snap_points_hull; } @@ -360,14 +360,14 @@ void Inkscape::SelTrans::grab(Geom::Point const &p, gdouble x, gdouble y, bool s if (prefs->getBool("/options/snapclosestonly/value", false)) { if (m.snapprefs.getSnapModeNode()) { - _keepClosestPointOnly(_snap_points, p); + m.keepClosestPointOnly(_snap_points, p); } else { _snap_points.clear(); // don't keep any point } if (m.snapprefs.getSnapModeBBox()) { - _keepClosestPointOnly(_bbox_points, p); - _keepClosestPointOnly(_bbox_points_for_translating, p); + m.keepClosestPointOnly(_bbox_points, p); + m.keepClosestPointOnly(_bbox_points_for_translating, p); } else { _bbox_points.clear(); // don't keep any point _bbox_points_for_translating.clear(); @@ -1632,26 +1632,6 @@ Geom::Point Inkscape::SelTrans::_calcAbsAffineGeom(Geom::Scale const geom_scale) return _calcAbsAffineDefault(geom_scale); // this is bogus, but we must return _something_ } -void Inkscape::SelTrans::_keepClosestPointOnly(std::vector &points, const Geom::Point &reference) -{ - if (points.size() < 2) return; - - Inkscape::SnapCandidatePoint closest_point = Inkscape::SnapCandidatePoint(Geom::Point(NR_HUGE, NR_HUGE), SNAPSOURCE_UNDEFINED, SNAPTARGET_UNDEFINED); - Geom::Coord closest_dist = NR_HUGE; - - for(std::vector::const_iterator i = points.begin(); i != points.end(); i++) { - Geom::Coord dist = Geom::L2((*i).getPoint() - reference); - if (i == points.begin() || dist < closest_dist) { - closest_point = *i; - closest_dist = dist; - } - } - - closest_point.setSourceNum(-1); - points.clear(); - points.push_back(closest_point); -} - /* Local Variables: mode:c++ diff --git a/src/seltrans.h b/src/seltrans.h index 0183683ff..bb26aa2cb 100644 --- a/src/seltrans.h +++ b/src/seltrans.h @@ -103,7 +103,6 @@ private: Geom::Point _getGeomHandlePos(Geom::Point const &visual_handle_pos); Geom::Point _calcAbsAffineDefault(Geom::Scale const default_scale); Geom::Point _calcAbsAffineGeom(Geom::Scale const geom_scale); - void _keepClosestPointOnly(std::vector &points, const Geom::Point &reference); void _display_snapsource(); enum State { diff --git a/src/snap.cpp b/src/snap.cpp index 79f398cc5..85d2fd5af 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -22,6 +22,7 @@ #include "sp-namedview.h" #include "snap.h" +#include "snap-enums.h" #include "snapped-line.h" #include "snapped-curve.h" @@ -682,7 +683,7 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( Geom::Point const &transformation, Geom::Point const &origin, Geom::Dim2 dim, - bool uniform) const + bool uniform) { /* 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 @@ -734,6 +735,12 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( g_warning("Unconstrained rotation is not supported!"); } + // We will try to snap a set of points, but we don't want to have a snap indicator displayed + // for each of them. That's why it's temporarily disabled here, and re-enabled again after we + // have finished calling the freeSnap() and constrainedSnap() methods + bool _orig_snapindicator_status = _snapindicator; + _snapindicator = false; + std::vector::iterator j = transformed_points.begin(); // std::cout << std::endl; @@ -802,6 +809,9 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( // std::cout << "dist = " << snapped_point.getSnapDistance() << std::endl; snapped_point.setPointerDistance(Geom::L2(pointer - (*i).getPoint())); + // Allow the snapindicator to be displayed again + _snapindicator = _orig_snapindicator_status; + Geom::Point result; /*Find the transformation that describes where the snapped point has @@ -891,6 +901,7 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( if (snapped_point.getSnapped()) { // We snapped; keep track of the best snap + // TODO: Compare the transformations instead of the snap points; we should be looking for the closest transformation if (best_snapped_point.isOtherSnapBetter(snapped_point, true)) { best_transformation = result; best_snapped_point = snapped_point; @@ -932,6 +943,15 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( // Using " < 1e6" instead of " < NR_HUGE" for catching some rounding errors // These rounding errors might be caused by NRRects, see bug #1584301 best_snapped_point.setSnapDistance(best_metric < 1e6 ? best_metric : NR_HUGE); + + if (_snapindicator) { + if (best_snapped_point.getSnapped()) { + _desktop->snapindicator->set_new_snaptarget(best_snapped_point); + } else { + _desktop->snapindicator->remove_snaptarget(); + } + } + return best_snapped_point; } @@ -947,7 +967,7 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( Inkscape::SnappedPoint SnapManager::freeSnapTranslate(std::vector const &p, Geom::Point const &pointer, - Geom::Point const &tr) const + Geom::Point const &tr) { Inkscape::SnappedPoint result = _snapTransformed(p, pointer, false, Geom::Point(0,0), TRANSLATE, tr, Geom::Point(0,0), Geom::X, false); @@ -971,7 +991,7 @@ Inkscape::SnappedPoint SnapManager::freeSnapTranslate(std::vector const &p, Geom::Point const &pointer, Inkscape::Snapper::SnapConstraint const &constraint, - Geom::Point const &tr) const + Geom::Point const &tr) { Inkscape::SnappedPoint result = _snapTransformed(p, pointer, true, constraint, TRANSLATE, tr, Geom::Point(0,0), Geom::X, false); @@ -996,7 +1016,7 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapTranslate(std::vector const &p, Geom::Point const &pointer, Geom::Scale const &s, - Geom::Point const &o) const + Geom::Point const &o) { Inkscape::SnappedPoint result = _snapTransformed(p, pointer, false, Geom::Point(0,0), SCALE, Geom::Point(s[Geom::X], s[Geom::Y]), o, Geom::X, false); @@ -1021,7 +1041,7 @@ Inkscape::SnappedPoint SnapManager::freeSnapScale(std::vector const &p, Geom::Point const &pointer, Geom::Scale const &s, - Geom::Point const &o) const + Geom::Point const &o) { // When constrained scaling, only uniform scaling is supported. Inkscape::SnappedPoint result = _snapTransformed(p, pointer, true, Geom::Point(0,0), SCALE, Geom::Point(s[Geom::X], s[Geom::Y]), o, Geom::X, true); @@ -1050,7 +1070,7 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapStretch(std::vector const &p, Geom::Point const &pointer, Geom::Coord const &angle, - Geom::Point const &o) const + Geom::Point const &o) { // Snapping the nodes of the bounding box of a selection that is being transformed, will only work if // the transformation of the bounding box is equal to the transformation of the individual nodes. This is @@ -1429,6 +1449,26 @@ void SnapManager::_displaySnapsource(Inkscape::SnapCandidatePoint const &p) cons } } +void SnapManager::keepClosestPointOnly(std::vector &points, const Geom::Point &reference) const +{ + if (points.size() < 2) return; + + Inkscape::SnapCandidatePoint closest_point = Inkscape::SnapCandidatePoint(Geom::Point(NR_HUGE, NR_HUGE), Inkscape::SNAPSOURCE_UNDEFINED, Inkscape::SNAPTARGET_UNDEFINED); + Geom::Coord closest_dist = NR_HUGE; + + for(std::vector::const_iterator i = points.begin(); i != points.end(); i++) { + Geom::Coord dist = Geom::L2((*i).getPoint() - reference); + if (i == points.begin() || dist < closest_dist) { + closest_point = *i; + closest_dist = dist; + } + } + + closest_point.setSourceNum(-1); + points.clear(); + points.push_back(closest_point); +} + /* Local Variables: mode:c++ diff --git a/src/snap.h b/src/snap.h index c79bd308a..8f8416ee5 100644 --- a/src/snap.h +++ b/src/snap.h @@ -149,41 +149,41 @@ public: Inkscape::SnappedPoint freeSnapTranslate(std::vector const &p, Geom::Point const &pointer, - Geom::Point const &tr) const; + Geom::Point const &tr); Inkscape::SnappedPoint constrainedSnapTranslate(std::vector const &p, Geom::Point const &pointer, Inkscape::Snapper::SnapConstraint const &constraint, - Geom::Point const &tr) const; + Geom::Point const &tr); Inkscape::SnappedPoint freeSnapScale(std::vector const &p, Geom::Point const &pointer, Geom::Scale const &s, - Geom::Point const &o) const; + Geom::Point const &o); Inkscape::SnappedPoint constrainedSnapScale(std::vector const &p, Geom::Point const &pointer, Geom::Scale const &s, - Geom::Point const &o) const; + Geom::Point const &o); Inkscape::SnappedPoint constrainedSnapStretch(std::vector const &p, Geom::Point const &pointer, Geom::Coord const &s, Geom::Point const &o, Geom::Dim2 d, - bool uniform) const; + bool uniform); Inkscape::SnappedPoint constrainedSnapSkew(std::vector const &p, Geom::Point const &pointer, Inkscape::Snapper::SnapConstraint const &constraint, Geom::Point const &s, // s[0] = skew factor, s[1] = scale factor Geom::Point const &o, - Geom::Dim2 d) const; + Geom::Dim2 d); Inkscape::SnappedPoint constrainedSnapRotate(std::vector const &p, Geom::Point const &pointer, Geom::Coord const &angle, - Geom::Point const &o) const; + Geom::Point const &o); Inkscape::GuideSnapper guide; ///< guide snapper Inkscape::ObjectSnapper object; ///< snapper to other objects @@ -200,6 +200,7 @@ public: bool getSnapIndicator() const {return _snapindicator;} Inkscape::SnappedPoint findBestSnap(Inkscape::SnapCandidatePoint const &p, SnappedConstraints const &sc, bool constrained, bool noCurves = false, bool allowOffScreen = false) const; + void keepClosestPointOnly(std::vector &points, const Geom::Point &reference) const; protected: SPNamedView const *_named_view; @@ -220,7 +221,7 @@ private: Geom::Point const &transformation, Geom::Point const &origin, Geom::Dim2 dim, - bool uniform) const; + bool uniform); Geom::Point _transformPoint(Inkscape::SnapCandidatePoint const &p, Transformation const transformation_type, diff --git a/src/ui/tool/control-point-selection.cpp b/src/ui/tool/control-point-selection.cpp index baa53f76e..517e90da2 100644 --- a/src/ui/tool/control-point-selection.cpp +++ b/src/ui/tool/control-point-selection.cpp @@ -642,6 +642,24 @@ bool ControlPointSelection::event(GdkEvent *event) return false; } +std::vector ControlPointSelection::getOriginalPoints() +{ + std::vector points; + for (iterator i = _points.begin(); i != _points.end(); ++i) { + points.push_back(Inkscape::SnapCandidatePoint(_original_positions[*i], SNAPSOURCE_NODE_HANDLE)); + } + return points; +} + +void ControlPointSelection::setOriginalPoints() +{ + _original_positions.clear(); + for (iterator i = _points.begin(); i != _points.end(); ++i) { + _original_positions.insert(std::make_pair(*i, (*i)->position())); + } +} + + } // namespace UI } // namespace Inkscape diff --git a/src/ui/tool/control-point-selection.h b/src/ui/tool/control-point-selection.h index 6a5b05e85..0e5acf6c5 100644 --- a/src/ui/tool/control-point-selection.h +++ b/src/ui/tool/control-point-selection.h @@ -22,6 +22,7 @@ #include "util/unordered-containers.h" #include "ui/tool/commit-events.h" #include "ui/tool/manipulator.h" +#include "snap-candidate.h" class SPDesktop; struct SPCanvasGroup; @@ -108,6 +109,10 @@ public: sigc::signal signal_update; sigc::signal signal_point_changed; sigc::signal signal_commit; + + std::vector getOriginalPoints(); + void setOriginalPoints(); + private: // The functions below are invoked from SelectableControlPoint. // Previously they were connected to handlers when selecting, but this diff --git a/src/ui/tool/transform-handle-set.cpp b/src/ui/tool/transform-handle-set.cpp index cafd592a3..209b7fe98 100644 --- a/src/ui/tool/transform-handle-set.cpp +++ b/src/ui/tool/transform-handle-set.cpp @@ -19,11 +19,15 @@ #include "display/sodipodi-ctrlrect.h" #include "preferences.h" #include "snap.h" +#include "snap-candidate.h" #include "sp-namedview.h" #include "ui/tool/commit-events.h" #include "ui/tool/control-point.h" +#include "ui/tool/control-point-selection.h" +#include "ui/tool/selectable-control-point.h" #include "ui/tool/event-utils.h" #include "ui/tool/transform-handle-set.h" +#include "ui/tool/node-tool.h" // FIXME BRAIN DAMAGE WARNING: this is a global variable in select-context.cpp // It should be moved to a header @@ -96,6 +100,8 @@ protected: Geom::Matrix _last_transform; Geom::Point _origin; TransformHandleSet &_th; + std::vector _snap_points; + private: virtual bool grabbed(GdkEventMotion *) { _origin = position(); @@ -105,6 +111,20 @@ private: _th._setActiveHandle(this); _cset = &invisible_cset; _setState(_state); + + // Collect the snap-candidates, one for each selected node. These will be stored in the _snap_points vector. + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + SnapManager &m = desktop->namedview->snap_manager; + InkNodeTool *nt = INK_NODE_TOOL(_desktop->event_context); + ControlPointSelection *selection = nt->_selected_nodes.get(); + + _snap_points = selection->getOriginalPoints(); + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + if (prefs->getBool("/options/snapclosestonly/value", false)) { + m.keepClosestPointOnly(_snap_points, _origin); + } + return false; } virtual void dragged(Geom::Point &new_pos, GdkEventMotion *event) @@ -118,6 +138,7 @@ private: _last_transform = t; } virtual void ungrabbed(GdkEventButton *) { + _snap_points.clear(); _th._clearActiveHandle(); _cset = &thandle_cset; _setState(_state); @@ -177,23 +198,64 @@ protected: _sc_center = _th.rotationCenter(); _sc_opposite = _th.bounds().corner(_corner + 2); _last_scale_x = _last_scale_y = 1.0; + InkNodeTool *nt = INK_NODE_TOOL(_desktop->event_context); + ControlPointSelection *selection = nt->_selected_nodes.get(); + std::cout << "startTransform()" << std::endl; + selection->setOriginalPoints(); } virtual Geom::Matrix computeTransform(Geom::Point const &new_pos, GdkEventMotion *event) { Geom::Point scc = held_shift(*event) ? _sc_center : _sc_opposite; Geom::Point vold = _origin - scc, vnew = new_pos - scc; + // avoid exploding the selection if (Geom::are_near(vold[Geom::X], 0) || Geom::are_near(vold[Geom::Y], 0)) return Geom::identity(); double scale[2] = { vnew[Geom::X] / vold[Geom::X], vnew[Geom::Y] / vold[Geom::Y] }; + if (held_alt(*event)) { for (unsigned i = 0; i < 2; ++i) { if (scale[i] >= 1.0) scale[i] = round(scale[i]); else scale[i] = 1.0 / round(1.0 / scale[i]); } - } else if (held_control(*event)) { - scale[0] = scale[1] = std::min(scale[0], scale[1]); + } else { + //SPDesktop *desktop = _th._desktop; // Won't work as _desktop is protected + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + SnapManager &m = desktop->namedview->snap_manager; + + // The lines below have been copied from Handle::dragged() in node.cpp, and need to be + // activated if we want to snap to unselected (i.e. stationary) nodes and stationary pieces of paths of the + // path that's currently being edited + /* + std::vector unselected; + typedef ControlPointSelection::Set Set; + Set &nodes = _parent->_selection.allPoints(); + for (Set::iterator i = nodes.begin(); i != nodes.end(); ++i) { + Node *n = static_cast(*i); + Inkscape::SnapCandidatePoint p(n->position(), n->_snapSourceType(), n->_snapTargetType()); + unselected.push_back(p); + } + m.setupIgnoreSelection(_desktop, true, &unselected); + */ + + m.setupIgnoreSelection(_desktop); + + Inkscape::SnappedPoint sp; + if (held_control(*event)) { + scale[0] = scale[1] = std::min(scale[0], scale[1]); + sp = m.constrainedSnapScale(_snap_points, _origin, Geom::Scale(scale[0], scale[1]), scc); + } else { + sp = m.freeSnapScale(_snap_points, _origin, Geom::Scale(scale[0], scale[1]), scc); + } + m.unSetup(); + + if (sp.getSnapped()) { + Geom::Point result = sp.getTransformation(); + scale[0] = result[0]; + scale[1] = result[1]; + } } + _last_scale_x = scale[0]; _last_scale_y = scale[1]; Geom::Matrix t = Geom::Translate(-scc) -- 2.30.2