diff options
Diffstat (limited to 'src/ui/tool/control-point-selection.cpp')
| -rw-r--r-- | src/ui/tool/control-point-selection.cpp | 132 |
1 files changed, 88 insertions, 44 deletions
diff --git a/src/ui/tool/control-point-selection.cpp b/src/ui/tool/control-point-selection.cpp index 638318dbf..df27c2a72 100644 --- a/src/ui/tool/control-point-selection.cpp +++ b/src/ui/tool/control-point-selection.cpp @@ -8,6 +8,7 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include <boost/none.hpp> #include <2geom/transforms.h> #include "desktop.h" #include "preferences.h" @@ -53,15 +54,13 @@ ControlPointSelection::ControlPointSelection(SPDesktop *d, SPCanvasGroup *th_gro signal_update.connect( sigc::bind( sigc::mem_fun(*this, &ControlPointSelection::_updateTransformHandles), true)); - signal_point_changed.connect( - sigc::hide( sigc::hide( - sigc::bind( - sigc::mem_fun(*this, &ControlPointSelection::_updateTransformHandles), - false)))); + ControlPoint::signal_mouseover_change.connect( + sigc::hide( + sigc::mem_fun(*this, &ControlPointSelection::_mouseoverChanged))); _handles->signal_transform.connect( sigc::mem_fun(*this, &ControlPointSelection::transform)); _handles->signal_commit.connect( - sigc::mem_fun(*this, &ControlPointSelection::_commitTransform)); + sigc::mem_fun(*this, &ControlPointSelection::_commitHandlesTransform)); } ControlPointSelection::~ControlPointSelection() @@ -81,8 +80,7 @@ std::pair<ControlPointSelection::iterator, bool> ControlPointSelection::insert(c found = _points.insert(x).first; x->updateState(); - _rot_radius.reset(); - signal_point_changed.emit(x, true); + _pointChanged(x, true); return std::pair<iterator, bool>(found, true); } @@ -93,8 +91,7 @@ void ControlPointSelection::erase(iterator pos) SelectableControlPoint *erased = *pos; _points.erase(pos); erased->updateState(); - _rot_radius.reset(); - signal_point_changed.emit(erased, false); + _pointChanged(erased, false); } ControlPointSelection::size_type ControlPointSelection::erase(const key_type &k) { @@ -175,8 +172,10 @@ void ControlPointSelection::transform(Geom::Matrix const &m) SelectableControlPoint *cur = *i; cur->transform(m); } + _updateBounds(); // TODO preserving the rotation radius needs some rethinking... if (_rot_radius) (*_rot_radius) *= m.descrim(); + if (_mouseover_rot_radius) (*_mouseover_rot_radius) *= m.descrim(); signal_update.emit(); } @@ -233,28 +232,12 @@ void ControlPointSelection::distribute(Geom::Dim2 d) * or nothing if the selection is empty */ Geom::OptRect ControlPointSelection::pointwiseBounds() { - Geom::OptRect bound; - for (iterator i = _points.begin(); i != _points.end(); ++i) { - SelectableControlPoint *cur = (*i); - Geom::Point p = cur->position(); - if (!bound) { - bound = Geom::Rect(p, p); - } else { - bound->expandTo(p); - } - } - return bound; + return _bounds; } Geom::OptRect ControlPointSelection::bounds() { - Geom::OptRect bound; - for (iterator i = _points.begin(); i != _points.end(); ++i) { - SelectableControlPoint *cur = (*i); - Geom::OptRect r = cur->bounds(); - bound.unionWith(r); - } - return bound; + return size() == 1 ? (*_points.begin())->bounds() : _bounds; } void ControlPointSelection::showTransformHandles(bool v, bool one_node) @@ -305,6 +288,7 @@ void ControlPointSelection::_pointUngrabbed() { _dragging = false; _grabbed_point = NULL; + _updateBounds(); restoreTransformHandles(); signal_commit.emit(COMMIT_MOUSE_MOVE); } @@ -320,13 +304,42 @@ bool ControlPointSelection::_pointClicked(SelectableControlPoint *p, GdkEventBut return false; } +void ControlPointSelection::_pointChanged(SelectableControlPoint *p, bool selected) +{ + _updateBounds(); + _updateTransformHandles(false); + if (_bounds) + _handles->rotationCenter().move(_bounds->midpoint()); + + signal_point_changed.emit(p, selected); +} + +void ControlPointSelection::_mouseoverChanged() +{ + _mouseover_rot_radius = boost::none; +} + +void ControlPointSelection::_updateBounds() +{ + _rot_radius = boost::none; + _bounds = Geom::OptRect(); + for (iterator i = _points.begin(); i != _points.end(); ++i) { + SelectableControlPoint *cur = (*i); + Geom::Point p = cur->position(); + if (!_bounds) { + _bounds = Geom::Rect(p, p); + } else { + _bounds->expandTo(p); + } + } +} + void ControlPointSelection::_updateTransformHandles(bool preserve_center) { if (_dragging) return; if (_handles_visible && size() > 1) { - Geom::OptRect b = pointwiseBounds(); - _handles->setBounds(*b, preserve_center); + _handles->setBounds(*bounds(), preserve_center); _handles->setVisible(true); } else if (_one_node_handles && size() == 1) { // only one control point in selection SelectableControlPoint *p = *begin(); @@ -365,6 +378,20 @@ bool ControlPointSelection::_keyboardMove(GdkEventKey const &event, Geom::Point return true; } +/** @brief Computes the distance to the farthest corner of the bounding box. + * Used to determine what it means to "rotate by one pixel". */ +double ControlPointSelection::_rotationRadius(Geom::Point const &rc) +{ + if (empty()) return 1.0; // some safe value + Geom::Rect b = *bounds(); + double maxlen = 0; + for (unsigned i = 0; i < 4; ++i) { + double len = Geom::distance(b.corner(i), rc); + if (len > maxlen) maxlen = len; + } + return maxlen; +} + /** Rotates the selected points in the given direction according to the modifier state * from the supplied event. * @param event Key event to take modifier state from @@ -374,15 +401,25 @@ bool ControlPointSelection::_keyboardRotate(GdkEventKey const &event, int dir) { if (empty()) return false; - Geom::Point rc = _handles->rotationCenter(); - if (!_rot_radius) { - Geom::Rect b = *(size() == 1 ? bounds() : pointwiseBounds()); - double maxlen = 0; - for (unsigned i = 0; i < 4; ++i) { - double len = (b.corner(i) - rc).length(); - if (len > maxlen) maxlen = len; + Geom::Point rc; + + // rotate around the mouseovered point, or the selection's rotation center + // if nothing is mouseovered + double radius; + SelectableControlPoint *scp = + dynamic_cast<SelectableControlPoint*>(ControlPoint::mouseovered_point); + if (scp) { + rc = scp->position(); + if (!_mouseover_rot_radius) { + _mouseover_rot_radius = _rotationRadius(rc); + } + radius = *_mouseover_rot_radius; + } else { + rc = _handles->rotationCenter(); + if (!_rot_radius) { + _rot_radius = _rotationRadius(rc); } - _rot_radius = maxlen; + radius = *_rot_radius; } double angle; @@ -390,7 +427,7 @@ bool ControlPointSelection::_keyboardRotate(GdkEventKey const &event, int dir) // Rotate by "one pixel". We interpret this as rotating by an angle that causes // the topmost point of a circle circumscribed about the selection's bounding box // to move on an arc 1 screen pixel long. - angle = atan2(1.0 / _desktop->current_zoom(), *_rot_radius) * dir; + angle = atan2(1.0 / _desktop->current_zoom(), radius) * dir; } else { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); int snaps = prefs->getIntLimited("/options/rotationsnapsperpi/value", 12, 1, 1000); @@ -410,11 +447,17 @@ bool ControlPointSelection::_keyboardScale(GdkEventKey const &event, int dir) { if (empty()) return false; - // TODO should the saved rotation center or the current center be used? - Geom::Rect bound = (size() == 1 ? *bounds() : *pointwiseBounds()); - double maxext = bound.maxExtent(); + double maxext = bounds()->maxExtent(); if (Geom::are_near(maxext, 0)) return false; - Geom::Point center = _handles->rotationCenter().position(); + + Geom::Point center; + SelectableControlPoint *scp = + dynamic_cast<SelectableControlPoint*>(ControlPoint::mouseovered_point); + if (scp) { + center = scp->position(); + } else { + center = _handles->rotationCenter().position(); + } double length_change; if (held_alt(event)) { @@ -455,8 +498,9 @@ bool ControlPointSelection::_keyboardFlip(Geom::Dim2 d) return true; } -void ControlPointSelection::_commitTransform(CommitEvent ce) +void ControlPointSelection::_commitHandlesTransform(CommitEvent ce) { + _updateBounds(); _updateTransformHandles(true); signal_commit.emit(ce); } |
