diff options
| author | Krzysztof Kosi??ski <tweenk.pl@gmail.com> | 2011-04-07 23:42:04 +0000 |
|---|---|---|
| committer | Krzysztof KosiĆski <tweenk.pl@gmail.com> | 2011-04-07 23:42:04 +0000 |
| commit | 945ce419c806c73d70203dec33ececafbe108a92 (patch) | |
| tree | cfcdb59bf47e9db7f9e01f7eebb59924bdeaea94 /src/ui/tool/node.cpp | |
| parent | Merge from trunk (again) (diff) | |
| parent | Extensions. SVG+media fix (see Bug #400356). (diff) | |
| download | inkscape-945ce419c806c73d70203dec33ececafbe108a92.tar.gz inkscape-945ce419c806c73d70203dec33ececafbe108a92.zip | |
Merge from trunk
(bzr r9508.1.73)
Diffstat (limited to 'src/ui/tool/node.cpp')
| -rw-r--r-- | src/ui/tool/node.cpp | 121 |
1 files changed, 80 insertions, 41 deletions
diff --git a/src/ui/tool/node.cpp b/src/ui/tool/node.cpp index 12d04dd2b..8e3da266b 100644 --- a/src/ui/tool/node.cpp +++ b/src/ui/tool/node.cpp @@ -113,12 +113,29 @@ void Handle::move(Geom::Point const &new_pos) Handle *towards_second = node_towards ? node_towards->handleToward(_parent) : NULL; if (Geom::are_near(new_pos, _parent->position())) { - // The handle becomes degenerate. If the segment between it and the node + // The handle becomes degenerate. + // Adjust node type as necessary. + if (other->isDegenerate()) { + // If both handles become degenerate, convert to parent cusp node + _parent->setType(NODE_CUSP, false); + } else { + // Only 1 handle becomes degenerate + switch (_parent->type()) { + case NODE_AUTO: + case NODE_SYMMETRIC: + _parent->setType(NODE_SMOOTH, false); + break; + default: + // do nothing for other node types + break; + } + } + // If the segment between the handle and the node // in its direction becomes linear and there are smooth nodes // at its ends, make their handles colinear with the segment - if (towards && towards->isDegenerate()) { + if (towards && towards_second->isDegenerate()) { if (node_towards->type() == NODE_SMOOTH) { - towards_second->setDirection(*_parent, *node_towards); + towards->setDirection(*_parent, *node_towards); } if (_parent->type() == NODE_SMOOTH) { other->setDirection(*node_towards, *_parent); @@ -160,6 +177,7 @@ void Handle::move(Geom::Point const &new_pos) void Handle::setPosition(Geom::Point const &p) { + Geom::Point old_pos = position(); ControlPoint::setPosition(p); sp_ctrlline_set_coords(SP_CTRLLINE(_handle_line), _parent->position(), position()); @@ -167,15 +185,12 @@ void Handle::setPosition(Geom::Point const &p) if (Geom::are_near(position(), _parent->position())) _degenerate = true; else _degenerate = false; + if (_parent->_handles_shown && _parent->visible() && !_degenerate) { setVisible(true); } else { setVisible(false); } - // If both handles become degenerate, convert to parent cusp node - if (_parent->isDegenerate()) { - _parent->setType(NODE_CUSP, false); - } } void Handle::setLength(double len) @@ -187,7 +202,7 @@ void Handle::setLength(double len) void Handle::retract() { - setPosition(_parent->position()); + move(_parent->position()); } void Handle::setDirection(Geom::Point const &from, Geom::Point const &to) @@ -212,6 +227,35 @@ char const *Handle::handle_type_to_localized_string(NodeType type) } } +bool Handle::_eventHandler(GdkEvent *event) +{ + switch (event->type) + { + case GDK_KEY_PRESS: + switch (shortcut_key(event->key)) + { + case GDK_s: + case GDK_S: + if (held_only_shift(event->key) && _parent->_type == NODE_CUSP) { + // when Shift+S is pressed when hovering over a handle belonging to a cusp node, + // hold this handle in place; otherwise process normally + // this handle is guaranteed not to be degenerate + other()->move(_parent->position() - (position() - _parent->position())); + _parent->setType(NODE_SMOOTH, false); + _parent->_pm().update(); // magic triple combo to add undo event + _parent->_pm().writeXML(); + _parent->_pm()._commit(_("Change node type")); + return true; + } + break; + default: break; + } + default: break; + } + + return ControlPoint::_eventHandler(event); +} + bool Handle::grabbed(GdkEventMotion *) { _saved_other_pos = other()->position(); @@ -226,6 +270,7 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event) Geom::Point origin = _last_drag_origin(); SnapManager &sm = _desktop->namedview->snap_manager; bool snap = sm.someSnapperMightSnap(); + boost::optional<Inkscape::Snapper::SnapConstraint> ctrl_constraint; // with Alt, preserve length if (held_alt(*event)) { @@ -241,16 +286,23 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event) // note: if snapping to the original position is only desired in the original // direction of the handle, change to Ray instead of Line Geom::Line original_line(parent_pos, origin); + Geom::Line perp_line(parent_pos, parent_pos + Geom::rot90(origin - parent_pos)); Geom::Point snap_pos = parent_pos + Geom::constrain_angle( Geom::Point(0,0), new_pos - parent_pos, snaps, Geom::Point(1,0)); Geom::Point orig_pos = original_line.pointAt(original_line.nearestPoint(new_pos)); + Geom::Point perp_pos = perp_line.pointAt(perp_line.nearestPoint(new_pos)); - if (Geom::distance(snap_pos, new_pos) < Geom::distance(orig_pos, new_pos)) { - new_pos = snap_pos; - } else { - new_pos = orig_pos; + Geom::Point result = snap_pos; + ctrl_constraint = Inkscape::Snapper::SnapConstraint(parent_pos, parent_pos - snap_pos); + if (Geom::distance(orig_pos, new_pos) < Geom::distance(result, new_pos)) { + result = orig_pos; + ctrl_constraint = Inkscape::Snapper::SnapConstraint(parent_pos, parent_pos - orig_pos); } - snap = false; + if (Geom::distance(perp_pos, new_pos) < Geom::distance(result, new_pos)) { + result = perp_pos; + ctrl_constraint = Inkscape::Snapper::SnapConstraint(parent_pos, parent_pos - perp_pos); + } + new_pos = result; } std::vector<Inkscape::SnapCandidatePoint> unselected; @@ -264,13 +316,20 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event) } sm.setupIgnoreSelection(_desktop, true, &unselected); - Node *node_away = (this == &_parent->_front ? _parent->_prev() : _parent->_next()); + Node *node_away = _parent->nodeAwayFrom(this); if (_parent->type() == NODE_SMOOTH && Node::_is_line_segment(_parent, node_away)) { Inkscape::Snapper::SnapConstraint cl(_parent->position(), _parent->position() - node_away->position()); Inkscape::SnappedPoint p; p = sm.constrainedSnap(Inkscape::SnapCandidatePoint(new_pos, SNAPSOURCE_NODE_HANDLE), cl); new_pos = p.getPoint(); + } else if (ctrl_constraint) { + // NOTE: this is subtly wrong. + // We should get all possible constraints and snap along them using + // multipleConstrainedSnaps, instead of first snapping to angle and the to objects + Inkscape::SnappedPoint p; + p = sm.constrainedSnap(Inkscape::SnapCandidatePoint(new_pos, SNAPSOURCE_NODE_HANDLE), *ctrl_constraint); + new_pos = p.getPoint(); } else { sm.freeSnapReturnByRef(new_pos, SNAPSOURCE_NODE_HANDLE); } @@ -455,7 +514,7 @@ void Node::move(Geom::Point const &new_pos) _fixNeighbors(old_pos, new_pos); } -void Node::transform(Geom::Matrix const &m) +void Node::transform(Geom::Affine const &m) { Geom::Point old_pos = position(); setPosition(position() * m); @@ -770,26 +829,6 @@ bool Node::_eventHandler(GdkEvent *event) return ControlPoint::_eventHandler(event); } -// TODO Move this to 2Geom! -static double bezier_length (Geom::Point a0, Geom::Point a1, Geom::Point a2, Geom::Point a3) -{ - double lower = Geom::distance(a0, a3); - double upper = Geom::distance(a0, a1) + Geom::distance(a1, a2) + Geom::distance(a2, a3); - - if (upper - lower < Geom::EPSILON) return (lower + upper)/2; - - Geom::Point // Casteljau subdivision - b0 = a0, - c0 = a3, - b1 = 0.5*(a0 + a1), - t0 = 0.5*(a1 + a2), - c1 = 0.5*(a2 + a3), - b2 = 0.5*(b1 + t0), - c2 = 0.5*(t0 + c1), - b3 = 0.5*(b2 + c2); // == c3 - return bezier_length(b0, b1, b2, b3) + bezier_length(b3, c2, c1, c0); -} - /** Select or deselect a node in this node's subpath based on its path distance from this node. * @param dir If negative, shrink selection by one node; if positive, grow by one node */ void Node::_linearGrow(int dir) @@ -816,7 +855,7 @@ void Node::_linearGrow(int dir) // find first unselected nodes on both sides while (fwd && fwd->selected()) { NodeList::iterator n = fwd.next(); - distance_front += bezier_length(*fwd, fwd->_front, n->_back, *n); + distance_front += Geom::bezier_length(*fwd, fwd->_front, n->_back, *n); fwd = n; if (fwd == this_iter) // there is no unselected node in this cyclic subpath @@ -827,7 +866,7 @@ void Node::_linearGrow(int dir) // so we are guaranteed to stop. while (rev && rev->selected()) { NodeList::iterator p = rev.prev(); - distance_back += bezier_length(*rev, rev->_back, p->_front, *p); + distance_back += Geom::bezier_length(*rev, rev->_back, p->_front, *p); rev = p; } @@ -858,7 +897,7 @@ void Node::_linearGrow(int dir) last_distance_front = distance_front; } NodeList::iterator n = fwd.next(); - if (n) distance_front += bezier_length(*fwd, fwd->_front, n->_back, *n); + if (n) distance_front += Geom::bezier_length(*fwd, fwd->_front, n->_back, *n); fwd = n; } else if (rev && (!fwd || distance_front > distance_back)) { if (rev->selected()) { @@ -866,7 +905,7 @@ void Node::_linearGrow(int dir) last_distance_back = distance_back; } NodeList::iterator p = rev.prev(); - if (p) distance_back += bezier_length(*rev, rev->_back, p->_front, *p); + if (p) distance_back += Geom::bezier_length(*rev, rev->_back, p->_front, *p); rev = p; } // Check whether we walked the entire cyclic subpath. @@ -876,8 +915,8 @@ void Node::_linearGrow(int dir) if (fwd && fwd == rev) { if (!fwd->selected()) break; NodeList::iterator fwdp = fwd.prev(), revn = rev.next(); - double df = distance_front + bezier_length(*fwdp, fwdp->_front, fwd->_back, *fwd); - double db = distance_back + bezier_length(*revn, revn->_back, rev->_front, *rev); + double df = distance_front + Geom::bezier_length(*fwdp, fwdp->_front, fwd->_back, *fwd); + double db = distance_back + Geom::bezier_length(*revn, revn->_back, rev->_front, *rev); if (df > db) { last_fwd = fwd; last_distance_front = df; |
