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 | |
| 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')
| -rw-r--r-- | src/ui/tool/control-point-selection.cpp | 34 | ||||
| -rw-r--r-- | src/ui/tool/control-point-selection.h | 14 | ||||
| -rw-r--r-- | src/ui/tool/control-point.cpp | 3 | ||||
| -rw-r--r-- | src/ui/tool/control-point.h | 4 | ||||
| -rw-r--r-- | src/ui/tool/manipulator.h | 1 | ||||
| -rw-r--r-- | src/ui/tool/multi-path-manipulator.cpp | 81 | ||||
| -rw-r--r-- | src/ui/tool/multi-path-manipulator.h | 2 | ||||
| -rw-r--r-- | src/ui/tool/node-tool.cpp | 6 | ||||
| -rw-r--r-- | src/ui/tool/node-tool.h | 10 | ||||
| -rw-r--r-- | src/ui/tool/node.cpp | 121 | ||||
| -rw-r--r-- | src/ui/tool/node.h | 4 | ||||
| -rw-r--r-- | src/ui/tool/path-manipulator.cpp | 119 | ||||
| -rw-r--r-- | src/ui/tool/path-manipulator.h | 18 | ||||
| -rw-r--r-- | src/ui/tool/selector.h | 1 | ||||
| -rw-r--r-- | src/ui/tool/shape-record.h | 4 | ||||
| -rw-r--r-- | src/ui/tool/transform-handle-set.cpp | 113 | ||||
| -rw-r--r-- | src/ui/tool/transform-handle-set.h | 7 |
17 files changed, 363 insertions, 179 deletions
diff --git a/src/ui/tool/control-point-selection.cpp b/src/ui/tool/control-point-selection.cpp index 91e0bc2c2..1fb98d78f 100644 --- a/src/ui/tool/control-point-selection.cpp +++ b/src/ui/tool/control-point-selection.cpp @@ -166,7 +166,7 @@ void ControlPointSelection::spatialGrow(SelectableControlPoint *origin, int dir) } /** Transform all selected control points by the given affine transformation. */ -void ControlPointSelection::transform(Geom::Matrix const &m) +void ControlPointSelection::transform(Geom::Affine const &m) { for (iterator i = _points.begin(); i != _points.end(); ++i) { SelectableControlPoint *cur = *i; @@ -273,7 +273,7 @@ void ControlPointSelection::_pointGrabbed(SelectableControlPoint *point) _grabbed_point = point; _farthest_point = point; double maxdist = 0; - Geom::Matrix m; + Geom::Affine m; m.setIdentity(); for (iterator i = _points.begin(); i != _points.end(); ++i) { _original_positions.insert(std::make_pair(*i, (*i)->position())); @@ -290,11 +290,11 @@ void ControlPointSelection::_pointDragged(Geom::Point &new_pos, GdkEventMotion * { Geom::Point abs_delta = new_pos - _original_positions[_grabbed_point]; double fdist = Geom::distance(_original_positions[_grabbed_point], _original_positions[_farthest_point]); - if (held_alt(*event) && fdist > 0) { + if (held_only_alt(*event) && fdist > 0) { // Sculpting for (iterator i = _points.begin(); i != _points.end(); ++i) { SelectableControlPoint *cur = (*i); - Geom::Matrix trans; + Geom::Affine trans; trans.setIdentity(); double dist = Geom::distance(_original_positions[cur], _original_positions[_grabbed_point]); double deltafrac = 0.5 + 0.5 * cos(M_PI * dist/fdist); @@ -323,7 +323,7 @@ void ControlPointSelection::_pointDragged(Geom::Point &new_pos, GdkEventMotion * Geom::Point newdx = (newpx - newp) / Geom::EPSILON; Geom::Point newdy = (newpy - newp) / Geom::EPSILON; - Geom::Matrix itrans(newdx[Geom::X], newdx[Geom::Y], newdy[Geom::X], newdy[Geom::Y], 0, 0); + Geom::Affine itrans(newdx[Geom::X], newdx[Geom::Y], newdy[Geom::X], newdy[Geom::Y], 0, 0); if (itrans.isSingular()) itrans.setIdentity(); @@ -502,7 +502,7 @@ bool ControlPointSelection::_keyboardRotate(GdkEventKey const &event, int dir) } // translate to origin, rotate, translate back to original position - Geom::Matrix m = Geom::Translate(-rc) + Geom::Affine m = Geom::Translate(-rc) * Geom::Rotate(angle) * Geom::Translate(rc); transform(m); signal_commit.emit(COMMIT_KEYBOARD_ROTATE); @@ -538,7 +538,7 @@ bool ControlPointSelection::_keyboardScale(GdkEventKey const &event, int dir) } double scale = (maxext + length_change) / maxext; - Geom::Matrix m = Geom::Translate(-center) * Geom::Scale(scale) * Geom::Translate(center); + Geom::Affine m = Geom::Translate(-center) * Geom::Scale(scale) * Geom::Translate(center); transform(m); signal_commit.emit(COMMIT_KEYBOARD_SCALE_UNIFORM); return true; @@ -559,7 +559,7 @@ bool ControlPointSelection::_keyboardFlip(Geom::Dim2 d) dynamic_cast<SelectableControlPoint*>(ControlPoint::mouseovered_point); Geom::Point center = scp ? scp->position() : _handles->rotationCenter().position(); - Geom::Matrix m = Geom::Translate(-center) * scale_transform * Geom::Translate(center); + Geom::Affine m = Geom::Translate(-center) * scale_transform * Geom::Translate(center); transform(m); signal_commit.emit(d == Geom::X ? COMMIT_FLIP_X : COMMIT_FLIP_Y); return true; @@ -642,6 +642,24 @@ bool ControlPointSelection::event(GdkEvent *event) return false; } +std::vector<Inkscape::SnapCandidatePoint> ControlPointSelection::getOriginalPoints() +{ + std::vector<Inkscape::SnapCandidatePoint> 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 3aed6ae93..7e09d50f5 100644 --- a/src/ui/tool/control-point-selection.h +++ b/src/ui/tool/control-point-selection.h @@ -14,17 +14,19 @@ #include <memory> #include <boost/optional.hpp> +#include <stddef.h> #include <sigc++/sigc++.h> #include <2geom/forward.h> #include <2geom/point.h> #include <2geom/rect.h> -#include "display/display-forward.h" #include "util/accumulators.h" #include "util/unordered-containers.h" #include "ui/tool/commit-events.h" #include "ui/tool/manipulator.h" +#include "snap-candidate.h" class SPDesktop; +struct SPCanvasGroup; namespace Inkscape { namespace UI { @@ -90,7 +92,7 @@ public: virtual bool event(GdkEvent *); - void transform(Geom::Matrix const &m); + void transform(Geom::Affine const &m); void align(Geom::Dim2 d); void distribute(Geom::Dim2 d); @@ -108,6 +110,10 @@ public: sigc::signal<void> signal_update; sigc::signal<void, SelectableControlPoint *, bool> signal_point_changed; sigc::signal<void, CommitEvent> signal_commit; + + std::vector<Inkscape::SnapCandidatePoint> getOriginalPoints(); + void setOriginalPoints(); + private: // The functions below are invoked from SelectableControlPoint. // Previously they were connected to handlers when selecting, but this @@ -125,14 +131,14 @@ private: bool _keyboardRotate(GdkEventKey const &, int); bool _keyboardScale(GdkEventKey const &, int); bool _keyboardFlip(Geom::Dim2); - void _keyboardTransform(Geom::Matrix const &); + void _keyboardTransform(Geom::Affine const &); void _commitHandlesTransform(CommitEvent ce); double _rotationRadius(Geom::Point const &); set_type _points; set_type _all_points; INK_UNORDERED_MAP<SelectableControlPoint *, Geom::Point> _original_positions; - INK_UNORDERED_MAP<SelectableControlPoint *, Geom::Matrix> _last_trans; + INK_UNORDERED_MAP<SelectableControlPoint *, Geom::Affine> _last_trans; boost::optional<double> _rot_radius; boost::optional<double> _mouseover_rot_radius; Geom::OptRect _bounds; diff --git a/src/ui/tool/control-point.cpp b/src/ui/tool/control-point.cpp index d5e5b7dfe..bece1324b 100644 --- a/src/ui/tool/control-point.cpp +++ b/src/ui/tool/control-point.cpp @@ -14,6 +14,7 @@ #include <2geom/point.h> #include "desktop.h" #include "desktop-handles.h" +#include "display/sp-canvas.h" #include "display/snap-indicator.h" #include "event-context.h" #include "message-context.h" @@ -213,7 +214,7 @@ void ControlPoint::move(Geom::Point const &pos) /** Apply an arbitrary affine transformation to a control point. This is used * by ControlPointSelection, and is important for things like nodes with handles. * The default implementation simply moves the point according to the transform. */ -void ControlPoint::transform(Geom::Matrix const &m) { +void ControlPoint::transform(Geom::Affine const &m) { move(position() * m); } diff --git a/src/ui/tool/control-point.h b/src/ui/tool/control-point.h index 4de5e5847..9f62fca42 100644 --- a/src/ui/tool/control-point.h +++ b/src/ui/tool/control-point.h @@ -12,12 +12,12 @@ #define SEEN_UI_TOOL_CONTROL_POINT_H #include <boost/utility.hpp> +#include <stddef.h> #include <sigc++/sigc++.h> #include <gdkmm.h> #include <gtkmm.h> #include <2geom/point.h> -#include "display/display-forward.h" #include "forward.h" #include "util/accumulators.h" #include "display/sodipodi-ctrl.h" @@ -57,7 +57,7 @@ public: operator Geom::Point const &() { return _position; } virtual void move(Geom::Point const &pos); virtual void setPosition(Geom::Point const &pos); - virtual void transform(Geom::Matrix const &m); + virtual void transform(Geom::Affine const &m); /// @} /// @name Toggle the point's visibility diff --git a/src/ui/tool/manipulator.h b/src/ui/tool/manipulator.h index fd24e7b61..6866ec9dd 100644 --- a/src/ui/tool/manipulator.h +++ b/src/ui/tool/manipulator.h @@ -13,6 +13,7 @@ #include <set> #include <map> +#include <stddef.h> #include <sigc++/sigc++.h> #include <glib.h> #include <gdk/gdk.h> diff --git a/src/ui/tool/multi-path-manipulator.cpp b/src/ui/tool/multi-path-manipulator.cpp index ef1c764bb..082ac194b 100644 --- a/src/ui/tool/multi-path-manipulator.cpp +++ b/src/ui/tool/multi-path-manipulator.cpp @@ -210,7 +210,78 @@ void MultiPathManipulator::selectSubpaths() void MultiPathManipulator::shiftSelection(int dir) { - invokeForAll(&PathManipulator::shiftSelection, dir); + if (empty()) return; + + // 1. find last selected node + // 2. select the next node; if the last node or nothing is selected, + // select first node + MapType::iterator last_i; + SubpathList::iterator last_j; + NodeList::iterator last_k; + bool anything_found = false; + + for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) { + SubpathList &sp = i->second->subpathList(); + for (SubpathList::iterator j = sp.begin(); j != sp.end(); ++j) { + for (NodeList::iterator k = (*j)->begin(); k != (*j)->end(); ++k) { + if (k->selected()) { + last_i = i; + last_j = j; + last_k = k; + anything_found = true; + // when tabbing backwards, we want the first node + if (dir == -1) goto exit_loop; + } + } + } + } + exit_loop: + + // NOTE: we should not assume the _selection contains only nodes + // in future it might also contain handles and other types of control points + // this is why we use a flag instead in the loop above, instead of calling + // selection.empty() + if (!anything_found) { + // select first / last node + // this should never fail because there must be at least 1 non-empty manipulator + if (dir == 1) { + _selection.insert((*_mmap.begin()->second->subpathList().begin())->begin().ptr()); + } else { + _selection.insert((--(*--(--_mmap.end())->second->subpathList().end())->end()).ptr()); + } + return; + } + + // three levels deep - w00t! + if (dir == 1) { + if (++last_k == (*last_j)->end()) { + // here, last_k points to the node to be selected + ++last_j; + if (last_j == last_i->second->subpathList().end()) { + ++last_i; + if (last_i == _mmap.end()) { + last_i = _mmap.begin(); + } + last_j = last_i->second->subpathList().begin(); + } + last_k = (*last_j)->begin(); + } + } else { + if (!last_k || last_k == (*last_j)->begin()) { + if (last_j == last_i->second->subpathList().begin()) { + if (last_i == _mmap.begin()) { + last_i = _mmap.end(); + } + --last_i; + last_j = last_i->second->subpathList().end(); + } + --last_j; + last_k = (*last_j)->end(); + } + --last_k; + } + _selection.clear(); + _selection.insert(last_k.ptr()); } void MultiPathManipulator::invertSelectionInSubpaths() @@ -695,6 +766,14 @@ void MultiPathManipulator::_commit(CommitEvent cps) reason = _("Scale nodes vertically"); key = "node:scale:y"; break; + case COMMIT_MOUSE_SKEW_X: + reason = _("Skew nodes horizontally"); + key = "node:skew:x"; + break; + case COMMIT_MOUSE_SKEW_Y: + reason = _("Skew nodes vertically"); + key = "node:skew:y"; + break; case COMMIT_FLIP_X: reason = _("Flip nodes horizontally"); break; diff --git a/src/ui/tool/multi-path-manipulator.h b/src/ui/tool/multi-path-manipulator.h index ddb74c1bc..c25719790 100644 --- a/src/ui/tool/multi-path-manipulator.h +++ b/src/ui/tool/multi-path-manipulator.h @@ -11,8 +11,8 @@ #ifndef SEEN_UI_TOOL_MULTI_PATH_MANIPULATOR_H #define SEEN_UI_TOOL_MULTI_PATH_MANIPULATOR_H +#include <stddef.h> #include <sigc++/connection.h> -#include "display/display-forward.h" #include "forward.h" #include "ui/tool/commit-events.h" #include "ui/tool/manipulator.h" diff --git a/src/ui/tool/node-tool.cpp b/src/ui/tool/node-tool.cpp index 8661e7946..f83f8c473 100644 --- a/src/ui/tool/node-tool.cpp +++ b/src/ui/tool/node-tool.cpp @@ -13,6 +13,7 @@ #include <glib/gi18n.h> #include "desktop.h" #include "desktop-handles.h" +#include "display/sp-canvas-group.h" #include "display/canvas-bpath.h" #include "display/curve.h" #include "display/sp-canvas.h" @@ -479,10 +480,11 @@ gint ink_node_tool_root_handler(SPEventContext *event_context, GdkEvent *event) nt->flash_tempitem = NULL; nt->flashed_item = NULL; } - if (!SP_IS_PATH(over_item)) break; // for now, handle only paths + if (!SP_IS_SHAPE(over_item)) break; // for now, handle only shapes nt->flashed_item = over_item; - SPCurve *c = sp_path_get_curve_for_edit(SP_PATH(over_item)); + SPCurve *c = SP_SHAPE(over_item)->getCurveBeforeLPE(); + if (!c) break; // break out when curve doesn't exist c->transform(over_item->i2d_affine()); SPCanvasItem *flash = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), c); sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(flash), diff --git a/src/ui/tool/node-tool.h b/src/ui/tool/node-tool.h index 4d38e69e2..d005a0bdf 100644 --- a/src/ui/tool/node-tool.h +++ b/src/ui/tool/node-tool.h @@ -14,10 +14,10 @@ #include <memory> #include <boost/ptr_container/ptr_map.hpp> #include <glib.h> +#include <stddef.h> #include <sigc++/sigc++.h> #include "event-context.h" #include "forward.h" -#include "display/display-forward.h" #include "ui/tool/node-types.h" #define INK_TYPE_NODE_TOOL (ink_node_tool_get_type ()) @@ -30,13 +30,17 @@ class InkNodeTool; class InkNodeToolClass; namespace Inkscape { + +namespace Display { +class TemporaryItem; +} // namespace Display namespace UI { class MultiPathManipulator; class ControlPointSelection; class Selector; struct PathSharedData; -} -} +} // namespace UI +} // namespace Inkscape typedef std::auto_ptr<Inkscape::UI::MultiPathManipulator> MultiPathPtr; typedef std::auto_ptr<Inkscape::UI::ControlPointSelection> CSelPtr; 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; diff --git a/src/ui/tool/node.h b/src/ui/tool/node.h index 0194f5053..b7145790b 100644 --- a/src/ui/tool/node.h +++ b/src/ui/tool/node.h @@ -15,6 +15,7 @@ #include <iterator> #include <iosfwd> #include <stdexcept> +#include <cstddef> #include <tr1/functional> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> @@ -102,6 +103,7 @@ public: protected: Handle(NodeSharedData const &data, Geom::Point const &initial_pos, Node *parent); + virtual bool _eventHandler(GdkEvent *event); virtual void dragged(Geom::Point &, GdkEventMotion *); virtual bool grabbed(GdkEventMotion *); virtual void ungrabbed(GdkEventButton *); @@ -127,7 +129,7 @@ class Node : ListNode, public SelectableControlPoint { public: Node(NodeSharedData const &data, Geom::Point const &pos); virtual void move(Geom::Point const &p); - virtual void transform(Geom::Matrix const &m); + virtual void transform(Geom::Affine const &m); virtual Geom::Rect bounds(); NodeType type() { return _type; } diff --git a/src/ui/tool/path-manipulator.cpp b/src/ui/tool/path-manipulator.cpp index 5ae9c4137..7c2013872 100644 --- a/src/ui/tool/path-manipulator.cpp +++ b/src/ui/tool/path-manipulator.cpp @@ -102,14 +102,14 @@ private: void build_segment(Geom::PathBuilder &, Node *, Node *); PathManipulator::PathManipulator(MultiPathManipulator &mpm, SPPath *path, - Geom::Matrix const &et, guint32 outline_color, Glib::ustring lpe_key) + Geom::Affine const &et, guint32 outline_color, Glib::ustring lpe_key) : PointManipulator(mpm._path_data.node_data.desktop, *mpm._path_data.node_data.selection) , _subpaths(*this) , _multi_path_manipulator(mpm) , _path(path) , _spcurve(new SPCurve()) , _dragpoint(new CurveDragPoint(*this)) - , /* XML Tree being used here directly while it shouldn't be*/_observer(new PathManipulatorObserver(this, SP_OBJECT(path)->getRepr())) + , /* XML Tree being used here directly while it shouldn't be*/_observer(new PathManipulatorObserver(this, path->getRepr())) , _edit_transform(et) , _num_selected(0) , _show_handles(true) @@ -191,7 +191,7 @@ void PathManipulator::writeXML() if (!_path) return; _observer->block(); if (!empty()) { - SP_OBJECT(_path)->updateRepr(); + _path->updateRepr(); _getXMLNode()->setAttribute(_nodetypesKey().data(), _createTypeString().data()); } else { // this manipulator will have to be destroyed right after this call @@ -215,7 +215,7 @@ void PathManipulator::clear() /** Select all nodes in subpaths that have something selected. */ void PathManipulator::selectSubpaths() { - for (std::list<SubpathPtr>::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { + for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { NodeList::iterator sp_start = (*i)->begin(), sp_end = (*i)->end(); for (NodeList::iterator j = sp_start; j != sp_end; ++j) { if (j->selected()) { @@ -229,63 +229,6 @@ void PathManipulator::selectSubpaths() } } -/** Move the selection forward or backward by one node in each subpath, based on the sign - * of the parameter. */ -void PathManipulator::shiftSelection(int dir) -{ - if (dir == 0) return; - if (_num_selected == 0) { - // select the first node of the path. - SubpathList::iterator s = _subpaths.begin(); - if (s == _subpaths.end()) return; - NodeList::iterator n = (*s)->begin(); - if (n != (*s)->end()) - _selection.insert(n.ptr()); - return; - } - // We cannot do any tricks here, like iterating in different directions based on - // the sign and only setting the selection of nodes behind us, because it would break - // for closed paths. - for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { - std::deque<bool> sels; // I hope this is specialized for bools! - unsigned num = 0; - - for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) { - sels.push_back(j->selected()); - _selection.erase(j.ptr()); - ++num; - } - if (num == 0) continue; // should never happen! zero-node subpaths are not allowed - - num = 0; - // In closed subpath, shift the selection cyclically. In an open one, - // let the selection 'slide into nothing' at ends. - if (dir > 0) { - if ((*i)->closed()) { - bool last = sels.back(); - sels.pop_back(); - sels.push_front(last); - } else { - sels.push_front(false); - } - } else { - if ((*i)->closed()) { - bool first = sels.front(); - sels.pop_front(); - sels.push_back(first); - } else { - sels.push_back(false); - num = 1; - } - } - - for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) { - if (sels[num]) _selection.insert(j.ptr()); - ++num; - } - } -} - /** Invert selection in the selected subpaths. */ void PathManipulator::invertSelectionInSubpaths() { @@ -332,22 +275,31 @@ void PathManipulator::duplicateNodes() NodeList::iterator k = j.next(); Node *n = new Node(_multi_path_manipulator._path_data.node_data, *j); - // Move the new node to the bottom of the Z-order. This way you can drag all - // nodes that were selected before this operation without deselecting - // everything because there is a new node above. - n->sink(); + if (k) { + // Move the new node to the bottom of the Z-order. This way you can drag all + // nodes that were selected before this operation without deselecting + // everything because there is a new node above. + n->sink(); + } n->front()->setPosition(*j->front()); j->front()->retract(); j->setType(NODE_CUSP, false); (*i)->insert(k, n); - // We need to manually call the selection change callback to refresh - // the handle display correctly. - // This call changes num_selected, but we call this once for a selected node - // and once for an unselected node, so in the end the number stays correct. - _selectionChanged(j.ptr(), true); - _selectionChanged(n, false); + if (k) { + // We need to manually call the selection change callback to refresh + // the handle display correctly. + // This call changes num_selected, but we call this once for a selected node + // and once for an unselected node, so in the end the number stays correct. + _selectionChanged(j.ptr(), true); + _selectionChanged(n, false); + } else { + // select the new end node instead of the node just before it + _selection.erase(j.ptr()); + _selection.insert(n); + break; // this was the end node, nothing more to do + } } } } @@ -895,9 +847,9 @@ void PathManipulator::setLiveObjects(bool set) _live_objects = set; } -void PathManipulator::setControlsTransform(Geom::Matrix const &tnew) +void PathManipulator::setControlsTransform(Geom::Affine const &tnew) { - Geom::Matrix delta = _i2d_transform.inverse() * _edit_transform.inverse() * tnew * _i2d_transform; + Geom::Affine delta = _i2d_transform.inverse() * _edit_transform.inverse() * tnew * _i2d_transform; _edit_transform = tnew; for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) { @@ -1023,7 +975,7 @@ void PathManipulator::_externalChange(unsigned type) _updateOutline(); } break; case PATH_CHANGE_TRANSFORM: { - Geom::Matrix i2d_change = _d2i_transform; + Geom::Affine i2d_change = _d2i_transform; _i2d_transform = SP_ITEM(_path)->i2d_affine(); _d2i_transform = _i2d_transform.inverse(); i2d_change *= _i2d_transform; @@ -1087,13 +1039,14 @@ void PathManipulator::_createControlPointsFromGeometry() subpath->push_back(current_node); } // if this is a bezier segment, move handles appropriately - if (Geom::CubicBezier const *cubic_bezier = - dynamic_cast<Geom::CubicBezier const*>(&*cit)) + // TODO: I don't know why the dynamic cast below doesn't want to work + // when I replace BezierCurve with CubicBezier. Might be a bug + // somewhere in pathv_to_linear_and_cubic_beziers + Geom::BezierCurve const *bezier = dynamic_cast<Geom::BezierCurve const*>(&*cit); + if (bezier && bezier->order() == 3) { - std::vector<Geom::Point> points = cubic_bezier->points(); - - previous_node->front()->setPosition(points[1]); - current_node ->back() ->setPosition(points[2]); + previous_node->front()->setPosition((*bezier)[1]); + current_node ->back() ->setPosition((*bezier)[2]); } previous_node = current_node; } @@ -1436,7 +1389,7 @@ void PathManipulator::_commit(Glib::ustring const &annotation, gchar const *key) * point of the path. */ void PathManipulator::_updateDragPoint(Geom::Point const &evp) { - Geom::Matrix to_desktop = _edit_transform * _i2d_transform; + Geom::Affine to_desktop = _edit_transform * _i2d_transform; Geom::PathVector pv = _spcurve->get_pathvector(); boost::optional<Geom::PathVectorPosition> pvp = Geom::nearestPoint(pv, _desktop->w2d(evp) * to_desktop.inverse()); @@ -1477,8 +1430,8 @@ double PathManipulator::_getStrokeTolerance() * drag tolerance setting. */ Inkscape::Preferences *prefs = Inkscape::Preferences::get(); double ret = prefs->getIntLimited("/options/dragtolerance/value", 2, 0, 100); - if (_path && SP_OBJECT_STYLE(_path) && !SP_OBJECT_STYLE(_path)->stroke.isNone()) { - ret += SP_OBJECT_STYLE(_path)->stroke_width.computed * 0.5 + if (_path && _path->style && !_path->style->stroke.isNone()) { + ret += _path->style->stroke_width.computed * 0.5 * (_edit_transform * _i2d_transform).descrim() // scale to desktop coords * _desktop->current_zoom(); // == _d2w.descrim() - scale to window coords } diff --git a/src/ui/tool/path-manipulator.h b/src/ui/tool/path-manipulator.h index 87b88fc77..27a83f06b 100644 --- a/src/ui/tool/path-manipulator.h +++ b/src/ui/tool/path-manipulator.h @@ -14,15 +14,15 @@ #include <string> #include <memory> #include <2geom/pathvector.h> -#include <2geom/matrix.h> +#include <2geom/affine.h> #include <boost/shared_ptr.hpp> #include <boost/weak_ptr.hpp> -#include "display/display-forward.h" #include "forward.h" #include "ui/tool/node.h" #include "ui/tool/manipulator.h" struct SPCanvasItem; +struct SPCurve; namespace Inkscape { namespace XML { class Node; } @@ -53,7 +53,7 @@ class PathManipulator : public PointManipulator { public: typedef SPPath *ItemType; - PathManipulator(MultiPathManipulator &mpm, SPPath *path, Geom::Matrix const &edit_trans, + PathManipulator(MultiPathManipulator &mpm, SPPath *path, Geom::Affine const &edit_trans, guint32 outline_color, Glib::ustring lpe_key); ~PathManipulator(); virtual bool event(GdkEvent *); @@ -65,7 +65,6 @@ public: SPPath *item() { return _path; } void selectSubpaths(); - void shiftSelection(int dir); void invertSelectionInSubpaths(); void insertNodes(); @@ -86,7 +85,7 @@ public: void showPathDirection(bool show); void setLiveOutline(bool set); void setLiveObjects(bool set); - void setControlsTransform(Geom::Matrix const &); + void setControlsTransform(Geom::Affine const &); void hideDragPoint(); MultiPathManipulator &mpm() { return _multi_path_manipulator; } @@ -94,6 +93,9 @@ public: NodeList::iterator extremeNode(NodeList::iterator origin, bool search_selected, bool search_unselected, bool closest); + // this is necessary for Tab-selection in MultiPathManipulator + SubpathList &subpathList() { return _subpaths; } + static bool is_item_type(void *item); private: typedef NodeList Subpath; @@ -132,9 +134,9 @@ private: SPCanvasItem *_outline; CurveDragPoint *_dragpoint; // an invisible control point hoverng over curve PathManipulatorObserver *_observer; - Geom::Matrix _d2i_transform; ///< desktop-to-item transform - Geom::Matrix _i2d_transform; ///< item-to-desktop transform, inverse of _d2i_transform - Geom::Matrix _edit_transform; ///< additional transform to apply to editing controls + Geom::Affine _d2i_transform; ///< desktop-to-item transform + Geom::Affine _i2d_transform; ///< item-to-desktop transform, inverse of _d2i_transform + Geom::Affine _edit_transform; ///< additional transform to apply to editing controls unsigned _num_selected; ///< number of selected nodes bool _show_handles; bool _show_outline; diff --git a/src/ui/tool/selector.h b/src/ui/tool/selector.h index e61668d9e..05d72c208 100644 --- a/src/ui/tool/selector.h +++ b/src/ui/tool/selector.h @@ -14,7 +14,6 @@ #include <memory> #include <gdk/gdk.h> #include <2geom/rect.h> -#include "display/display-forward.h" #include "ui/tool/manipulator.h" class SPDesktop; diff --git a/src/ui/tool/shape-record.h b/src/ui/tool/shape-record.h index 893231404..646e6f4f6 100644 --- a/src/ui/tool/shape-record.h +++ b/src/ui/tool/shape-record.h @@ -14,7 +14,7 @@ #include <glibmm/ustring.h> #include <boost/operators.hpp> -#include <2geom/matrix.h> +#include <2geom/affine.h> class SPItem; namespace Inkscape { @@ -32,7 +32,7 @@ struct ShapeRecord : public boost::totally_ordered<ShapeRecord> { SPItem *item; // SP node for the edited shape - Geom::Matrix edit_transform; // how to transform controls - used for clipping paths and masks + Geom::Affine edit_transform; // how to transform controls - used for clipping paths and masks ShapeRole role; Glib::ustring lpe_key; // name of LPE shape param being edited diff --git a/src/ui/tool/transform-handle-set.cpp b/src/ui/tool/transform-handle-set.cpp index ef93a3767..26263c26b 100644 --- a/src/ui/tool/transform-handle-set.cpp +++ b/src/ui/tool/transform-handle-set.cpp @@ -18,10 +18,16 @@ #include "desktop-handles.h" #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 @@ -88,12 +94,14 @@ public: protected: virtual void startTransform() {} virtual void endTransform() {} - virtual Geom::Matrix computeTransform(Geom::Point const &pos, GdkEventMotion *event) = 0; + virtual Geom::Affine computeTransform(Geom::Point const &pos, GdkEventMotion *event) = 0; virtual CommitEvent getCommitEvent() = 0; - Geom::Matrix _last_transform; + Geom::Affine _last_transform; Geom::Point _origin; TransformHandleSet &_th; + std::vector<Inkscape::SnapCandidatePoint> _snap_points; + private: virtual bool grabbed(GdkEventMotion *) { _origin = position(); @@ -103,19 +111,34 @@ 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) { - Geom::Matrix t = computeTransform(new_pos, event); + Geom::Affine t = computeTransform(new_pos, event); // protect against degeneracies if (t.isSingular()) return; - Geom::Matrix incr = _last_transform.inverse() * t; + Geom::Affine incr = _last_transform.inverse() * t; if (incr.isSingular()) return; _th.signal_transform.emit(incr); _last_transform = t; } virtual void ungrabbed(GdkEventButton *) { + _snap_points.clear(); _th._clearActiveHandle(); _cset = &thandle_cset; _setState(_state); @@ -175,26 +198,66 @@ 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(); + selection->setOriginalPoints(); } - virtual Geom::Matrix computeTransform(Geom::Point const &new_pos, GdkEventMotion *event) { + virtual Geom::Affine 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<Inkscape::SnapCandidatePoint> unselected; + typedef ControlPointSelection::Set Set; + Set &nodes = _parent->_selection.allPoints(); + for (Set::iterator i = nodes.begin(); i != nodes.end(); ++i) { + Node *n = static_cast<Node*>(*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) + Geom::Affine t = Geom::Translate(-scc) * Geom::Scale(scale[0], scale[1]) * Geom::Translate(scc); return t; @@ -231,7 +294,7 @@ protected: _sc_opposite = Geom::middle_point(b.corner(_side + 2), b.corner(_side + 3)); _last_scale_x = _last_scale_y = 1.0; } - virtual Geom::Matrix computeTransform(Geom::Point const &new_pos, GdkEventMotion *event) { + virtual Geom::Affine computeTransform(Geom::Point const &new_pos, GdkEventMotion *event) { Geom::Point scc = held_shift(*event) ? _sc_center : _sc_opposite; Geom::Point vs; Geom::Dim2 d1 = static_cast<Geom::Dim2>((_side + 1) % 2); @@ -250,7 +313,7 @@ protected: _last_scale_x = vs[Geom::X]; _last_scale_y = vs[Geom::Y]; - Geom::Matrix t = Geom::Translate(-scc) + Geom::Affine t = Geom::Translate(-scc) * Geom::Scale(vs) * Geom::Translate(scc); return t; @@ -288,7 +351,7 @@ protected: _last_angle = 0; } - virtual Geom::Matrix computeTransform(Geom::Point const &new_pos, GdkEventMotion *event) + virtual Geom::Affine computeTransform(Geom::Point const &new_pos, GdkEventMotion *event) { Geom::Point rotc = held_shift(*event) ? _rot_opposite : _rot_center; double angle = Geom::angle_between(_origin - rotc, new_pos - rotc); @@ -296,7 +359,7 @@ protected: angle = snap_angle(angle); } _last_angle = angle; - Geom::Matrix t = Geom::Translate(-rotc) + Geom::Affine t = Geom::Translate(-rotc) * Geom::Rotate(angle) * Geom::Translate(rotc); return t; @@ -362,7 +425,7 @@ protected: _last_horizontal = _side % 2; } - virtual Geom::Matrix computeTransform(Geom::Point const &new_pos, GdkEventMotion *event) + virtual Geom::Affine computeTransform(Geom::Point const &new_pos, GdkEventMotion *event) { Geom::Point scc = held_shift(*event) ? _skew_center : _skew_opposite; // d1 and d2 are reversed with respect to ScaleSideHandle @@ -395,12 +458,12 @@ protected: // skew matrix has the from [[1, k],[0, 1]] for horizontal skew // and [[1,0],[k,1]] for vertical skew. - Geom::Matrix skew = Geom::identity(); + Geom::Affine skew = Geom::identity(); // correct the sign of the tangent skew[d2 + 1] = (d1 == Geom::X ? -1.0 : 1.0) * tan(angle); _last_angle = angle; - Geom::Matrix t = Geom::Translate(-scc) + Geom::Affine t = Geom::Translate(-scc) * Geom::Scale(scale) * skew * Geom::Translate(scc); return t; @@ -473,7 +536,23 @@ public: } protected: - + virtual void dragged(Geom::Point &new_pos, GdkEventMotion *event) { + SnapManager &sm = _desktop->namedview->snap_manager; + sm.setup(_desktop); + bool snap = !held_shift(*event) && sm.someSnapperMightSnap(); + if (held_control(*event)) { + // constrain to axes + Geom::Point origin = _last_drag_origin(); + std::vector<Inkscape::Snapper::SnapConstraint> constraints; + constraints.push_back(Inkscape::Snapper::SnapConstraint(origin, Geom::Point(1, 0))); + constraints.push_back(Inkscape::Snapper::SnapConstraint(origin, Geom::Point(0, 1))); + new_pos = sm.multipleConstrainedSnaps(Inkscape::SnapCandidatePoint(new_pos, + SNAPSOURCE_ROTATION_CENTER), constraints, held_shift(*event)).getPoint(); + } else if (snap) { + sm.freeSnapReturnByRef(new_pos, SNAPSOURCE_ROTATION_CENTER); + } + sm.unSetup(); + } virtual Glib::ustring _getTip(unsigned /*state*/) { return C_("Transform handle tip", "<b>Rotation center</b>: drag to change the origin of transforms"); @@ -566,7 +645,7 @@ bool TransformHandleSet::event(GdkEvent*) return false; } -void TransformHandleSet::_emitTransform(Geom::Matrix const &t) +void TransformHandleSet::_emitTransform(Geom::Affine const &t) { signal_transform.emit(t); _center->transform(t); diff --git a/src/ui/tool/transform-handle-set.h b/src/ui/tool/transform-handle-set.h index 2a4df8751..0557b1278 100644 --- a/src/ui/tool/transform-handle-set.h +++ b/src/ui/tool/transform-handle-set.h @@ -14,12 +14,11 @@ #include <memory> #include <gdk/gdk.h> #include <2geom/forward.h> -#include "display/display-forward.h" #include "ui/tool/commit-events.h" #include "ui/tool/manipulator.h" class SPDesktop; -class CtrlRect; // this is not present in display-forward.h! +class CtrlRect; namespace Inkscape { namespace UI { @@ -51,10 +50,10 @@ public: bool transforming() { return _in_transform; } ControlPoint &rotationCenter(); - sigc::signal<void, Geom::Matrix const &> signal_transform; + sigc::signal<void, Geom::Affine const &> signal_transform; sigc::signal<void, CommitEvent> signal_commit; private: - void _emitTransform(Geom::Matrix const &); + void _emitTransform(Geom::Affine const &); void _setActiveHandle(ControlPoint *h); void _clearActiveHandle(); void _updateVisibility(bool v); |
