From: Krzysztof Kosiński Date: Sun, 14 Mar 2010 21:04:08 +0000 (+0100) Subject: New node tool: implement handle snapping X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=2db884161c805d3382058d7a549cbaaec79bbc4d;p=inkscape.git New node tool: implement handle snapping --- diff --git a/src/snap.cpp b/src/snap.cpp index 352683623..b8b08dad5 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -368,32 +368,25 @@ Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::SnapCandidatePoint Inkscape::SnappedPoint no_snap = Inkscape::SnappedPoint(pp, p.getSourceType(), p.getSourceNum(), Inkscape::SNAPTARGET_CONSTRAINT, Geom::L2(pp - p.getPoint()), 0, false, false); if (!someSnapperMightSnap()) { - // The constraint should always be enforced, so we return pp here instead of p - if (_snapindicator) { - _desktop->snapindicator->set_new_snaptarget(no_snap); - } + // Always return point on constraint return no_snap; } - // Then try to snap the projected point - Inkscape::SnapCandidatePoint candidate(pp, p.getSourceType(), p.getSourceNum(), Inkscape::SNAPTARGET_UNDEFINED, Geom::Rect()); - SnappedConstraints sc; SnapperList const snappers = getSnappers(); for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) { - (*i)->constrainedSnap(sc, candidate, bbox_to_snap, constraint, &_items_to_ignore); + (*i)->constrainedSnap(sc, p, bbox_to_snap, constraint, &_items_to_ignore); } - Inkscape::SnappedPoint result = findBestSnap(candidate, sc, true); + Inkscape::SnappedPoint result = findBestSnap(p, sc, true); if (result.getSnapped()) { + // only change the snap indicator if we really snapped to something + if (_snapindicator) { + _desktop->snapindicator->set_new_snaptarget(result); + } return result; } - - // The constraint should always be enforced, so we return pp here instead of p - if (_snapindicator) { - _desktop->snapindicator->set_new_snaptarget(no_snap); - } return no_snap; } diff --git a/src/ui/tool/node.cpp b/src/ui/tool/node.cpp index b72da1374..ebf30cc77 100644 --- a/src/ui/tool/node.cpp +++ b/src/ui/tool/node.cpp @@ -235,9 +235,13 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event) { Geom::Point parent_pos = _parent->position(); Geom::Point origin = _last_drag_origin(); + SnapManager &sm = _desktop->namedview->snap_manager; + bool snap = sm.someSnapperMightSnap(); + // with Alt, preserve length if (held_alt(*event)) { new_pos = parent_pos + Geom::unit_vector(new_pos - parent_pos) * _saved_length; + snap = false; } // with Ctrl, constrain to M_PI/rotationsnapsperpi increments from vertical // and the original position. @@ -246,7 +250,7 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event) int snaps = 2 * prefs->getIntLimited("/options/rotationsnapsperpi/value", 12, 1, 1000); // note: if snapping to the original position is only desired in the original - // direction of the handle, change 2nd line below to Ray instead of Line + // direction of the handle, change to Ray instead of Line Geom::Line original_line(parent_pos, origin); Geom::Point snap_pos = parent_pos + Geom::constrain_angle( Geom::Point(0,0), new_pos - parent_pos, snaps, Geom::Point(1,0)); @@ -257,7 +261,34 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event) } else { new_pos = orig_pos; } + snap = false; + } + + std::vector unselected; + if (snap) { + 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); + } + sm.setupIgnoreSelection(_desktop, true, &unselected); + + Node *node_away = (this == &_parent->_front ? _parent->_prev() : _parent->_next()); + if (_parent->type() == NODE_SMOOTH && Node::_is_line_segment(_parent, node_away)) { + Inkscape::Snapper::ConstraintLine cl(_parent->position(), + _parent->position() - node_away->position()); + Inkscape::SnappedPoint p; + p = sm.constrainedSnap(Inkscape::SnapCandidatePoint(new_pos, SNAPSOURCE_NODE_HANDLE), cl); + if (p.getSnapped()) { + p.getPoint(new_pos); + } + } else { + sm.freeSnapReturnByRef(new_pos, SNAPSOURCE_NODE_HANDLE); + } } + // with Shift, if the node is cusp, rotate the other handle as well if (_parent->type() == NODE_CUSP && !_drag_out) { if (held_shift(*event)) { @@ -269,6 +300,7 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event) other().setPosition(_saved_other_pos); } } + move(new_pos); // needed for correct update, even though it's redundant _pm().update(); } @@ -346,11 +378,11 @@ Glib::ustring Handle::_getTip(unsigned state) if (state_held_control(state)) { if (state_held_shift(state) && can_shift_rotate) { return format_tip(C_("Path handle tip", - "Ctrl: snap rotation angle to %g° increments, click to retract"), + "Shift+Ctrl: snap rotation angle to %g° increments and rotate both handles"), snap_increment_degrees()); } else { return format_tip(C_("Path handle tip", - "Shift+Ctrl: snap rotation angle to %g° increments and rotate both handles"), + "Ctrl: snap rotation angle to %g° increments, click to retract"), snap_increment_degrees()); } } else if (state_held_shift(state) && can_shift_rotate) { @@ -943,7 +975,7 @@ void Node::dragged(Geom::Point &new_pos, GdkEventMotion *event) unselected.push_back(p); } } - sm.setupIgnoreSelection(_desktop, true, &unselected); + sm.setupIgnoreSelection(_desktop, false, &unselected); } if (held_control(*event)) { @@ -984,10 +1016,10 @@ void Node::dragged(Geom::Point &new_pos, GdkEventMotion *event) } if (fp.getSnapped() || bp.getSnapped()) { if (fp.isOtherSnapBetter(bp, false)) { - bp.getPoint(new_pos); - } else { - fp.getPoint(new_pos); + fp = bp; } + fp.getPoint(new_pos); + _desktop->snapindicator->set_new_snaptarget(fp); } else { boost::optional pos; if (line_front) { @@ -1018,6 +1050,9 @@ void Node::dragged(Geom::Point &new_pos, GdkEventMotion *event) fp = bp; } fp.getPoint(new_pos); + if (fp.getTarget() != SNAPTARGET_CONSTRAINT) { + _desktop->snapindicator->set_new_snaptarget(fp); + } } else { Geom::Point origin = _last_drag_origin(); Geom::Point delta = new_pos - origin; @@ -1026,7 +1061,11 @@ void Node::dragged(Geom::Point &new_pos, GdkEventMotion *event) } } } else if (snap) { - sm.freeSnapReturnByRef(new_pos, _snapSourceType()); + Inkscape::SnappedPoint p = sm.freeSnap(Inkscape::SnapCandidatePoint(new_pos, _snapSourceType())); + if (p.getSnapped()) { + p.getPoint(new_pos); + _desktop->snapindicator->set_new_snaptarget(p); + } } SelectableControlPoint::dragged(new_pos, event); diff --git a/src/ui/tool/path-manipulator.cpp b/src/ui/tool/path-manipulator.cpp index d395d0e0a..ebf0f3828 100644 --- a/src/ui/tool/path-manipulator.cpp +++ b/src/ui/tool/path-manipulator.cpp @@ -1045,6 +1045,7 @@ void PathManipulator::_createControlPointsFromGeometry() // we need to set the nodetypes after all the handles are in place, // so that pickBestType works correctly // TODO maybe migrate to inkscape:node-types? + // TODO move this into SPPath - do not manipulate directly gchar const *nts_raw = _path ? _path->repr->attribute(_nodetypesKey().data()) : 0; std::string nodetype_string = nts_raw ? nts_raw : ""; /* Calculate the needed length of the nodetype string.