summaryrefslogtreecommitdiffstats
path: root/src/ui/tool/node.cpp
diff options
context:
space:
mode:
authorKrzysztof Kosi??ski <tweenk.pl@gmail.com>2011-04-07 23:42:04 +0000
committerKrzysztof KosiƄski <tweenk.pl@gmail.com>2011-04-07 23:42:04 +0000
commit945ce419c806c73d70203dec33ececafbe108a92 (patch)
treecfcdb59bf47e9db7f9e01f7eebb59924bdeaea94 /src/ui/tool/node.cpp
parentMerge from trunk (again) (diff)
parentExtensions. SVG+media fix (see Bug #400356). (diff)
downloadinkscape-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.cpp121
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;