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/multi-path-manipulator.cpp | 568 +++++++++++++++++++++++++++++++++ 1 file changed, 568 insertions(+) create mode 100644 src/ui/tool/multi-path-manipulator.cpp (limited to 'src/ui/tool/multi-path-manipulator.cpp') diff --git a/src/ui/tool/multi-path-manipulator.cpp b/src/ui/tool/multi-path-manipulator.cpp new file mode 100644 index 000000000..6b245702a --- /dev/null +++ b/src/ui/tool/multi-path-manipulator.cpp @@ -0,0 +1,568 @@ +/** @file + * Path manipulator - implementation + */ +/* Authors: + * Krzysztof KosiƄski + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#include +#include +#include +#include "desktop.h" +#include "desktop-handles.h" +#include "document.h" +#include "message-stack.h" +#include "sp-path.h" +#include "ui/tool/control-point-selection.h" +#include "ui/tool/event-utils.h" +#include "ui/tool/node.h" +#include "ui/tool/multi-path-manipulator.h" +#include "ui/tool/path-manipulator.h" + +namespace std { using namespace tr1; } + +namespace Inkscape { +namespace UI { + +namespace { +typedef std::pair IterPair; +typedef std::vector IterPairList; +typedef std::unordered_set IterSet; +typedef std::multimap DistanceMap; +typedef std::pair DistanceMapItem; + +/** Find two selected endnodes. + * @returns -1 if not enough endnodes selected, 1 if too many, 0 if OK */ +void find_join_iterators(ControlPointSelection &sel, IterPairList &pairs) +{ + IterSet join_iters; + DistanceMap dists; + + // find all endnodes in selection + for (ControlPointSelection::iterator i = sel.begin(); i != sel.end(); ++i) { + Node *node = dynamic_cast(i->first); + if (!node) continue; + NodeList::iterator iter = NodeList::get_iterator(node); + if (!iter.next() || !iter.prev()) join_iters.insert(iter); + } + + if (join_iters.size() < 2) return; + + // Below we find the closest pairs. The algorithm is O(N^3). + // We can go down to O(N^2 log N) by using O(N^2) memory, by putting all pairs + // with their distances in a multimap (not worth it IMO). + while (join_iters.size() >= 2) { + double closest = DBL_MAX; + IterPair closest_pair; + for (IterSet::iterator i = join_iters.begin(); i != join_iters.end(); ++i) { + for (IterSet::iterator j = join_iters.begin(); j != i; ++j) { + double dist = Geom::distance(**i, **j); + if (dist < closest) { + closest = dist; + closest_pair = std::make_pair(*i, *j); + } + } + } + pairs.push_back(closest_pair); + join_iters.erase(closest_pair.first); + join_iters.erase(closest_pair.second); + } +} + +/** After this function, first should be at the end of path and second at the beginnning. + * @returns True if the nodes are in the same subpath */ +bool prepare_join(IterPair &join_iters) +{ + if (&NodeList::get(join_iters.first) == &NodeList::get(join_iters.second)) { + if (join_iters.first.next()) // if first is begin, swap the iterators + std::swap(join_iters.first, join_iters.second); + return true; + } + + NodeList &sp_first = NodeList::get(join_iters.first); + NodeList &sp_second = NodeList::get(join_iters.second); + if (join_iters.first.next()) { // first is begin + if (join_iters.second.next()) { // second is begin + sp_first.reverse(); + } else { // second is end + std::swap(join_iters.first, join_iters.second); + } + } else { // first is end + if (join_iters.second.next()) { // second is begin + // do nothing + } else { // second is end + sp_second.reverse(); + } + } + return false; +} +} // anonymous namespace + + +MultiPathManipulator::MultiPathManipulator(PathSharedData const &data, sigc::connection &chg) + : PointManipulator(data.node_data.desktop, *data.node_data.selection) + , _path_data(data) + , _changed(chg) +{ + // + _selection.signal_commit.connect( + sigc::mem_fun(*this, &MultiPathManipulator::_commit)); + _selection.signal_point_changed.connect( + sigc::hide( sigc::hide( + signal_coords_changed.make_slot()))); +} + +MultiPathManipulator::~MultiPathManipulator() +{ + _mmap.clear(); +} + +/** Remove empty manipulators. */ +void MultiPathManipulator::cleanup() +{ + for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ) { + if (i->second->empty()) _mmap.erase(i++); + else ++i; + } +} + +void MultiPathManipulator::setItems(std::map > const &items) +{ + typedef std::map > TransMap; + typedef std::set ItemSet; + ItemSet to_remove, to_add, current, new_items; + + for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) { + current.insert(i->first); + } + for (TransMap::const_iterator i = items.begin(); i != items.end(); ++i) { + new_items.insert(i->first); + } + + std::set_difference(current.begin(), current.end(), new_items.begin(), new_items.end(), + std::inserter(to_remove, to_remove.end())); + std::set_difference(new_items.begin(), new_items.end(), current.begin(), current.end(), + std::inserter(to_add, to_add.end())); + + for (ItemSet::iterator i = to_remove.begin(); i != to_remove.end(); ++i) { + _mmap.erase(*i); + } + for (ItemSet::iterator i = to_add.begin(); i != to_add.end(); ++i) { + boost::shared_ptr pm; + TransMap::const_iterator f = items.find(*i); + pm.reset(new PathManipulator(_path_data, *i, f->second.first, f->second.second)); + pm->showHandles(_show_handles); + pm->showOutline(_show_outline); + pm->showPathDirection(_show_path_direction); + _mmap.insert(std::make_pair(*i, pm)); + } +} + +void MultiPathManipulator::selectSubpaths() +{ + if (_selection.empty()) { + invokeForAll(&PathManipulator::selectAll); + } else { + invokeForAll(&PathManipulator::selectSubpaths); + } +} +void MultiPathManipulator::selectAll() +{ + invokeForAll(&PathManipulator::selectAll); +} + +void MultiPathManipulator::selectArea(Geom::Rect const &area, bool take) +{ + if (take) _selection.clear(); + invokeForAll(&PathManipulator::selectArea, area); +} + +void MultiPathManipulator::shiftSelection(int dir) +{ + invokeForAll(&PathManipulator::shiftSelection, dir); +} +void MultiPathManipulator::invertSelection() +{ + invokeForAll(&PathManipulator::invertSelection); +} +void MultiPathManipulator::invertSelectionInSubpaths() +{ + invokeForAll(&PathManipulator::invertSelectionInSubpaths); +} +void MultiPathManipulator::deselect() +{ + _selection.clear(); +} + +void MultiPathManipulator::setNodeType(NodeType type) +{ + if (_selection.empty()) return; + for (ControlPointSelection::iterator i = _selection.begin(); i != _selection.end(); ++i) { + Node *node = dynamic_cast(i->first); + if (node) node->setType(type); + } + _done(_("Change node type")); +} + +void MultiPathManipulator::setSegmentType(SegmentType type) +{ + if (_selection.empty()) return; + invokeForAll(&PathManipulator::setSegmentType, type); + if (type == SEGMENT_STRAIGHT) { + _done(_("Straighten segments")); + } else { + _done(_("Make segments curves")); + } +} + +void MultiPathManipulator::insertNodes() +{ + invokeForAll(&PathManipulator::insertNodes); + _done(_("Add nodes")); +} + +void MultiPathManipulator::joinNodes() +{ + // Node join has two parts. In the first one we join two subpaths by fusing endpoints + // into one. In the second we fuse nodes in each subpath. + IterPairList joins; + NodeList::iterator preserve_pos; + Node *mouseover_node = dynamic_cast(ControlPoint::mouseovered_point); + if (mouseover_node) { + preserve_pos = NodeList::get_iterator(mouseover_node); + } + find_join_iterators(_selection, joins); + + for (IterPairList::iterator i = joins.begin(); i != joins.end(); ++i) { + bool same_path = prepare_join(*i); + bool mouseover = true; + NodeList &sp_first = NodeList::get(i->first); + NodeList &sp_second = NodeList::get(i->second); + i->first->setType(NODE_CUSP, false); + + Geom::Point joined_pos, pos_front, pos_back; + pos_front = *i->second->front(); + pos_back = *i->first->back(); + if (i->first == preserve_pos) { + joined_pos = *i->first; + } else if (i->second == preserve_pos) { + joined_pos = *i->second; + } else { + joined_pos = Geom::middle_point(pos_back, pos_front); + mouseover = false; + } + + // if the handles aren't degenerate, don't move them + i->first->move(joined_pos); + Node *joined_node = i->first.ptr(); + if (!i->second->front()->isDegenerate()) { + joined_node->front()->setPosition(pos_front); + } + if (!i->first->back()->isDegenerate()) { + joined_node->back()->setPosition(pos_back); + } + if (mouseover) { + // Second node could be mouseovered, but it will be deleted, so we must change + // the preserve_pos iterator to the first node. + preserve_pos = i->first; + } + sp_second.erase(i->second); + + if (same_path) { + sp_first.setClosed(true); + } else { + sp_first.splice(sp_first.end(), sp_second); + sp_second.kill(); + } + _selection.insert(i->first.ptr()); + } + // Second part replaces contiguous selections of nodes with single nodes + invokeForAll(&PathManipulator::weldNodes, preserve_pos); + _doneWithCleanup(_("Join nodes")); +} + +void MultiPathManipulator::breakNodes() +{ + if (_selection.empty()) return; + invokeForAll(&PathManipulator::breakNodes); + _done(_("Break nodes")); +} + +void MultiPathManipulator::deleteNodes(bool keep_shape) +{ + if (_selection.empty()) return; + invokeForAll(&PathManipulator::deleteNodes, keep_shape); + _doneWithCleanup(_("Delete nodes")); +} + +/** Join selected endpoints to create segments. */ +void MultiPathManipulator::joinSegment() +{ + IterPairList joins; + find_join_iterators(_selection, joins); + if (joins.empty()) { + _desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, + _("There must be at least 2 endnodes in selection")); + return; + } + + for (IterPairList::iterator i = joins.begin(); i != joins.end(); ++i) { + bool same_path = prepare_join(*i); + NodeList &sp_first = NodeList::get(i->first); + NodeList &sp_second = NodeList::get(i->second); + i->first->setType(NODE_CUSP, false); + i->second->setType(NODE_CUSP, false); + if (same_path) { + sp_first.setClosed(true); + } else { + sp_first.splice(sp_first.end(), sp_second); + sp_second.kill(); + } + } + + _doneWithCleanup("Join segment"); +} + +void MultiPathManipulator::deleteSegments() +{ + if (_selection.empty()) return; + invokeForAll(&PathManipulator::deleteSegments); + _doneWithCleanup("Delete segments"); +} + +void MultiPathManipulator::alignNodes(Geom::Dim2 d) +{ + _selection.align(d); + if (d == Geom::X) { + _done("Align nodes to a horizontal line"); + } else { + _done("Align nodes to a vertical line"); + } +} + +void MultiPathManipulator::distributeNodes(Geom::Dim2 d) +{ + _selection.distribute(d); + if (d == Geom::X) { + _done("Distrubute nodes horizontally"); + } else { + _done("Distribute nodes vertically"); + } +} + +void MultiPathManipulator::reverseSubpaths() +{ + invokeForAll(&PathManipulator::reverseSubpaths); + _done("Reverse selected subpaths"); +} + +void MultiPathManipulator::move(Geom::Point const &delta) +{ + _selection.transform(Geom::Translate(delta)); + _done("Move nodes"); +} + +void MultiPathManipulator::showOutline(bool show) +{ + invokeForAll(&PathManipulator::showOutline, show); + _show_outline = show; +} + +void MultiPathManipulator::showHandles(bool show) +{ + invokeForAll(&PathManipulator::showHandles, show); + _show_handles = show; +} + +void MultiPathManipulator::showPathDirection(bool show) +{ + invokeForAll(&PathManipulator::showPathDirection, show); + _show_path_direction = show; +} + +bool MultiPathManipulator::event(GdkEvent *event) +{ + switch (event->type) { + case GDK_KEY_PRESS: + switch (shortcut_key(event->key)) { + case GDK_Insert: + case GDK_KP_Insert: + insertNodes(); + return true; + case GDK_i: + case GDK_I: + if (held_only_shift(event->key)) { + insertNodes(); + return true; + } + break; + case GDK_j: + case GDK_J: + if (held_only_shift(event->key)) { + joinNodes(); + return true; + } + if (held_only_alt(event->key)) { + joinSegment(); + return true; + } + break; + case GDK_b: + case GDK_B: + if (held_only_shift(event->key)) { + breakNodes(); + return true; + } + break; + case GDK_Delete: + case GDK_KP_Delete: + case GDK_BackSpace: + if (held_shift(event->key)) break; + if (held_alt(event->key)) { + deleteSegments(); + } else { + deleteNodes(!held_control(event->key)); + } + return true; + case GDK_c: + case GDK_C: + if (held_only_shift(event->key)) { + setNodeType(NODE_CUSP); + return true; + } + break; + case GDK_s: + case GDK_S: + if (held_only_shift(event->key)) { + setNodeType(NODE_SMOOTH); + return true; + } + break; + case GDK_a: + case GDK_A: + if (held_only_shift(event->key)) { + setNodeType(NODE_AUTO); + return true; + } + break; + case GDK_y: + case GDK_Y: + if (held_only_shift(event->key)) { + setNodeType(NODE_SYMMETRIC); + return true; + } + break; + case GDK_r: + case GDK_R: + if (held_only_shift(event->key)) { + reverseSubpaths(); + break; + } + break; + default: + break; + } + break; + default: break; + } + + for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) { + if (i->second->event(event)) return true; + } + return false; +} + +void MultiPathManipulator::_commit(CommitEvent cps) +{ + gchar const *reason = NULL; + gchar const *key = NULL; + switch(cps) { + case COMMIT_MOUSE_MOVE: + reason = _("Move nodes"); + break; + case COMMIT_KEYBOARD_MOVE_X: + reason = _("Move nodes horizontally"); + key = "node:move:x"; + break; + case COMMIT_KEYBOARD_MOVE_Y: + reason = _("Move nodes vertically"); + key = "node:move:y"; + break; + case COMMIT_MOUSE_ROTATE: + reason = _("Rotate nodes"); + break; + case COMMIT_KEYBOARD_ROTATE: + reason = _("Rotate nodes"); + key = "node:rotate"; + break; + case COMMIT_MOUSE_SCALE_UNIFORM: + reason = _("Scale nodes uniformly"); + break; + case COMMIT_MOUSE_SCALE: + reason = _("Scale nodes"); + break; + case COMMIT_KEYBOARD_SCALE_UNIFORM: + reason = _("Scale nodes uniformly"); + key = "node:scale:uniform"; + break; + case COMMIT_KEYBOARD_SCALE_X: + reason = _("Scale nodes horizontally"); + key = "node:scale:x"; + break; + case COMMIT_KEYBOARD_SCALE_Y: + reason = _("Scale nodes vertically"); + key = "node:scale:y"; + break; + case COMMIT_FLIP_X: + reason = _("Flip nodes horizontally"); + break; + case COMMIT_FLIP_Y: + reason = _("Flip nodes vertically"); + break; + default: return; + } + + _selection.signal_update.emit(); + invokeForAll(&PathManipulator::writeXML); + if (key) { + sp_document_maybe_done(sp_desktop_document(_desktop), key, SP_VERB_CONTEXT_NODE, reason); + } else { + sp_document_done(sp_desktop_document(_desktop), SP_VERB_CONTEXT_NODE, reason); + } + signal_coords_changed.emit(); +} + +/** Commits changes to XML and adds undo stack entry. */ +void MultiPathManipulator::_done(gchar const *reason) { + invokeForAll(&PathManipulator::update); + invokeForAll(&PathManipulator::writeXML); + sp_document_done(sp_desktop_document(_desktop), SP_VERB_CONTEXT_NODE, reason); + signal_coords_changed.emit(); +} + +/** Commits changes to XML, adds undo stack entry and removes empty manipulators. */ +void MultiPathManipulator::_doneWithCleanup(gchar const *reason) { + _changed.block(); + _done(reason); + cleanup(); + _changed.unblock(); +} + +} // 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 a79eab7e518e7c1b3540075552ecb3e7aa62b0df Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Sat, 5 Dec 2009 03:48:07 +0100 Subject: Fix mask editing behavior on undo and outline display for masks/clips; prepare to fix LPE path parameters (bzr r8846.2.2) --- src/ui/tool/multi-path-manipulator.cpp | 95 ++++++++++++++++++++++++---------- 1 file changed, 67 insertions(+), 28 deletions(-) (limited to 'src/ui/tool/multi-path-manipulator.cpp') diff --git a/src/ui/tool/multi-path-manipulator.cpp b/src/ui/tool/multi-path-manipulator.cpp index 6b245702a..7c539f6b9 100644 --- a/src/ui/tool/multi-path-manipulator.cpp +++ b/src/ui/tool/multi-path-manipulator.cpp @@ -16,6 +16,7 @@ #include "desktop-handles.h" #include "document.h" #include "message-stack.h" +#include "preferences.h" #include "sp-path.h" #include "ui/tool/control-point-selection.h" #include "ui/tool/event-utils.h" @@ -130,36 +131,48 @@ void MultiPathManipulator::cleanup() } } -void MultiPathManipulator::setItems(std::map > const &items) +void MultiPathManipulator::setItems(std::set const &s) { - typedef std::map > TransMap; - typedef std::set ItemSet; - ItemSet to_remove, to_add, current, new_items; - - for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) { - current.insert(i->first); - } - for (TransMap::const_iterator i = items.begin(); i != items.end(); ++i) { - new_items.insert(i->first); + std::set shapes(s); + + // iterate over currently edited items, modifying / removing them as necessary + for (MapType::iterator i = _mmap.begin(); i != _mmap.end();) { + std::set::iterator si = shapes.find(i->first); + if (si == shapes.end()) { + // This item is no longer supposed to be edited - remove its manipulator + _mmap.erase(i++); + } else { + ShapeRecord const &sr = i->first; + ShapeRecord const &sr_new = *si; + // if the shape record differs, replace the key only and modify other values + if (sr.edit_transform != sr_new.edit_transform || + sr.role != sr_new.role) + { + boost::shared_ptr hold(i->second); + if (sr.edit_transform != sr_new.edit_transform) + hold->setControlsTransform(sr_new.edit_transform); + if (sr.role != sr_new.role) { + //hold->setOutlineColor(_getOutlineColor(sr_new.role)); + } + _mmap.erase(sr); + _mmap.insert(std::make_pair(sr_new, hold)); + } + shapes.erase(si); // remove the processed record + ++i; + } } - std::set_difference(current.begin(), current.end(), new_items.begin(), new_items.end(), - std::inserter(to_remove, to_remove.end())); - std::set_difference(new_items.begin(), new_items.end(), current.begin(), current.end(), - std::inserter(to_add, to_add.end())); - - for (ItemSet::iterator i = to_remove.begin(); i != to_remove.end(); ++i) { - _mmap.erase(*i); - } - for (ItemSet::iterator i = to_add.begin(); i != to_add.end(); ++i) { - boost::shared_ptr pm; - TransMap::const_iterator f = items.find(*i); - pm.reset(new PathManipulator(_path_data, *i, f->second.first, f->second.second)); - pm->showHandles(_show_handles); - pm->showOutline(_show_outline); - pm->showPathDirection(_show_path_direction); - _mmap.insert(std::make_pair(*i, pm)); + // add newly selected items + for (std::set::iterator i = shapes.begin(); i != shapes.end(); ++i) { + ShapeRecord const &r = *i; + if (!SP_IS_PATH(r.item)) continue; + boost::shared_ptr newpm(new PathManipulator(_path_data, (SPPath*) r.item, + r.edit_transform, _getOutlineColor(r.role))); + newpm->showHandles(_show_handles); + // always show outlines for clips and masks + newpm->showOutline(_show_outline || r.role != SHAPE_ROLE_NORMAL); + newpm->showPathDirection(_show_path_direction); + _mmap.insert(std::make_pair(r, newpm)); } } @@ -369,7 +382,10 @@ void MultiPathManipulator::move(Geom::Point const &delta) void MultiPathManipulator::showOutline(bool show) { - invokeForAll(&PathManipulator::showOutline, show); + for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) { + // always show outlines for clipping paths and masks + i->second->showOutline(show || i->first.role != SHAPE_ROLE_NORMAL); + } _show_outline = show; } @@ -385,6 +401,13 @@ void MultiPathManipulator::showPathDirection(bool show) _show_path_direction = show; } +void MultiPathManipulator::updateOutlineColors() +{ + //for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) { + // i->second->setOutlineColor(_getOutlineColor(i->first.role)); + //} +} + bool MultiPathManipulator::event(GdkEvent *event) { switch (event->type) { @@ -553,6 +576,22 @@ void MultiPathManipulator::_doneWithCleanup(gchar const *reason) { _changed.unblock(); } +guint32 MultiPathManipulator::_getOutlineColor(ShapeRole role) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + switch(role) { + case SHAPE_ROLE_CLIPPING_PATH: + return prefs->getColor("/tools/nodes/clipping_path_color", 0x00ff00ff); + case SHAPE_ROLE_MASK: + return prefs->getColor("/tools/nodes/mask_color", 0x0000ffff); + case SHAPE_ROLE_LPE_PARAM: + return prefs->getColor("/tools/nodes/lpe_param_color", 0xb700ffff); + case SHAPE_ROLE_NORMAL: + default: + return prefs->getColor("/tools/nodes/outline_color", 0xff0000ff); + } +} + } // namespace UI } // namespace Inkscape -- cgit v1.2.3 From e2b9f78d271e5fea988138d49020e704e72c83b1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Tue, 8 Dec 2009 03:21:08 +0100 Subject: Fix LPEs and break mask transform undo (bzr r8846.2.3) --- src/ui/tool/multi-path-manipulator.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src/ui/tool/multi-path-manipulator.cpp') diff --git a/src/ui/tool/multi-path-manipulator.cpp b/src/ui/tool/multi-path-manipulator.cpp index 7c539f6b9..aaf7e413c 100644 --- a/src/ui/tool/multi-path-manipulator.cpp +++ b/src/ui/tool/multi-path-manipulator.cpp @@ -15,6 +15,7 @@ #include "desktop.h" #include "desktop-handles.h" #include "document.h" +#include "live_effects/lpeobject.h" #include "message-stack.h" #include "preferences.h" #include "sp-path.h" @@ -131,6 +132,9 @@ void MultiPathManipulator::cleanup() } } +/** @brief Change the set of items to edit. + * + * This method attempts to preserve as much of the state as possible. */ void MultiPathManipulator::setItems(std::set const &s) { std::set shapes(s); @@ -165,9 +169,9 @@ void MultiPathManipulator::setItems(std::set const &s) // add newly selected items for (std::set::iterator i = shapes.begin(); i != shapes.end(); ++i) { ShapeRecord const &r = *i; - if (!SP_IS_PATH(r.item)) continue; + if (!SP_IS_PATH(r.item) && !IS_LIVEPATHEFFECT(r.item)) continue; boost::shared_ptr newpm(new PathManipulator(_path_data, (SPPath*) r.item, - r.edit_transform, _getOutlineColor(r.role))); + r.edit_transform, _getOutlineColor(r.role), r.lpe_key)); newpm->showHandles(_show_handles); // always show outlines for clips and masks newpm->showOutline(_show_outline || r.role != SHAPE_ROLE_NORMAL); @@ -241,6 +245,7 @@ void MultiPathManipulator::insertNodes() void MultiPathManipulator::joinNodes() { + invokeForAll(&PathManipulator::hideDragPoint); // Node join has two parts. In the first one we join two subpaths by fusing endpoints // into one. In the second we fuse nodes in each subpath. IterPairList joins; @@ -585,7 +590,7 @@ guint32 MultiPathManipulator::_getOutlineColor(ShapeRole role) case SHAPE_ROLE_MASK: return prefs->getColor("/tools/nodes/mask_color", 0x0000ffff); case SHAPE_ROLE_LPE_PARAM: - return prefs->getColor("/tools/nodes/lpe_param_color", 0xb700ffff); + return prefs->getColor("/tools/nodes/lpe_param_color", 0x009000ff); case SHAPE_ROLE_NORMAL: default: return prefs->getColor("/tools/nodes/outline_color", 0xff0000ff); -- cgit v1.2.3 From 1075267dd1ba150a82b7e1aad543fbf0a69a1c00 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Sat, 26 Dec 2009 04:13:01 +0100 Subject: Implement selection spatial grow (bzr r8846.2.7) --- src/ui/tool/multi-path-manipulator.cpp | 46 +++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 6 deletions(-) (limited to 'src/ui/tool/multi-path-manipulator.cpp') diff --git a/src/ui/tool/multi-path-manipulator.cpp b/src/ui/tool/multi-path-manipulator.cpp index aaf7e413c..ac0165e1a 100644 --- a/src/ui/tool/multi-path-manipulator.cpp +++ b/src/ui/tool/multi-path-manipulator.cpp @@ -37,8 +37,7 @@ typedef std::unordered_set IterSet; typedef std::multimap DistanceMap; typedef std::pair DistanceMapItem; -/** Find two selected endnodes. - * @returns -1 if not enough endnodes selected, 1 if too many, 0 if OK */ +/** Find pairs of selected endnodes suitable for joining. */ void find_join_iterators(ControlPointSelection &sel, IterPairList &pairs) { IterSet join_iters; @@ -105,12 +104,11 @@ bool prepare_join(IterPair &join_iters) } // anonymous namespace -MultiPathManipulator::MultiPathManipulator(PathSharedData const &data, sigc::connection &chg) +MultiPathManipulator::MultiPathManipulator(PathSharedData &data, sigc::connection &chg) : PointManipulator(data.node_data.desktop, *data.node_data.selection) , _path_data(data) , _changed(chg) { - // _selection.signal_commit.connect( sigc::mem_fun(*this, &MultiPathManipulator::_commit)); _selection.signal_point_changed.connect( @@ -170,7 +168,7 @@ void MultiPathManipulator::setItems(std::set const &s) for (std::set::iterator i = shapes.begin(); i != shapes.end(); ++i) { ShapeRecord const &r = *i; if (!SP_IS_PATH(r.item) && !IS_LIVEPATHEFFECT(r.item)) continue; - boost::shared_ptr newpm(new PathManipulator(_path_data, (SPPath*) r.item, + boost::shared_ptr newpm(new PathManipulator(*this, (SPPath*) r.item, r.edit_transform, _getOutlineColor(r.role), r.lpe_key)); newpm->showHandles(_show_handles); // always show outlines for clips and masks @@ -203,6 +201,39 @@ void MultiPathManipulator::shiftSelection(int dir) { invokeForAll(&PathManipulator::shiftSelection, dir); } +void MultiPathManipulator::spatialGrow(NodeList::iterator origin, int dir) +{ + double extr_dist = dir > 0 ? HUGE_VAL : -HUGE_VAL; + NodeList::iterator target; + + do { // this substitutes for goto + if ((dir > 0 && !origin->selected())) { + target = origin; + break; + } + + bool closest = dir > 0; // when growing, find closest node + bool selected = dir < 0; // when growing, consider only unselected nodes + + for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) { + NodeList::iterator t = i->second->extremeNode(origin, selected, !selected, closest); + if (!t) continue; + double dist = Geom::distance(*t, *origin); + bool cond = closest ? (dist < extr_dist) : (dist > extr_dist); + if (cond) { + extr_dist = dist; + target = t; + } + } + } while (0); + + if (!target) return; + if (dir > 0) { + _selection.insert(target.ptr()); + } else { + _selection.erase(target.ptr()); + } +} void MultiPathManipulator::invertSelection() { invokeForAll(&PathManipulator::invertSelection); @@ -343,7 +374,7 @@ void MultiPathManipulator::joinSegment() } } - _doneWithCleanup("Join segment"); + _doneWithCleanup("Join segments"); } void MultiPathManipulator::deleteSegments() @@ -505,6 +536,8 @@ bool MultiPathManipulator::event(GdkEvent *event) return false; } +/** Commit changes to XML and add undo stack entry based on the action that was done. Invoked + * by sub-manipulators, for example TransformHandleSet and ControlPointSelection. */ void MultiPathManipulator::_commit(CommitEvent cps) { gchar const *reason = NULL; @@ -581,6 +614,7 @@ void MultiPathManipulator::_doneWithCleanup(gchar const *reason) { _changed.unblock(); } +/** Get an outline color based on the shape's role (normal, mask, LPE parameter, etc.). */ guint32 MultiPathManipulator::_getOutlineColor(ShapeRole role) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); -- 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/multi-path-manipulator.cpp | 52 +--------------------------------- 1 file changed, 1 insertion(+), 51 deletions(-) (limited to 'src/ui/tool/multi-path-manipulator.cpp') diff --git a/src/ui/tool/multi-path-manipulator.cpp b/src/ui/tool/multi-path-manipulator.cpp index ac0165e1a..33d96c706 100644 --- a/src/ui/tool/multi-path-manipulator.cpp +++ b/src/ui/tool/multi-path-manipulator.cpp @@ -181,71 +181,21 @@ void MultiPathManipulator::setItems(std::set const &s) void MultiPathManipulator::selectSubpaths() { if (_selection.empty()) { - invokeForAll(&PathManipulator::selectAll); + _selection.selectAll(); } else { invokeForAll(&PathManipulator::selectSubpaths); } } -void MultiPathManipulator::selectAll() -{ - invokeForAll(&PathManipulator::selectAll); -} - -void MultiPathManipulator::selectArea(Geom::Rect const &area, bool take) -{ - if (take) _selection.clear(); - invokeForAll(&PathManipulator::selectArea, area); -} void MultiPathManipulator::shiftSelection(int dir) { invokeForAll(&PathManipulator::shiftSelection, dir); } -void MultiPathManipulator::spatialGrow(NodeList::iterator origin, int dir) -{ - double extr_dist = dir > 0 ? HUGE_VAL : -HUGE_VAL; - NodeList::iterator target; - do { // this substitutes for goto - if ((dir > 0 && !origin->selected())) { - target = origin; - break; - } - - bool closest = dir > 0; // when growing, find closest node - bool selected = dir < 0; // when growing, consider only unselected nodes - - for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) { - NodeList::iterator t = i->second->extremeNode(origin, selected, !selected, closest); - if (!t) continue; - double dist = Geom::distance(*t, *origin); - bool cond = closest ? (dist < extr_dist) : (dist > extr_dist); - if (cond) { - extr_dist = dist; - target = t; - } - } - } while (0); - - if (!target) return; - if (dir > 0) { - _selection.insert(target.ptr()); - } else { - _selection.erase(target.ptr()); - } -} -void MultiPathManipulator::invertSelection() -{ - invokeForAll(&PathManipulator::invertSelection); -} void MultiPathManipulator::invertSelectionInSubpaths() { invokeForAll(&PathManipulator::invertSelectionInSubpaths); } -void MultiPathManipulator::deselect() -{ - _selection.clear(); -} void MultiPathManipulator::setNodeType(NodeType type) { -- cgit v1.2.3 From dd3076a51d8a53223c771a39fa5f976db0c85af5 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Thu, 14 Jan 2010 09:42:20 +0100 Subject: Implement segment weld to make segment join similar to node join (bzr r8846.2.12) --- src/ui/tool/multi-path-manipulator.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'src/ui/tool/multi-path-manipulator.cpp') diff --git a/src/ui/tool/multi-path-manipulator.cpp b/src/ui/tool/multi-path-manipulator.cpp index 2cc9bc97b..3ae7e4d24 100644 --- a/src/ui/tool/multi-path-manipulator.cpp +++ b/src/ui/tool/multi-path-manipulator.cpp @@ -304,15 +304,10 @@ void MultiPathManipulator::deleteNodes(bool keep_shape) } /** Join selected endpoints to create segments. */ -void MultiPathManipulator::joinSegment() +void MultiPathManipulator::joinSegments() { IterPairList joins; find_join_iterators(_selection, joins); - if (joins.empty()) { - _desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, - _("There must be at least 2 endnodes in selection")); - return; - } for (IterPairList::iterator i = joins.begin(); i != joins.end(); ++i) { bool same_path = prepare_join(*i); @@ -328,6 +323,9 @@ void MultiPathManipulator::joinSegment() } } + if (joins.empty()) { + invokeForAll(&PathManipulator::weldSegments); + } _doneWithCleanup("Join segments"); } @@ -421,7 +419,7 @@ bool MultiPathManipulator::event(GdkEvent *event) return true; } if (held_only_alt(event->key)) { - joinSegment(); + joinSegments(); return true; } break; -- cgit v1.2.3