From cb04b2e8a56fe57f6f6945ab64a574c624a5546d Mon Sep 17 00:00:00 2001 From: =?utf8?q?Krzysztof=20Kosi=C5=84ski?= Date: Thu, 18 Mar 2010 03:18:56 +0100 Subject: [PATCH] Fix scaling of degenerate handles using keybard shortcuts. --- src/ui/tool/node.cpp | 93 +++++++++++++++++++++++--------- src/ui/tool/node.h | 8 ++- src/ui/tool/path-manipulator.cpp | 28 ++++++++-- 3 files changed, 98 insertions(+), 31 deletions(-) diff --git a/src/ui/tool/node.cpp b/src/ui/tool/node.cpp index c82b0c7d6..e9fa79fb3 100644 --- a/src/ui/tool/node.cpp +++ b/src/ui/tool/node.cpp @@ -106,22 +106,11 @@ void Handle::setVisible(bool v) void Handle::move(Geom::Point const &new_pos) { - Handle *other, *towards, *towards_second; - Node *node_towards; // node in direction of this handle - Node *node_away; // node in the opposite direction - if (this == &_parent->_front) { - other = &_parent->_back; - node_towards = _parent->_next(); - node_away = _parent->_prev(); - towards = node_towards ? &node_towards->_back : 0; - towards_second = node_towards ? &node_towards->_front : 0; - } else { - other = &_parent->_front; - node_towards = _parent->_prev(); - node_away = _parent->_next(); - towards = node_towards ? &node_towards->_front : 0; - towards_second = node_towards ? &node_towards->_back : 0; - } + Handle *other = this->other(); + Node *node_towards = _parent->nodeToward(this); // node in direction of this handle + Node *node_away = _parent->nodeAwayFrom(this); // node in the opposite direction + Handle *towards = node_towards ? node_towards->handleAwayFrom(_parent) : NULL; + 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 @@ -225,7 +214,7 @@ char const *Handle::handle_type_to_localized_string(NodeType type) bool Handle::grabbed(GdkEventMotion *) { - _saved_other_pos = other().position(); + _saved_other_pos = other()->position(); _saved_length = _drag_out ? 0 : length(); _pm()._handleGrabbed(); return false; @@ -294,10 +283,10 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event) if (held_shift(*event)) { Geom::Point other_relpos = _saved_other_pos - parent_pos; other_relpos *= Geom::Rotate(Geom::angle_between(origin - parent_pos, new_pos - parent_pos)); - other().setRelativePos(other_relpos); + other()->setRelativePos(other_relpos); } else { // restore the position - other().setPosition(_saved_other_pos); + other()->setPosition(_saved_other_pos); } } move(new_pos); // needed for correct update, even though it's redundant @@ -332,10 +321,10 @@ bool Handle::clicked(GdkEventButton *event) return true; } -Handle &Handle::other() +Handle *Handle::other() { - if (this == &_parent->_front) return _parent->_back; - return _parent->_front; + if (this == &_parent->_front) return &_parent->_back; + return &_parent->_front; } static double snap_increment_degrees() { @@ -347,7 +336,7 @@ static double snap_increment_degrees() { Glib::ustring Handle::_getTip(unsigned state) { char const *more; - bool can_shift_rotate = _parent->type() == NODE_CUSP && !other().isDegenerate(); + bool can_shift_rotate = _parent->type() == NODE_CUSP && !other()->isDegenerate(); if (can_shift_rotate) { more = C_("Path handle tip", "more: Shift, Ctrl, Alt"); } else { @@ -1091,6 +1080,58 @@ Inkscape::SnapTargetType Node::_snapTargetType() return SNAPTARGET_NODE_CUSP; } +/** @brief Gets the handle that faces the given adjacent node. + * Will abort with error if the given node is not adjacent. */ +Handle *Node::handleToward(Node *to) +{ + if (_next() == to) { + return front(); + } + if (_prev() == to) { + return back(); + } + g_error("Node::handleToward(): second node is not adjacent!"); +} + +/** @brief Gets the node in the direction of the given handle. + * Will abort with error if the handle doesn't belong to this node. */ +Node *Node::nodeToward(Handle *dir) +{ + if (front() == dir) { + return _next(); + } + if (back() == dir) { + return _prev(); + } + g_error("Node::nodeToward(): handle is not a child of this node!"); +} + +/** @brief Gets the handle that goes in the direction opposite to the given adjacent node. + * Will abort with error if the given node is not adjacent. */ +Handle *Node::handleAwayFrom(Node *to) +{ + if (_next() == to) { + return back(); + } + if (_prev() == to) { + return front(); + } + g_error("Node::handleAwayFrom(): second node is not adjacent!"); +} + +/** @brief Gets the node in the direction opposite to the given handle. + * Will abort with error if the handle doesn't belong to this node. */ +Node *Node::nodeAwayFrom(Handle *h) +{ + if (front() == h) { + return _prev(); + } + if (back() == h) { + return _next(); + } + g_error("Node::nodeAwayFrom(): handle is not a child of this node!"); +} + Glib::ustring Node::_getTip(unsigned state) { if (state_held_shift(state)) { @@ -1115,7 +1156,11 @@ Glib::ustring Node::_getTip(unsigned state) "Ctrl: move along axes, click to change node type"); } - // assemble tip from node name + if (state_held_alt(state)) { + return C_("Path node tip", "Alt: sculpt nodes"); + } + + // No modifiers: assemble tip from node type char const *nodetype = node_type_to_localized_string(_type); if (_selection.transformHandlesEnabled() && selected()) { if (_selection.size() == 1) { diff --git a/src/ui/tool/node.h b/src/ui/tool/node.h index d04b87976..af4cd7e3a 100644 --- a/src/ui/tool/node.h +++ b/src/ui/tool/node.h @@ -96,7 +96,7 @@ public: void setDirection(Geom::Point const &from, Geom::Point const &to); void setDirection(Geom::Point const &dir); Node *parent() { return _parent; } - Handle &other(); + Handle *other(); static char const *handle_type_to_localized_string(NodeType type); protected: @@ -138,10 +138,14 @@ public: bool isEndNode(); Handle *front() { return &_front; } Handle *back() { return &_back; } - static NodeType parse_nodetype(char x); + Handle *handleToward(Node *to); + Node *nodeToward(Handle *h); + Handle *handleAwayFrom(Node *to); + Node *nodeAwayFrom(Handle *h); NodeList &nodeList() { return *(static_cast(this)->ln_list); } void sink(); + static NodeType parse_nodetype(char x); static char const *node_type_to_localized_string(NodeType type); // temporarily public virtual bool _eventHandler(GdkEvent *event); diff --git a/src/ui/tool/path-manipulator.cpp b/src/ui/tool/path-manipulator.cpp index ebf0f3828..f6d5bde37 100644 --- a/src/ui/tool/path-manipulator.cpp +++ b/src/ui/tool/path-manipulator.cpp @@ -720,6 +720,7 @@ void PathManipulator::setSegmentType(SegmentType type) case SEGMENT_CUBIC_BEZIER: if (!j->front()->isDegenerate() || !k->back()->isDegenerate()) break; + // move both handles to 1/3 of the line j->front()->move(j->position() + (k->position() - j->position()) / 3); k->back()->move(k->position() + (j->position() - k->position()) / 3); break; @@ -744,9 +745,17 @@ void PathManipulator::scaleHandle(Node *n, int which, int dir, bool pixel) length_change *= dir; } - Geom::Point relpos = h->relativePos(); - double rellen = relpos.length(); - h->setRelativePos(relpos * ((rellen + length_change) / rellen)); + Geom::Point relpos; + if (h->isDegenerate()) { + Node *nh = n->nodeToward(h); + if (!nh) return; + relpos = Geom::unit_vector(nh->position() - n->position()) * length_change; + } else { + relpos = h->relativePos(); + double rellen = relpos.length(); + relpos *= ((rellen + length_change) / rellen); + } + h->setRelativePos(relpos); update(); gchar const *key = which < 0 ? "handle:scale:left" : "handle:scale:right"; @@ -759,8 +768,9 @@ void PathManipulator::rotateHandle(Node *n, int which, int dir, bool pixel) n->setType(NODE_CUSP); } Handle *h = _chooseHandle(n, which); - double angle; + if (h->isDegenerate()) return; + double angle; if (pixel) { // Rotate by "one pixel" angle = atan2(1.0 / _desktop->current_zoom(), h->length()) * dir; @@ -769,6 +779,7 @@ void PathManipulator::rotateHandle(Node *n, int which, int dir, bool pixel) int snaps = prefs->getIntLimited("/options/rotationsnapsperpi/value", 12, 1, 1000); angle = M_PI * dir / snaps; } + h->setRelativePos(h->relativePos() * Geom::Rotate(angle)); update(); gchar const *key = which < 0 ? "handle:rotate:left" : "handle:rotate:right"; @@ -777,7 +788,14 @@ void PathManipulator::rotateHandle(Node *n, int which, int dir, bool pixel) Handle *PathManipulator::_chooseHandle(Node *n, int which) { - Geom::Point f = n->front()->position(), b = n->back()->position(); + // Rationale for this choice: + // Imagine you have two handles pointing right, where one of them is only slighty higher + // than the other. Extending one of the handles could make its X coord larger than + // the second one, and keeping the shortcut pressed would result in two handles being + // extended alternately. This appears like extending both handles at once and is confusing. + // Using the unit vector avoids this problem and remains fairly intuitive. + Geom::Point f = Geom::unit_vector(n->front()->position()); + Geom::Point b = Geom::unit_vector(n->back()->position()); if (which < 0) { // pick left handle. // we just swap the handles and pick the right handle below. -- 2.30.2