From 31bb8269c26a781036448ed8f8cd93cc84fb2118 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Sun, 29 Nov 2009 16:33:18 +0100 Subject: First GSoC node tool commit to Bazaar (bzr r8846.1.1) --- src/ui/tool/control-point-selection.cpp | 530 ++++++++++++++++++++++++++++++++ 1 file changed, 530 insertions(+) create mode 100644 src/ui/tool/control-point-selection.cpp (limited to 'src/ui/tool/control-point-selection.cpp') diff --git a/src/ui/tool/control-point-selection.cpp b/src/ui/tool/control-point-selection.cpp new file mode 100644 index 000000000..d10045c62 --- /dev/null +++ b/src/ui/tool/control-point-selection.cpp @@ -0,0 +1,530 @@ +/** @file + * Node selection - implementation + */ +/* Authors: + * Krzysztof KosiƄski + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <2geom/transforms.h> +#include "desktop.h" +#include "preferences.h" +#include "ui/tool/control-point-selection.h" +#include "ui/tool/event-utils.h" +#include "ui/tool/selectable-control-point.h" +#include "ui/tool/transform-handle-set.h" + +namespace Inkscape { +namespace UI { + +/** + * @class ControlPointSelection + * @brief Group of selected control points. + * + * Some operations can be performed on all selected points regardless of their type, therefore + * this class is also a Manipulator. It handles the transformations of points using + * the keyboard. + * + * The exposed interface is similar to that of an STL set. Internally, a hash map is used. + * @todo Correct iterators (that don't expose the connection list) + */ + +/** @var ControlPointSelection::signal_update + * Fires when the display needs to be updated to reflect changes. + */ +/** @var ControlPointSelection::signal_point_changed + * Fires when a control point is added to or removed from the selection. + * The first param contains a pointer to the control point that changed sel. state. + * The second says whether the point is currently selected. + */ +/** @var ControlPointSelection::signal_commit + * Fires when a change that needs to be committed to XML happens. + */ + +ControlPointSelection::ControlPointSelection(SPDesktop *d, SPCanvasGroup *th_group) + : Manipulator(d) + , _handles(new TransformHandleSet(d, th_group)) + , _dragging(false) + , _handles_visible(true) + , _one_node_handles(false) + , _sculpt_enabled(false) + , _sculpting(false) +{ + 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)))); + _handles->signal_transform.connect( + sigc::mem_fun(*this, &ControlPointSelection::transform)); + _handles->signal_commit.connect( + sigc::mem_fun(*this, &ControlPointSelection::_commitTransform)); +} + +ControlPointSelection::~ControlPointSelection() +{ + clear(); + delete _handles; +} + +/** Add a control point to the selection. */ +std::pair ControlPointSelection::insert(const value_type &x) +{ + iterator found = _points.find(x); + if (found != _points.end()) { + return std::pair(found, false); + } + + boost::shared_ptr clist(new connlist_type()); + + // hide event param and always return false + clist->push_back( + x->signal_grabbed.connect( + sigc::bind_return( + sigc::bind<0>( + sigc::mem_fun(*this, &ControlPointSelection::_selectionGrabbed), + x), + false))); + clist->push_back( + x->signal_dragged.connect( + sigc::mem_fun(*this, &ControlPointSelection::_selectionDragged))); + // hide event parameter + clist->push_back( + x->signal_ungrabbed.connect( + sigc::hide( + sigc::mem_fun(*this, &ControlPointSelection::_selectionUngrabbed)))); + + found = _points.insert(std::make_pair(x, clist)).first; + + x->updateState(); + _rot_radius.reset(); + signal_point_changed.emit(x, true); + + return std::pair(found, true); +} + +/** Remove a point from the selection. */ +void ControlPointSelection::erase(iterator pos) +{ + SelectableControlPoint *erased = pos->first; + boost::shared_ptr clist = pos->second; + for (connlist_type::iterator i = clist->begin(); i != clist->end(); ++i) { + i->disconnect(); + } + _points.erase(pos); + erased->updateState(); + _rot_radius.reset(); + signal_point_changed.emit(erased, false); +} +ControlPointSelection::size_type ControlPointSelection::erase(const key_type &k) +{ + iterator pos = _points.find(k); + if (pos == _points.end()) return 0; + erase(pos); + return 1; +} +void ControlPointSelection::erase(iterator first, iterator last) +{ + while (first != last) erase(first++); +} + +/** Remove all points from the selection, making it empty. */ +void ControlPointSelection::clear() +{ + for (iterator i = begin(); i != end(); ) + erase(i++); +} + +/** Transform all selected control points by the supplied affine transformation. */ +void ControlPointSelection::transform(Geom::Matrix const &m) +{ + for (iterator i = _points.begin(); i != _points.end(); ++i) { + SelectableControlPoint *cur = i->first; + cur->transform(m); + } + // TODO preserving the rotation radius needs some rethinking... + if (_rot_radius) (*_rot_radius) *= m.descrim(); + signal_update.emit(); +} + +/** Align control points on the specified axis. */ +void ControlPointSelection::align(Geom::Dim2 axis) +{ + if (empty()) return; + Geom::Dim2 d = static_cast((axis + 1) % 2); + + Geom::OptInterval bound; + for (iterator i = _points.begin(); i != _points.end(); ++i) { + bound.unionWith(Geom::OptInterval(i->first->position()[d])); + } + + double new_coord = bound->middle(); + for (iterator i = _points.begin(); i != _points.end(); ++i) { + Geom::Point pos = i->first->position(); + pos[d] = new_coord; + i->first->move(pos); + } +} + +/** Equdistantly distribute control points by moving them in the specified dimension. */ +void ControlPointSelection::distribute(Geom::Dim2 d) +{ + if (empty()) return; + + // this needs to be a multimap, otherwise it will fail when some points have the same coord + typedef std::multimap SortMap; + + SortMap sm; + Geom::OptInterval bound; + // first we insert all points into a multimap keyed by the aligned coord to sort them + // simultaneously we compute the extent of selection + for (iterator i = _points.begin(); i != _points.end(); ++i) { + Geom::Point pos = i->first->position(); + sm.insert(std::make_pair(pos[d], i->first)); + bound.unionWith(Geom::OptInterval(pos[d])); + } + + // now we iterate over the multimap and set aligned positions. + double step = size() == 1 ? 0 : bound->extent() / (size() - 1); + double start = bound->min(); + unsigned num = 0; + for (SortMap::iterator i = sm.begin(); i != sm.end(); ++i, ++num) { + Geom::Point pos = i->second->position(); + pos[d] = start + num * step; + i->second->move(pos); + } +} + +/** Get the bounds of the selection. + * @return Smallest rectangle containing the positions of all selected points, + * 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->first; + Geom::Point p = cur->position(); + if (!bound) { + bound = Geom::Rect(p, p); + } else { + bound->expandTo(p); + } + } + return bound; +} + +Geom::OptRect ControlPointSelection::bounds() +{ + Geom::OptRect bound; + for (iterator i = _points.begin(); i != _points.end(); ++i) { + SelectableControlPoint *cur = i->first; + Geom::OptRect r = cur->bounds(); + bound.unionWith(r); + } + return bound; +} + +void ControlPointSelection::showTransformHandles(bool v, bool one_node) +{ + _one_node_handles = one_node; + _handles_visible = v; + _updateTransformHandles(false); +} + +void ControlPointSelection::hideTransformHandles() +{ + _handles->setVisible(false); +} +void ControlPointSelection::restoreTransformHandles() +{ + _updateTransformHandles(true); +} + +void ControlPointSelection::_selectionGrabbed(SelectableControlPoint *p, GdkEventMotion *event) +{ + hideTransformHandles(); + _dragging = true; + if (held_alt(*event) && _sculpt_enabled) { + _sculpting = true; + _grabbed_point = p; + } else { + _sculpting = false; + } +} + +void ControlPointSelection::_selectionDragged(Geom::Point const &old_pos, Geom::Point &new_pos, + GdkEventMotion *event) +{ + Geom::Point delta = new_pos - old_pos; + /*if (_sculpting) { + // for now we only support the default sculpting profile (bell) + // others will be added when preferences will be able to store enumerated values + double pressure, alpha; + if (gdk_event_get_axis (event, GDK_AXIS_PRESSURE, &pressure)) { + pressure = CLAMP(pressure, 0.2, 0.8); + } else { + pressure = 0.5; + } + + alpha = 1 - 2 * fabs(pressure - 0.5); + if (pressure > 0.5) alpha = 1/alpha; + + for (iterator i = _points.begin(); i != _points.end(); ++i) { + SelectableControlPoint *cur = i->first; + double dist = Geom::distance(cur->position(), _grabbed_point->position()); + + cur->move(cur->position() + delta); + } + } else*/ { + for (iterator i = _points.begin(); i != _points.end(); ++i) { + SelectableControlPoint *cur = i->first; + cur->move(cur->position() + delta); + } + _handles->rotationCenter().move(_handles->rotationCenter().position() + delta); + } + signal_update.emit(); +} + +void ControlPointSelection::_selectionUngrabbed() +{ + _dragging = false; + _grabbed_point = NULL; + restoreTransformHandles(); + signal_commit.emit(COMMIT_MOUSE_MOVE); +} + +void ControlPointSelection::_updateTransformHandles(bool preserve_center) +{ + if (_dragging) return; + + if (_handles_visible && size() > 1) { + Geom::OptRect b = pointwiseBounds(); + _handles->setBounds(*b, preserve_center); + _handles->setVisible(true); + } else if (_one_node_handles && size() == 1) { // only one control point in selection + SelectableControlPoint *p = begin()->first; + _handles->setBounds(p->bounds()); + _handles->rotationCenter().move(p->position()); + _handles->rotationCenter().setVisible(false); + _handles->setVisible(true); + } else { + _handles->setVisible(false); + } +} + +/** Moves the selected points along the supplied unit vector according to + * the modifier state of the supplied event. */ +bool ControlPointSelection::_keyboardMove(GdkEventKey const &event, Geom::Point const &dir) +{ + if (held_control(event)) return false; + unsigned num = 1 + consume_same_key_events(shortcut_key(event), 0); + + Geom::Point delta = dir * num; + if (held_shift(event)) delta *= 10; + if (held_alt(event)) { + delta /= _desktop->current_zoom(); + } else { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + double nudge = prefs->getDoubleLimited("/options/nudgedistance/value", 2, 0, 1000); + delta *= nudge; + } + + transform(Geom::Translate(delta)); + if (fabs(dir[Geom::X]) > 0) { + signal_commit.emit(COMMIT_KEYBOARD_MOVE_X); + } else { + signal_commit.emit(COMMIT_KEYBOARD_MOVE_Y); + } + return true; +} + +/** 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 + * @param dir Direction of rotation (math convention: 1 = counterclockwise, -1 = clockwise) + */ +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; + } + _rot_radius = maxlen; + } + + double angle; + if (held_alt(event)) { + // 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; + } else { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int snaps = prefs->getIntLimited("/options/rotationsnapsperpi/value", 12, 1, 1000); + angle = M_PI * dir / snaps; + } + + // translate to origin, rotate, translate back to original position + Geom::Matrix m = Geom::Translate(-rc) + * Geom::Rotate(angle) * Geom::Translate(rc); + transform(m); + signal_commit.emit(COMMIT_KEYBOARD_ROTATE); + return true; +} + + +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(); + if (Geom::are_near(maxext, 0)) return false; + Geom::Point center = _handles->rotationCenter().position(); + + double length_change; + if (held_alt(event)) { + // Scale by "one pixel". It means shrink/grow 1px for the larger dimension + // of the bounding box. + length_change = 1.0 / _desktop->current_zoom() * dir; + } else { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + length_change = prefs->getDoubleLimited("/options/defaultscale/value", 2, 1, 1000); + length_change *= dir; + } + double scale = (maxext + length_change) / maxext; + + Geom::Matrix m = Geom::Translate(-center) * Geom::Scale(scale) * Geom::Translate(center); + transform(m); + signal_commit.emit(COMMIT_KEYBOARD_SCALE_UNIFORM); + return true; +} + +bool ControlPointSelection::_keyboardFlip(Geom::Dim2 d) +{ + if (empty()) return false; + + Geom::Scale scale_transform(1, 1); + if (d == Geom::X) { + scale_transform = Geom::Scale(-1, 1); + } else { + scale_transform = Geom::Scale(1, -1); + } + + SelectableControlPoint *scp = + dynamic_cast(ControlPoint::mouseovered_point); + Geom::Point center = scp ? scp->position() : _handles->rotationCenter().position(); + + Geom::Matrix 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; +} + +void ControlPointSelection::_commitTransform(CommitEvent ce) +{ + _updateTransformHandles(true); + signal_commit.emit(ce); +} + +bool ControlPointSelection::event(GdkEvent *event) +{ + // implement generic event handling that should apply for all control point selections here; + // for example, keyboard moves and transformations. This way this functionality doesn't need + // to be duplicated in many places + // Later split out so that it can be reused in object selection + + switch (event->type) { + case GDK_KEY_PRESS: + // do not handle key events if the selection is empty + if (empty()) break; + + switch(shortcut_key(event->key)) { + // moves + case GDK_Up: + case GDK_KP_Up: + case GDK_KP_8: + return _keyboardMove(event->key, Geom::Point(0, 1)); + case GDK_Down: + case GDK_KP_Down: + case GDK_KP_2: + return _keyboardMove(event->key, Geom::Point(0, -1)); + case GDK_Right: + case GDK_KP_Right: + case GDK_KP_6: + return _keyboardMove(event->key, Geom::Point(1, 0)); + case GDK_Left: + case GDK_KP_Left: + case GDK_KP_4: + return _keyboardMove(event->key, Geom::Point(-1, 0)); + + // rotates + case GDK_bracketleft: + return _keyboardRotate(event->key, 1); + case GDK_bracketright: + return _keyboardRotate(event->key, -1); + + // scaling + case GDK_less: + case GDK_comma: + return _keyboardScale(event->key, -1); + case GDK_greater: + case GDK_period: + return _keyboardScale(event->key, 1); + + // TODO: skewing + + // flipping + // NOTE: H is horizontal flip, while Shift+H switches transform handle mode! + case GDK_h: + case GDK_H: + if (held_shift(event->key)) { + // TODO make a method for mode switching + if (_handles->mode() == TransformHandleSet::MODE_SCALE) { + _handles->setMode(TransformHandleSet::MODE_ROTATE_SKEW); + if (size() == 1) _handles->rotationCenter().setVisible(false); + } else { + _handles->setMode(TransformHandleSet::MODE_SCALE); + } + return true; + } + // any modifiers except shift should cause no action + if (held_any_modifiers(event->key)) break; + return _keyboardFlip(Geom::X); + case GDK_v: + case GDK_V: + if (held_any_modifiers(event->key)) break; + return _keyboardFlip(Geom::Y); + default: break; + } + break; + default: break; + } + return false; +} + +} // namespace UI +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : -- cgit v1.2.3 From b52865a71a9f83da9719a3ec5f50a4a2cd7cdace Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Sun, 10 Jan 2010 01:46:28 +0100 Subject: * Implement node snapping. * Fix minor bug in linear grow. * Add --fixes. * Move some node selection-related functions to ControlPointSelection. Fixed bugs: - https://launchpad.net/bugs/170561 - https://launchpad.net/bugs/171893 - https://launchpad.net/bugs/182585 - https://launchpad.net/bugs/446773 (bzr r8846.2.9) --- src/ui/tool/control-point-selection.cpp | 55 ++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) (limited to 'src/ui/tool/control-point-selection.cpp') diff --git a/src/ui/tool/control-point-selection.cpp b/src/ui/tool/control-point-selection.cpp index d10045c62..5a84592b6 100644 --- a/src/ui/tool/control-point-selection.cpp +++ b/src/ui/tool/control-point-selection.cpp @@ -140,7 +140,60 @@ void ControlPointSelection::clear() erase(i++); } -/** Transform all selected control points by the supplied affine transformation. */ +/** Select all points that this selection can contain. */ +void ControlPointSelection::selectAll() +{ + for (set_type::iterator i = _all_points.begin(); i != _all_points.end(); ++i) { + insert(*i); + } +} +/** Select all points inside the given rectangle (in desktop coordinates). */ +void ControlPointSelection::selectArea(Geom::Rect const &r) +{ + for (set_type::iterator i = _all_points.begin(); i != _all_points.end(); ++i) { + if (r.contains(**i)) + insert(*i); + } +} +/** Unselect all selected points and select all unselected points. */ +void ControlPointSelection::invertSelection() +{ + for (set_type::iterator i = _all_points.begin(); i != _all_points.end(); ++i) { + if ((*i)->selected()) erase(*i); + else insert(*i); + } +} +void ControlPointSelection::spatialGrow(SelectableControlPoint *origin, int dir) +{ + bool grow = (dir > 0); + Geom::Point p = origin->position(); + double best_dist = grow ? HUGE_VAL : 0; + SelectableControlPoint *match = NULL; + for (set_type::iterator i = _all_points.begin(); i != _all_points.end(); ++i) { + bool selected = (*i)->selected(); + if (grow && !selected) { + double dist = Geom::distance((*i)->position(), p); + if (dist < best_dist) { + best_dist = dist; + match = *i; + } + } + if (!grow && selected) { + double dist = Geom::distance((*i)->position(), p); + // use >= to also deselect the origin node when it's the last one selected + if (dist >= best_dist) { + best_dist = dist; + match = *i; + } + } + } + if (match) { + if (grow) insert(match); + else erase(match); + } +} + +/** Transform all selected control points by the given affine transformation. */ void ControlPointSelection::transform(Geom::Matrix const &m) { for (iterator i = _points.begin(); i != _points.end(); ++i) { -- cgit v1.2.3 From 13b341b68636dbc621daf94f6efa229489b7fd70 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Thu, 14 Jan 2010 18:45:20 +0100 Subject: * Add "show transform handles" toggle button. * Transform handle mode switching similar to selector tool, when node transform handles are visible. (bzr r8846.2.18) --- src/ui/tool/control-point-selection.cpp | 82 +++++++++++++++------------------ 1 file changed, 37 insertions(+), 45 deletions(-) (limited to 'src/ui/tool/control-point-selection.cpp') diff --git a/src/ui/tool/control-point-selection.cpp b/src/ui/tool/control-point-selection.cpp index 5a84592b6..245feae1d 100644 --- a/src/ui/tool/control-point-selection.cpp +++ b/src/ui/tool/control-point-selection.cpp @@ -49,8 +49,6 @@ ControlPointSelection::ControlPointSelection(SPDesktop *d, SPCanvasGroup *th_gro , _dragging(false) , _handles_visible(true) , _one_node_handles(false) - , _sculpt_enabled(false) - , _sculpting(false) { signal_update.connect( sigc::bind( sigc::mem_fun(*this, &ControlPointSelection::_updateTransformHandles), @@ -83,7 +81,7 @@ std::pair ControlPointSelection::insert(c boost::shared_ptr clist(new connlist_type()); // hide event param and always return false - clist->push_back( + /*clist->push_back( x->signal_grabbed.connect( sigc::bind_return( sigc::bind<0>( @@ -93,11 +91,16 @@ std::pair ControlPointSelection::insert(c clist->push_back( x->signal_dragged.connect( sigc::mem_fun(*this, &ControlPointSelection::_selectionDragged))); - // hide event parameter clist->push_back( x->signal_ungrabbed.connect( sigc::hide( sigc::mem_fun(*this, &ControlPointSelection::_selectionUngrabbed)))); + clist->push_back( + x->signal_clicked.connect( + sigc::hide( + sigc::bind<0>( + sigc::mem_fun(*this, &ControlPointSelection::_selectionClicked), + x))));*/ found = _points.insert(std::make_pair(x, clist)).first; @@ -298,52 +301,35 @@ void ControlPointSelection::restoreTransformHandles() _updateTransformHandles(true); } -void ControlPointSelection::_selectionGrabbed(SelectableControlPoint *p, GdkEventMotion *event) +void ControlPointSelection::toggleTransformHandlesMode() { - hideTransformHandles(); - _dragging = true; - if (held_alt(*event) && _sculpt_enabled) { - _sculpting = true; - _grabbed_point = p; + if (_handles->mode() == TransformHandleSet::MODE_SCALE) { + _handles->setMode(TransformHandleSet::MODE_ROTATE_SKEW); + if (size() == 1) _handles->rotationCenter().setVisible(false); } else { - _sculpting = false; + _handles->setMode(TransformHandleSet::MODE_SCALE); } } -void ControlPointSelection::_selectionDragged(Geom::Point const &old_pos, Geom::Point &new_pos, +void ControlPointSelection::_pointGrabbed() +{ + hideTransformHandles(); + _dragging = true; +} + +void ControlPointSelection::_pointDragged(Geom::Point const &old_pos, Geom::Point &new_pos, GdkEventMotion *event) { Geom::Point delta = new_pos - old_pos; - /*if (_sculpting) { - // for now we only support the default sculpting profile (bell) - // others will be added when preferences will be able to store enumerated values - double pressure, alpha; - if (gdk_event_get_axis (event, GDK_AXIS_PRESSURE, &pressure)) { - pressure = CLAMP(pressure, 0.2, 0.8); - } else { - pressure = 0.5; - } - - alpha = 1 - 2 * fabs(pressure - 0.5); - if (pressure > 0.5) alpha = 1/alpha; - - for (iterator i = _points.begin(); i != _points.end(); ++i) { - SelectableControlPoint *cur = i->first; - double dist = Geom::distance(cur->position(), _grabbed_point->position()); - - cur->move(cur->position() + delta); - } - } else*/ { - for (iterator i = _points.begin(); i != _points.end(); ++i) { - SelectableControlPoint *cur = i->first; - cur->move(cur->position() + delta); - } - _handles->rotationCenter().move(_handles->rotationCenter().position() + delta); + for (iterator i = _points.begin(); i != _points.end(); ++i) { + SelectableControlPoint *cur = i->first; + cur->move(cur->position() + delta); } + _handles->rotationCenter().move(_handles->rotationCenter().position() + delta); signal_update.emit(); } -void ControlPointSelection::_selectionUngrabbed() +void ControlPointSelection::_pointUngrabbed() { _dragging = false; _grabbed_point = NULL; @@ -351,6 +337,18 @@ void ControlPointSelection::_selectionUngrabbed() signal_commit.emit(COMMIT_MOUSE_MOVE); } +bool ControlPointSelection::_pointClicked(SelectableControlPoint *p, GdkEventButton *event) +{ + // clicking a selected node should toggle the transform handles between rotate and scale mode, + // if they are visible + if (held_shift(*event)) return false; + if (_handles_visible && p->selected()) { + toggleTransformHandlesMode(); + return true; + } + return false; +} + void ControlPointSelection::_updateTransformHandles(bool preserve_center) { if (_dragging) return; @@ -544,13 +542,7 @@ bool ControlPointSelection::event(GdkEvent *event) case GDK_h: case GDK_H: if (held_shift(event->key)) { - // TODO make a method for mode switching - if (_handles->mode() == TransformHandleSet::MODE_SCALE) { - _handles->setMode(TransformHandleSet::MODE_ROTATE_SKEW); - if (size() == 1) _handles->rotationCenter().setVisible(false); - } else { - _handles->setMode(TransformHandleSet::MODE_SCALE); - } + toggleTransformHandlesMode(); return true; } // any modifiers except shift should cause no action -- cgit v1.2.3 From 2ab01d69f9d427bdf3536e1508a465372cd040c6 Mon Sep 17 00:00:00 2001 From: "Jon A. Cruz" Date: Thu, 14 Jan 2010 17:02:46 -0800 Subject: Warning cleanup. (bzr r8981) --- src/ui/tool/control-point-selection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/ui/tool/control-point-selection.cpp') diff --git a/src/ui/tool/control-point-selection.cpp b/src/ui/tool/control-point-selection.cpp index 245feae1d..2a3498d7f 100644 --- a/src/ui/tool/control-point-selection.cpp +++ b/src/ui/tool/control-point-selection.cpp @@ -318,7 +318,7 @@ void ControlPointSelection::_pointGrabbed() } void ControlPointSelection::_pointDragged(Geom::Point const &old_pos, Geom::Point &new_pos, - GdkEventMotion *event) + GdkEventMotion */*event*/) { Geom::Point delta = new_pos - old_pos; for (iterator i = _points.begin(); i != _points.end(); ++i) { -- cgit v1.2.3 From e6b5fdce005681751c781326ec649f0882a4c13f Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Wed, 20 Jan 2010 15:14:37 +0100 Subject: Fix snapping in the node tool when more than one point is dragged. (bzr r9001) --- src/ui/tool/control-point-selection.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'src/ui/tool/control-point-selection.cpp') diff --git a/src/ui/tool/control-point-selection.cpp b/src/ui/tool/control-point-selection.cpp index 2a3498d7f..d1ff5f9d8 100644 --- a/src/ui/tool/control-point-selection.cpp +++ b/src/ui/tool/control-point-selection.cpp @@ -81,26 +81,24 @@ std::pair ControlPointSelection::insert(c boost::shared_ptr clist(new connlist_type()); // hide event param and always return false - /*clist->push_back( + clist->push_back( x->signal_grabbed.connect( sigc::bind_return( - sigc::bind<0>( - sigc::mem_fun(*this, &ControlPointSelection::_selectionGrabbed), - x), + sigc::hide( + sigc::mem_fun(*this, &ControlPointSelection::_pointGrabbed)), false))); clist->push_back( x->signal_dragged.connect( - sigc::mem_fun(*this, &ControlPointSelection::_selectionDragged))); + sigc::mem_fun(*this, &ControlPointSelection::_pointDragged))); clist->push_back( x->signal_ungrabbed.connect( sigc::hide( - sigc::mem_fun(*this, &ControlPointSelection::_selectionUngrabbed)))); + sigc::mem_fun(*this, &ControlPointSelection::_pointUngrabbed)))); clist->push_back( x->signal_clicked.connect( - sigc::hide( - sigc::bind<0>( - sigc::mem_fun(*this, &ControlPointSelection::_selectionClicked), - x))));*/ + sigc::bind<0>( + sigc::mem_fun(*this, &ControlPointSelection::_pointClicked), + x))); found = _points.insert(std::make_pair(x, clist)).first; -- cgit v1.2.3 From eda60eb87722be160bdfa70694b971df9659c56b Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Wed, 20 Jan 2010 23:04:49 +0100 Subject: Fix Ctrl+Alt+click deletion of selected nodes when transform handles are turned on. (bzr r9008) --- src/ui/tool/control-point-selection.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/ui/tool/control-point-selection.cpp') diff --git a/src/ui/tool/control-point-selection.cpp b/src/ui/tool/control-point-selection.cpp index d1ff5f9d8..615968fef 100644 --- a/src/ui/tool/control-point-selection.cpp +++ b/src/ui/tool/control-point-selection.cpp @@ -339,8 +339,7 @@ bool ControlPointSelection::_pointClicked(SelectableControlPoint *p, GdkEventBut { // clicking a selected node should toggle the transform handles between rotate and scale mode, // if they are visible - if (held_shift(*event)) return false; - if (_handles_visible && p->selected()) { + if (held_no_modifiers(*event) && _handles_visible && p->selected()) { toggleTransformHandlesMode(); return true; } -- cgit v1.2.3 From 7ce8847f2410a24a6bce4ca8a43ad7ebdb4839eb Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Thu, 4 Feb 2010 03:14:09 +0100 Subject: Reduce libsigc++ usage to partially fix performance regressions in the new node tool. (bzr r9044) --- src/ui/tool/control-point-selection.cpp | 50 ++++++++------------------------- 1 file changed, 12 insertions(+), 38 deletions(-) (limited to 'src/ui/tool/control-point-selection.cpp') diff --git a/src/ui/tool/control-point-selection.cpp b/src/ui/tool/control-point-selection.cpp index 615968fef..de19fef8d 100644 --- a/src/ui/tool/control-point-selection.cpp +++ b/src/ui/tool/control-point-selection.cpp @@ -78,29 +78,7 @@ std::pair ControlPointSelection::insert(c return std::pair(found, false); } - boost::shared_ptr clist(new connlist_type()); - - // hide event param and always return false - clist->push_back( - x->signal_grabbed.connect( - sigc::bind_return( - sigc::hide( - sigc::mem_fun(*this, &ControlPointSelection::_pointGrabbed)), - false))); - clist->push_back( - x->signal_dragged.connect( - sigc::mem_fun(*this, &ControlPointSelection::_pointDragged))); - clist->push_back( - x->signal_ungrabbed.connect( - sigc::hide( - sigc::mem_fun(*this, &ControlPointSelection::_pointUngrabbed)))); - clist->push_back( - x->signal_clicked.connect( - sigc::bind<0>( - sigc::mem_fun(*this, &ControlPointSelection::_pointClicked), - x))); - - found = _points.insert(std::make_pair(x, clist)).first; + found = _points.insert(x).first; x->updateState(); _rot_radius.reset(); @@ -112,11 +90,7 @@ std::pair ControlPointSelection::insert(c /** Remove a point from the selection. */ void ControlPointSelection::erase(iterator pos) { - SelectableControlPoint *erased = pos->first; - boost::shared_ptr clist = pos->second; - for (connlist_type::iterator i = clist->begin(); i != clist->end(); ++i) { - i->disconnect(); - } + SelectableControlPoint *erased = *pos; _points.erase(pos); erased->updateState(); _rot_radius.reset(); @@ -198,7 +172,7 @@ void ControlPointSelection::spatialGrow(SelectableControlPoint *origin, int dir) void ControlPointSelection::transform(Geom::Matrix const &m) { for (iterator i = _points.begin(); i != _points.end(); ++i) { - SelectableControlPoint *cur = i->first; + SelectableControlPoint *cur = *i; cur->transform(m); } // TODO preserving the rotation radius needs some rethinking... @@ -214,14 +188,14 @@ void ControlPointSelection::align(Geom::Dim2 axis) Geom::OptInterval bound; for (iterator i = _points.begin(); i != _points.end(); ++i) { - bound.unionWith(Geom::OptInterval(i->first->position()[d])); + bound.unionWith(Geom::OptInterval((*i)->position()[d])); } double new_coord = bound->middle(); for (iterator i = _points.begin(); i != _points.end(); ++i) { - Geom::Point pos = i->first->position(); + Geom::Point pos = (*i)->position(); pos[d] = new_coord; - i->first->move(pos); + (*i)->move(pos); } } @@ -238,8 +212,8 @@ void ControlPointSelection::distribute(Geom::Dim2 d) // first we insert all points into a multimap keyed by the aligned coord to sort them // simultaneously we compute the extent of selection for (iterator i = _points.begin(); i != _points.end(); ++i) { - Geom::Point pos = i->first->position(); - sm.insert(std::make_pair(pos[d], i->first)); + Geom::Point pos = (*i)->position(); + sm.insert(std::make_pair(pos[d], (*i))); bound.unionWith(Geom::OptInterval(pos[d])); } @@ -261,7 +235,7 @@ Geom::OptRect ControlPointSelection::pointwiseBounds() { Geom::OptRect bound; for (iterator i = _points.begin(); i != _points.end(); ++i) { - SelectableControlPoint *cur = i->first; + SelectableControlPoint *cur = (*i); Geom::Point p = cur->position(); if (!bound) { bound = Geom::Rect(p, p); @@ -276,7 +250,7 @@ Geom::OptRect ControlPointSelection::bounds() { Geom::OptRect bound; for (iterator i = _points.begin(); i != _points.end(); ++i) { - SelectableControlPoint *cur = i->first; + SelectableControlPoint *cur = (*i); Geom::OptRect r = cur->bounds(); bound.unionWith(r); } @@ -320,7 +294,7 @@ void ControlPointSelection::_pointDragged(Geom::Point const &old_pos, Geom::Poin { Geom::Point delta = new_pos - old_pos; for (iterator i = _points.begin(); i != _points.end(); ++i) { - SelectableControlPoint *cur = i->first; + SelectableControlPoint *cur = (*i); cur->move(cur->position() + delta); } _handles->rotationCenter().move(_handles->rotationCenter().position() + delta); @@ -355,7 +329,7 @@ void ControlPointSelection::_updateTransformHandles(bool preserve_center) _handles->setBounds(*b, preserve_center); _handles->setVisible(true); } else if (_one_node_handles && size() == 1) { // only one control point in selection - SelectableControlPoint *p = begin()->first; + SelectableControlPoint *p = *begin(); _handles->setBounds(p->bounds()); _handles->rotationCenter().move(p->position()); _handles->rotationCenter().setVisible(false); -- cgit v1.2.3 From 9d9e9264afc4e6f83d59bd25ccae505eadb739d8 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Sat, 6 Feb 2010 22:45:14 +0100 Subject: Fix performance regressions in the node tool and a stupid crash bug when deleting more than one stretch of selected nodes (bzr r9061) --- src/ui/tool/control-point-selection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/ui/tool/control-point-selection.cpp') diff --git a/src/ui/tool/control-point-selection.cpp b/src/ui/tool/control-point-selection.cpp index de19fef8d..638318dbf 100644 --- a/src/ui/tool/control-point-selection.cpp +++ b/src/ui/tool/control-point-selection.cpp @@ -344,7 +344,7 @@ void ControlPointSelection::_updateTransformHandles(bool preserve_center) bool ControlPointSelection::_keyboardMove(GdkEventKey const &event, Geom::Point const &dir) { if (held_control(event)) return false; - unsigned num = 1 + consume_same_key_events(shortcut_key(event), 0); + unsigned num = 1 + combine_key_events(shortcut_key(event), 0); Geom::Point delta = dir * num; if (held_shift(event)) delta *= 10; -- cgit v1.2.3 From 81f88ca0856da56bdf426cd065ff0acd3414567f Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Tue, 9 Feb 2010 03:20:18 +0100 Subject: Fix multiple minor problems in the node tool (bzr r9070) --- src/ui/tool/control-point-selection.cpp | 132 +++++++++++++++++++++----------- 1 file changed, 88 insertions(+), 44 deletions(-) (limited to 'src/ui/tool/control-point-selection.cpp') 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 #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::insert(c found = _points.insert(x).first; x->updateState(); - _rot_radius.reset(); - signal_point_changed.emit(x, true); + _pointChanged(x, true); return std::pair(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(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(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); } -- cgit v1.2.3 From 7f5277e2c7722d2375fc7108f8d3b796f26aaccd Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Wed, 3 Mar 2010 01:10:54 +0100 Subject: Node tool: implement sculpting (bzr r9131) --- src/ui/tool/control-point-selection.cpp | 41 +++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 9 deletions(-) (limited to 'src/ui/tool/control-point-selection.cpp') diff --git a/src/ui/tool/control-point-selection.cpp b/src/ui/tool/control-point-selection.cpp index df27c2a72..f880d2ddf 100644 --- a/src/ui/tool/control-point-selection.cpp +++ b/src/ui/tool/control-point-selection.cpp @@ -266,28 +266,51 @@ void ControlPointSelection::toggleTransformHandlesMode() } } -void ControlPointSelection::_pointGrabbed() +void ControlPointSelection::_pointGrabbed(SelectableControlPoint *point) { hideTransformHandles(); _dragging = true; + _grabbed_point = point; + _farthest_point = point; + double maxdist = 0; + for (iterator i = _points.begin(); i != _points.end(); ++i) { + _original_positions.insert(std::make_pair(*i, (*i)->position())); + double dist = Geom::distance(*_grabbed_point, **i); + if (dist > maxdist) { + maxdist = dist; + _farthest_point = *i; + } + } } -void ControlPointSelection::_pointDragged(Geom::Point const &old_pos, Geom::Point &new_pos, - GdkEventMotion */*event*/) +void ControlPointSelection::_pointDragged(Geom::Point &new_pos, GdkEventMotion *event) { - Geom::Point delta = new_pos - old_pos; - for (iterator i = _points.begin(); i != _points.end(); ++i) { - SelectableControlPoint *cur = (*i); - cur->move(cur->position() + delta); + 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) { + // sculpting + for (iterator i = _points.begin(); i != _points.end(); ++i) { + SelectableControlPoint *cur = (*i); + double dist = Geom::distance(_original_positions[cur], _original_positions[_grabbed_point]); + double deltafrac = 0.5 + 0.5 * cos(M_PI * dist/fdist); + cur->move(_original_positions[cur] + abs_delta * deltafrac); + } + } else { + Geom::Point delta = new_pos - _grabbed_point->position(); + for (iterator i = _points.begin(); i != _points.end(); ++i) { + SelectableControlPoint *cur = (*i); + cur->move(_original_positions[cur] + abs_delta); + } + _handles->rotationCenter().move(_handles->rotationCenter().position() + delta); } - _handles->rotationCenter().move(_handles->rotationCenter().position() + delta); signal_update.emit(); } void ControlPointSelection::_pointUngrabbed() { + _original_positions.clear(); _dragging = false; - _grabbed_point = NULL; + _grabbed_point = _farthest_point = NULL; _updateBounds(); restoreTransformHandles(); signal_commit.emit(COMMIT_MOUSE_MOVE); -- cgit v1.2.3