diff options
| author | Jabier Arraiza Cenoz <jabier.arraiza@marker.es> | 2014-11-23 23:36:49 +0000 |
|---|---|---|
| committer | Jabiertxof <jtx@jtx.marker.es> | 2014-11-23 23:36:49 +0000 |
| commit | 0969085ddf607a7a98cf7fd6d9b10da5fbebe62d (patch) | |
| tree | 59b2bc9ed3412ab2de4c703ef30342dfe2401704 /src/ui/tool | |
| parent | refactor from lastApplied (diff) | |
| parent | Fixed a bug pointed by suv running from comand line, also removed another des... (diff) | |
| download | inkscape-0969085ddf607a7a98cf7fd6d9b10da5fbebe62d.tar.gz inkscape-0969085ddf607a7a98cf7fd6d9b10da5fbebe62d.zip | |
fixing to trunk
(bzr r12588.1.34)
Diffstat (limited to 'src/ui/tool')
| -rw-r--r-- | src/ui/tool/control-point-selection.cpp | 49 | ||||
| -rw-r--r-- | src/ui/tool/control-point-selection.h | 13 | ||||
| -rw-r--r-- | src/ui/tool/curve-drag-point.cpp | 17 | ||||
| -rw-r--r-- | src/ui/tool/multi-path-manipulator.cpp | 21 | ||||
| -rw-r--r-- | src/ui/tool/multi-path-manipulator.h | 5 | ||||
| -rw-r--r-- | src/ui/tool/node.cpp | 248 | ||||
| -rw-r--r-- | src/ui/tool/node.h | 15 | ||||
| -rw-r--r-- | src/ui/tool/path-manipulator.cpp | 170 | ||||
| -rw-r--r-- | src/ui/tool/path-manipulator.h | 12 | ||||
| -rw-r--r-- | src/ui/tool/selectable-control-point.h | 1 |
10 files changed, 493 insertions, 58 deletions
diff --git a/src/ui/tool/control-point-selection.cpp b/src/ui/tool/control-point-selection.cpp index d10ed0f0d..998f74ee0 100644 --- a/src/ui/tool/control-point-selection.cpp +++ b/src/ui/tool/control-point-selection.cpp @@ -74,7 +74,7 @@ ControlPointSelection::~ControlPointSelection() } /** Add a control point to the selection. */ -std::pair<ControlPointSelection::iterator, bool> ControlPointSelection::insert(const value_type &x) +std::pair<ControlPointSelection::iterator, bool> ControlPointSelection::insert(const value_type &x, bool notify) { iterator found = _points.find(x); if (found != _points.end()) { @@ -86,6 +86,10 @@ std::pair<ControlPointSelection::iterator, bool> ControlPointSelection::insert(c x->updateState(); _pointChanged(x, true); + if (notify) { + signal_selection_changed.emit(std::vector<key_type>(1, x), true); + } + return std::pair<iterator, bool>(found, true); } @@ -97,47 +101,75 @@ void ControlPointSelection::erase(iterator pos) erased->updateState(); _pointChanged(erased, false); } -ControlPointSelection::size_type ControlPointSelection::erase(const key_type &k) +ControlPointSelection::size_type ControlPointSelection::erase(const key_type &k, bool notify) { iterator pos = _points.find(k); if (pos == _points.end()) return 0; erase(pos); + + if (notify) { + signal_selection_changed.emit(std::vector<key_type>(1, k), false); + } return 1; } void ControlPointSelection::erase(iterator first, iterator last) { + std::vector<SelectableControlPoint *> out(first, last); while (first != last) erase(first++); + signal_selection_changed.emit(out, false); } /** Remove all points from the selection, making it empty. */ void ControlPointSelection::clear() { + std::vector<SelectableControlPoint *> out(begin(), end()); for (iterator i = begin(); i != end(); ) erase(i++); + if (!out.empty()) + signal_selection_changed.emit(out, false); } /** 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); + insert(*i, false); } + std::vector<SelectableControlPoint *> out(_all_points.begin(), _all_points.end()); + if (!out.empty()) + signal_selection_changed.emit(out, true); } /** Select all points inside the given rectangle (in desktop coordinates). */ void ControlPointSelection::selectArea(Geom::Rect const &r) { + std::vector<SelectableControlPoint *> out; for (set_type::iterator i = _all_points.begin(); i != _all_points.end(); ++i) { - if (r.contains(**i)) - insert(*i); + if (r.contains(**i)) { + insert(*i, false); + out.push_back(*i); + } } + if (!out.empty()) + signal_selection_changed.emit(out, true); } /** Unselect all selected points and select all unselected points. */ void ControlPointSelection::invertSelection() { + std::vector<SelectableControlPoint *> in, out; for (set_type::iterator i = _all_points.begin(); i != _all_points.end(); ++i) { - if ((*i)->selected()) erase(*i); - else insert(*i); + if ((*i)->selected()) { + in.push_back(*i); + erase(*i); + } + else { + out.push_back(*i); + insert(*i, false); + } } + if (!in.empty()) + signal_selection_changed.emit(in, false); + if (!out.empty()) + signal_selection_changed.emit(out, true); } void ControlPointSelection::spatialGrow(SelectableControlPoint *origin, int dir) { @@ -166,6 +198,7 @@ void ControlPointSelection::spatialGrow(SelectableControlPoint *origin, int dir) if (match) { if (grow) insert(match); else erase(match); + signal_selection_changed.emit(std::vector<value_type>(1, match), grow); } } @@ -389,7 +422,7 @@ void ControlPointSelection::_pointChanged(SelectableControlPoint *p, bool select _handles->rotationCenter().move(_bounds->midpoint()); } - signal_point_changed.emit(p, selected); + //signal_point_changed.emit(p, selected); } void ControlPointSelection::_mouseoverChanged() diff --git a/src/ui/tool/control-point-selection.h b/src/ui/tool/control-point-selection.h index a087e0455..2d812c0a3 100644 --- a/src/ui/tool/control-point-selection.h +++ b/src/ui/tool/control-point-selection.h @@ -62,18 +62,19 @@ public: const_iterator end() const { return _points.end(); } // insert - std::pair<iterator, bool> insert(const value_type& x); + std::pair<iterator, bool> insert(const value_type& x, bool notify = true); template <class InputIterator> void insert(InputIterator first, InputIterator last) { for (; first != last; ++first) { - insert(*first); + insert(*first, false); } + signal_selection_changed.emit(std::vector<key_type>(first, last), true); } // erase void clear(); void erase(iterator pos); - size_type erase(const key_type& k); + size_type erase(const key_type& k, bool notify = true); void erase(iterator first, iterator last); // find @@ -108,7 +109,9 @@ public: void toggleTransformHandlesMode(); sigc::signal<void> signal_update; - sigc::signal<void, SelectableControlPoint *, bool> signal_point_changed; + // It turns out that emitting a signal after every point is selected or deselected is not too efficient, + // so this can be done in a massive group once the selection is finally changed. + sigc::signal<void, std::vector<SelectableControlPoint *>, bool> signal_selection_changed; sigc::signal<void, CommitEvent> signal_commit; void getOriginalPoints(std::vector<Inkscape::SnapCandidatePoint> &pts); @@ -166,4 +169,4 @@ private: fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8 : diff --git a/src/ui/tool/curve-drag-point.cpp b/src/ui/tool/curve-drag-point.cpp index 4ca736f80..013553410 100644 --- a/src/ui/tool/curve-drag-point.cpp +++ b/src/ui/tool/curve-drag-point.cpp @@ -53,9 +53,11 @@ bool CurveDragPoint::grabbed(GdkEventMotion */*event*/) // delta is a vector equal 1/3 of distance from first to second Geom::Point delta = (second->position() - first->position()) / 3.0; - first->front()->move(first->front()->position() + delta); - second->back()->move(second->back()->position() - delta); - + // only update the nodes if the mode is bspline + if(!_pm.isBSpline(false)){ + first->front()->move(first->front()->position() + delta); + second->back()->move(second->back()->position() - delta); + } _pm.update(); } else { _segment_was_degenerate = false; @@ -88,9 +90,12 @@ void CurveDragPoint::dragged(Geom::Point &new_pos, GdkEventMotion *event) Geom::Point offset0 = ((1-weight)/(3*t*(1-t)*(1-t))) * delta; Geom::Point offset1 = (weight/(3*t*t*(1-t))) * delta; - first->front()->move(first->front()->position() + offset0); - second->back()->move(second->back()->position() + offset1); - + //modified so that, if the trace is bspline, it only acts if the SHIFT key is pressed + if(!_pm.isBSpline(false)){ + first->front()->move(first->front()->position() + offset0); + second->back()->move(second->back()->position() + offset1); + }else if(weight>=0.8 && held_shift(*event))second->back()->move(new_pos); + else if(weight<=0.2 && held_shift(*event))first->front()->move(new_pos); _pm.update(); } diff --git a/src/ui/tool/multi-path-manipulator.cpp b/src/ui/tool/multi-path-manipulator.cpp index 65987ad52..d7b35c974 100644 --- a/src/ui/tool/multi-path-manipulator.cpp +++ b/src/ui/tool/multi-path-manipulator.cpp @@ -122,7 +122,7 @@ MultiPathManipulator::MultiPathManipulator(PathSharedData &data, sigc::connectio { _selection.signal_commit.connect( sigc::mem_fun(*this, &MultiPathManipulator::_commit)); - _selection.signal_point_changed.connect( + _selection.signal_selection_changed.connect( sigc::hide( sigc::hide( signal_coords_changed.make_slot()))); } @@ -182,7 +182,7 @@ void MultiPathManipulator::setItems(std::set<ShapeRecord> const &s) ShapeRecord const &r = *i; if (!SP_IS_PATH(r.item) && !IS_LIVEPATHEFFECT(r.item)) continue; boost::shared_ptr<PathManipulator> newpm(new PathManipulator(*this, (SPPath*) r.item, - r.edit_transform, _getOutlineColor(r.role), r.lpe_key)); + r.edit_transform, _getOutlineColor(r.role, r.item), r.lpe_key)); newpm->showHandles(_show_handles); // always show outlines for clips and masks newpm->showOutline(_show_outline || r.role != SHAPE_ROLE_NORMAL); @@ -670,7 +670,18 @@ bool MultiPathManipulator::event(Inkscape::UI::Tools::ToolBase *event_context, G // a) del preserves shape, and control is not pressed // b) ctrl+del preserves shape (del_preserves_shape is false), and control is pressed // Hence xor - deleteNodes(del_preserves_shape ^ held_control(event->key)); + guint mode = prefs->getInt("/tools/freehand/pen/freehand-mode", 0); + + //if the trace is bspline ( mode 2) + if(mode==2){ + // is this correct ? + if(del_preserves_shape ^ held_control(event->key)) + deleteNodes(false); + else + deleteNodes(true); + } + else + deleteNodes(del_preserves_shape ^ held_control(event->key)); // Delete any selected gradient nodes as well event_context->deleteSelectedDrag(held_control(event->key)); @@ -833,7 +844,7 @@ void MultiPathManipulator::_doneWithCleanup(gchar const *reason, bool alert_LPE) } /** Get an outline color based on the shape's role (normal, mask, LPE parameter, etc.). */ -guint32 MultiPathManipulator::_getOutlineColor(ShapeRole role) +guint32 MultiPathManipulator::_getOutlineColor(ShapeRole role, SPItem *item) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); switch(role) { @@ -845,7 +856,7 @@ guint32 MultiPathManipulator::_getOutlineColor(ShapeRole role) return prefs->getColor("/tools/nodes/lpe_param_color", 0x009000ff); case SHAPE_ROLE_NORMAL: default: - return prefs->getColor("/tools/nodes/outline_color", 0xff0000ff); + return item->highlight_color(); } } diff --git a/src/ui/tool/multi-path-manipulator.h b/src/ui/tool/multi-path-manipulator.h index 1328372c6..cdbf34e9d 100644 --- a/src/ui/tool/multi-path-manipulator.h +++ b/src/ui/tool/multi-path-manipulator.h @@ -53,6 +53,7 @@ public: void insertNodesAtExtrema(ExtremumType extremum); void insertNodes(); + void alertLPE(); void duplicateNodes(); void joinNodes(); void breakNodes(); @@ -104,9 +105,9 @@ private: } void _commit(CommitEvent cps); - void _done(gchar const *reason, bool alert_LPE = false); + void _done(gchar const *reason, bool alert_LPE = true); void _doneWithCleanup(gchar const *reason, bool alert_LPE = false); - guint32 _getOutlineColor(ShapeRole role); + guint32 _getOutlineColor(ShapeRole role, SPItem *item); MapType _mmap; public: diff --git a/src/ui/tool/node.cpp b/src/ui/tool/node.cpp index fbbc4be64..8a1ca0b90 100644 --- a/src/ui/tool/node.cpp +++ b/src/ui/tool/node.cpp @@ -13,7 +13,6 @@ #include <glib/gi18n.h> #include <2geom/bezier-utils.h> #include <2geom/transforms.h> - #include "display/sp-ctrlline.h" #include "display/sp-canvas.h" #include "display/sp-canvas-util.h" @@ -28,7 +27,10 @@ #include "ui/tool/event-utils.h" #include "ui/tool/node.h" #include "ui/tool/path-manipulator.h" +#include "ui/tools/node-tool.h" +#include "ui/tools-switch.h" #include <gdk/gdkkeysyms.h> +#include <cmath> namespace { @@ -58,6 +60,11 @@ Inkscape::ControlType nodeTypeToCtrlType(Inkscape::UI::NodeType type) namespace Inkscape { namespace UI { +/*const double handleCubicGap = 0.01;*/ +const double noPower = 0.0; +const double defaultStartPower = 0.3334; +/*const double defaultEndPower = 0.6667;*/ + ControlPoint::ColorSet Node::node_colors = { {0xbfbfbf00, 0x000000ff}, // normal fill, stroke {0xff000000, 0x000000ff}, // mouseover fill, stroke @@ -166,6 +173,12 @@ void Handle::move(Geom::Point const &new_pos) } } setPosition(new_pos); + + //move the handler and its oposite the same proportion + if(_pm().isBSpline()){ + setPosition(_pm().BSplineHandleReposition(this,this)); + this->other()->setPosition(_pm().BSplineHandleReposition(this->other(),this)); + } return; } @@ -177,6 +190,13 @@ void Handle::move(Geom::Point const &new_pos) Geom::Point new_delta = (Geom::dot(delta, direction) / Geom::L2sq(direction)) * direction; setRelativePos(new_delta); + + //move the handler and its oposite the same proportion + if(_pm().isBSpline()){ + setPosition(_pm().BSplineHandleReposition(this,this)); + this->other()->setPosition(_pm().BSplineHandleReposition(this->other(),this)); + } + return; } @@ -195,8 +215,14 @@ void Handle::move(Geom::Point const &new_pos) break; default: break; } - setPosition(new_pos); + + // moves the handler and its oposite the same proportion + if(_pm().isBSpline()){ + setPosition(_pm().BSplineHandleReposition(this,this)); + this->other()->setPosition(_pm().BSplineHandleReposition(this->other(),this)); + } + } void Handle::setPosition(Geom::Point const &p) @@ -273,12 +299,27 @@ bool Handle::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEven break; default: break; } + break; + // new double click event to set the handlers of a node to the default proportion, defaultStartPower% + case GDK_2BUTTON_PRESS: + handle_2button_press(); + break; + default: break; } return ControlPoint::_eventHandler(event_context, event); } +//this function moves the handler and its oposite to the default proportion of defaultStartPower +void Handle::handle_2button_press(){ + if(_pm().isBSpline()){ + setPosition(_pm().BSplineHandleReposition(this,defaultStartPower)); + this->other()->setPosition(_pm().BSplineHandleReposition(this->other(),defaultStartPower)); + _pm().update(); + } +} + bool Handle::grabbed(GdkEventMotion *) { _saved_other_pos = other()->position(); @@ -289,6 +330,10 @@ bool Handle::grabbed(GdkEventMotion *) void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event) { + if (tools_isactive(_desktop, TOOLS_NODES)) { + Inkscape::UI::Tools::NodeTool *nt = static_cast<Inkscape::UI::Tools::NodeTool*>(_desktop->event_context); + nt->update_helperpath(); + } Geom::Point parent_pos = _parent->position(); Geom::Point origin = _last_drag_origin(); SnapManager &sm = _desktop->namedview->snap_manager; @@ -326,10 +371,18 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event) ctrl_constraint = Inkscape::Snapper::SnapConstraint(parent_pos, parent_pos - perp_pos); } new_pos = result; + // moves the handler and its oposite in X fixed positions depending on parameter "steps with control" + // by default in live BSpline + if(_pm().isBSpline()){ + setPosition(new_pos); + int steps = _pm().BSplineGetSteps(); + new_pos=_pm().BSplineHandleReposition(this,ceilf(_pm().BSplineHandlePosition(this,this)*steps)/steps); + } } std::vector<Inkscape::SnapCandidatePoint> unselected; - if (snap) { + //if the snap adjustment is activated and it is not bspline + if (snap && !_pm().isBSpline(false)) { ControlPointSelection::Set &nodes = _parent->_selection.allPoints(); for (ControlPointSelection::Set::iterator i = nodes.begin(); i != nodes.end(); ++i) { Node *n = static_cast<Node*>(*i); @@ -369,6 +422,10 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event) other()->setPosition(_saved_other_pos); } } + //if it is bspline but SHIFT or CONTROL are not pressed it fixes it in the original position + if(_pm().isBSpline() && !held_shift(*event) && !held_control(*event)){ + new_pos=_last_drag_origin(); + } move(new_pos); // needed for correct update, even though it's redundant _pm().update(); } @@ -427,13 +484,19 @@ static double snap_increment_degrees() { Glib::ustring Handle::_getTip(unsigned state) const { char const *more; + // a trick to mark as bspline if the node has no strength, we are going to use it later + // to show the appropiate messages. We cannot do it in any different way becasue the function is constant + + bool isBSpline = _pm().isBSpline(); bool can_shift_rotate = _parent->type() == NODE_CUSP && !other()->isDegenerate(); - if (can_shift_rotate) { + if (can_shift_rotate && !isBSpline) { more = C_("Path handle tip", "more: Shift, Ctrl, Alt"); - } else { + } else if(isBSpline){ + more = C_("Path handle tip", "more: Ctrl"); + }else { more = C_("Path handle tip", "more: Ctrl, Alt"); } - if (state_held_alt(state)) { + if (state_held_alt(state) && !isBSpline) { if (state_held_control(state)) { if (state_held_shift(state) && can_shift_rotate) { return format_tip(C_("Path handle tip", @@ -456,18 +519,24 @@ Glib::ustring Handle::_getTip(unsigned state) const } } else { if (state_held_control(state)) { - if (state_held_shift(state) && can_shift_rotate) { + if (state_held_shift(state) && can_shift_rotate && !isBSpline) { return format_tip(C_("Path handle tip", "<b>Shift+Ctrl</b>: snap rotation angle to %g° increments and rotate both handles"), snap_increment_degrees()); - } else { + } else if(isBSpline){ + return format_tip(C_("Path handle tip", + "<b>Ctrl</b>: Move handle by his actual steps in BSpline Live Effect")); + }else{ return format_tip(C_("Path handle tip", "<b>Ctrl</b>: snap rotation angle to %g° increments, click to retract"), snap_increment_degrees()); } - } else if (state_held_shift(state) && can_shift_rotate) { + } else if (state_held_shift(state) && can_shift_rotate && !isBSpline) { return C_("Path hande tip", "<b>Shift</b>: rotate both handles by the same angle"); + } else if(state_held_shift(state) && isBSpline){ + return C_("Path hande tip", + "<b>Shift</b>: move handle"); } } @@ -476,9 +545,13 @@ Glib::ustring Handle::_getTip(unsigned state) const return format_tip(C_("Path handle tip", "<b>Auto node handle</b>: drag to convert to smooth node (%s)"), more); default: - return format_tip(C_("Path handle tip", - "<b>%s</b>: drag to shape the segment (%s)"), - handle_type_to_localized_string(_parent->type()), more); + if(!isBSpline){ + return format_tip(C_("Path handle tip", + "<b>Auto node handle</b>: drag to convert to smooth node (%s)"), more); + }else{ + return format_tip(C_("Path handle tip", + "<b>BSpline node handle</b>: Shift to drag, double click to reset (%s)"), more); + } } } @@ -553,18 +626,80 @@ void Node::move(Geom::Point const &new_pos) // move handles when the node moves. Geom::Point old_pos = position(); Geom::Point delta = new_pos - position(); + + // save the previous nodes strength to apply it again once the node is moved + double nodeWeight = noPower; + double nextNodeWeight = noPower; + double prevNodeWeight = noPower; + Node *n = this; + Node * nextNode = n->nodeToward(n->front()); + Node * prevNode = n->nodeToward(n->back()); + nodeWeight = fmax(_pm().BSplineHandlePosition(n->front()),_pm().BSplineHandlePosition(n->back())); + if(prevNode){ + if(prevNode->isEndNode()){ + prevNodeWeight = _pm().BSplineHandlePosition(prevNode->front(),prevNode->front()); + } + } + if(nextNode){ + if(nextNode->isEndNode()){ + nextNodeWeight = _pm().BSplineHandlePosition(nextNode->back(),nextNode->back()); + } + } + setPosition(new_pos); + _front.setPosition(_front.position() + delta); _back.setPosition(_back.position() + delta); // if the node has a smooth handle after a line segment, it should be kept colinear // with the segment _fixNeighbors(old_pos, new_pos); + + // move the affected handlers. First the node ones, later the adjoining ones. + if(_pm().isBSpline()){ + _front.setPosition(_pm().BSplineHandleReposition(this->front(),nodeWeight)); + _back.setPosition(_pm().BSplineHandleReposition(this->back(),nodeWeight)); + if(prevNode){ + if(prevNode->isEndNode()){ + prevNode->front()->setPosition(_pm().BSplineHandleReposition(prevNode->front(),prevNodeWeight)); + }else{ + prevNode->front()->setPosition(_pm().BSplineHandleReposition(prevNode->front(),prevNode->back())); + } + } + if(nextNode){ + if(nextNode->isEndNode()){ + nextNode->back()->setPosition(_pm().BSplineHandleReposition(nextNode->back(),nextNodeWeight)); + }else{ + nextNode->back()->setPosition(_pm().BSplineHandleReposition(nextNode->back(),nextNode->front())); + } + } + } } void Node::transform(Geom::Affine const &m) { + Geom::Point old_pos = position(); + + // save the previous nodes strength to apply it again once the node is moved + double nodeWeight = noPower; + double nextNodeWeight = noPower; + double prevNodeWeight = noPower; + Node *n = this; + Node * nextNode = n->nodeToward(n->front()); + Node * prevNode = n->nodeToward(n->back()); + nodeWeight = _pm().BSplineHandlePosition(n->front()); + if(prevNode){ + if(prevNode->isEndNode()){ + prevNodeWeight = _pm().BSplineHandlePosition(prevNode->front(),prevNode->front()); + } + } + if(nextNode){ + if(nextNode->isEndNode()){ + nextNodeWeight = _pm().BSplineHandlePosition(nextNode->back(),nextNode->back()); + } + } + setPosition(position() * m); _front.setPosition(_front.position() * m); _back.setPosition(_back.position() * m); @@ -572,6 +707,26 @@ void Node::transform(Geom::Affine const &m) /* Affine transforms keep handle invariants for smooth and symmetric nodes, * but smooth nodes at ends of linear segments and auto nodes need special treatment */ _fixNeighbors(old_pos, position()); + + // move the involved handlers, first the node ones, later the adjoining ones + if(_pm().isBSpline()){ + _front.setPosition(_pm().BSplineHandleReposition(this->front(),nodeWeight)); + _back.setPosition(_pm().BSplineHandleReposition(this->back(),nodeWeight)); + if(prevNode){ + if(prevNode->isEndNode()){ + prevNode->front()->setPosition(_pm().BSplineHandleReposition(prevNode->front(),prevNodeWeight)); + }else{ + prevNode->front()->setPosition(_pm().BSplineHandleReposition(prevNode->front(),prevNode->back())); + } + } + if(nextNode){ + if(nextNode->isEndNode()){ + nextNode->back()->setPosition(_pm().BSplineHandleReposition(nextNode->back(),nextNodeWeight)); + }else{ + nextNode->back()->setPosition(_pm().BSplineHandleReposition(nextNode->back(),nextNode->front())); + } + } + } } Geom::Rect Node::bounds() const @@ -657,6 +812,7 @@ void Node::showHandles(bool v) if (!_back.isDegenerate()) { _back.setVisible(v); } + } void Node::updateHandles() @@ -758,6 +914,16 @@ void Node::setType(NodeType type, bool update_handles) break; default: break; } + /* in node type changes, about bspline traces, we can mantain them with noPower power in border mode, + or we give them the default power in curve mode */ + if(_pm().isBSpline()){ + double weight = noPower; + if(_pm().BSplineHandlePosition(this->front()) != noPower ){ + weight = defaultStartPower; + } + _front.setPosition(_pm().BSplineHandleReposition(this->front(),weight)); + _back.setPosition(_pm().BSplineHandleReposition(this->back(),weight)); + } } _type = type; _setControlType(nodeTypeToCtrlType(_type)); @@ -870,6 +1036,7 @@ bool Node::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent _selection.spatialGrow(this, dir); } return true; + default: break; } @@ -1004,6 +1171,11 @@ void Node::_setState(State state) case STATE_CLICKED: mgr.setActive(_canvas_item, true); mgr.setPrelight(_canvas_item, false); + //this shows the handlers when selecting the nodes + if(_pm().isBSpline()){ + this->front()->setPosition(_pm().BSplineHandleReposition(this->front())); + this->back()->setPosition(_pm().BSplineHandleReposition(this->back())); + } break; } SelectableControlPoint::_setState(state); @@ -1055,6 +1227,10 @@ bool Node::grabbed(GdkEventMotion *event) void Node::dragged(Geom::Point &new_pos, GdkEventMotion *event) { + if (tools_isactive(_desktop, TOOLS_NODES)) { + Inkscape::UI::Tools::NodeTool *nt = static_cast<Inkscape::UI::Tools::NodeTool*>(_desktop->event_context); + nt->update_helperpath(); + } // For a note on how snapping is implemented in Inkscape, see snap.h. SnapManager &sm = _desktop->namedview->snap_manager; // even if we won't really snap, we might still call the one of the @@ -1258,6 +1434,7 @@ Node *Node::nodeAwayFrom(Handle *h) Glib::ustring Node::_getTip(unsigned state) const { + bool isBSpline = _pm().isBSpline(); if (state_held_shift(state)) { bool can_drag_out = (_next() && _front.isDegenerate()) || (_prev() && _back.isDegenerate()); if (can_drag_out) { @@ -1287,15 +1464,24 @@ Glib::ustring Node::_getTip(unsigned state) const // No modifiers: assemble tip from node type char const *nodetype = node_type_to_localized_string(_type); if (_selection.transformHandlesEnabled() && selected()) { - if (_selection.size() == 1) { + if (_selection.size() == 1 && !isBSpline) { return format_tip(C_("Path node tip", "<b>%s</b>: drag to shape the path (more: Shift, Ctrl, Alt)"), nodetype); + }else if(_selection.size() == 1){ + return format_tip(C_("Path node tip", + "<b>BSpline node</b>: %g weight, drag to shape the path (more: Shift, Ctrl, Alt)"),noPower/*this->bsplineWeight*/); } return format_tip(C_("Path node tip", "<b>%s</b>: drag to shape the path, click to toggle scale/rotation handles (more: Shift, Ctrl, Alt)"), nodetype); } - return format_tip(C_("Path node tip", - "<b>%s</b>: drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt)"), nodetype); + if (!isBSpline) { + return format_tip(C_("Path node tip", + "<b>%s</b>: drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt)"), nodetype); + }else{ + return format_tip(C_("Path node tip", + "<b>BSpline node</b>: drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt)")); + + } } Glib::ustring Node::_getDragTip(GdkEventMotion */*event*/) const @@ -1452,7 +1638,37 @@ void NodeList::reverse() void NodeList::clear() { - for (iterator i = begin(); i != end();) erase (i++); + // ugly but more efficient clearing mechanism + std::vector<ControlPointSelection *> to_clear; + std::vector<std::pair<SelectableControlPoint *, long> > nodes; + long in = -1; + for (iterator i = begin(); i != end(); ++i) { + SelectableControlPoint *rm = static_cast<Node*>(i._node); + if (std::find(to_clear.begin(), to_clear.end(), &rm->_selection) == to_clear.end()) { + to_clear.push_back(&rm->_selection); + ++in; + } + nodes.push_back(std::make_pair(rm, in)); + } + for (size_t i = 0, e = nodes.size(); i != e; ++i) { + to_clear[nodes[i].second]->erase(nodes[i].first, false); + } + std::vector<std::vector<SelectableControlPoint *> > emission; + for (long i = 0, e = to_clear.size(); i != e; ++i) { + emission.push_back(std::vector<SelectableControlPoint *>()); + for (size_t j = 0, f = nodes.size(); j != f; ++j) { + if (nodes[j].second != i) + break; + emission[i].push_back(nodes[j].first); + } + } + + for (size_t i = 0, e = emission.size(); i != e; ++i) { + to_clear[i]->signal_selection_changed.emit(emission[i], false); + } + + for (iterator i = begin(); i != end();) + erase (i++); } NodeList::iterator NodeList::erase(iterator i) diff --git a/src/ui/tool/node.h b/src/ui/tool/node.h index 4582d998a..101af4817 100644 --- a/src/ui/tool/node.h +++ b/src/ui/tool/node.h @@ -41,6 +41,7 @@ template <typename> class NodeIterator; } } +/* #if HAVE_TR1_UNORDERED_SET namespace std { namespace tr1 { @@ -48,6 +49,8 @@ template <typename N> struct hash< Inkscape::UI::NodeIterator<N> >; } } #endif +#endif +*/ namespace Inkscape { namespace UI { @@ -117,7 +120,7 @@ public: protected: Handle(NodeSharedData const &data, Geom::Point const &initial_pos, Node *parent); - + virtual void handle_2button_press(); virtual bool _eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent *event); virtual void dragged(Geom::Point &new_pos, GdkEventMotion *event); virtual bool grabbed(GdkEventMotion *event); @@ -131,6 +134,7 @@ protected: private: inline PathManipulator &_pm(); + inline PathManipulator &_pm() const; Node *_parent; // the handle's lifetime does not extend beyond that of the parent node, // so a naked pointer is OK and allows setting it during Node's construction SPCtrlLine *_handle_line; @@ -217,6 +221,7 @@ public: Node *nodeAwayFrom(Handle *h); NodeList &nodeList() { return *(static_cast<ListNode*>(this)->ln_list); } + NodeList &nodeList() const { return *(static_cast<ListNode const*>(this)->ln_list); } /** * Move the node to the bottom of its canvas group. @@ -263,6 +268,7 @@ private: Inkscape::SnapSourceType _snapSourceType() const; Inkscape::SnapTargetType _snapTargetType() const; inline PathManipulator &_pm(); + inline PathManipulator &_pm() const; /** Determine whether two nodes are joined by a linear segment. */ static bool _is_line_segment(Node *first, Node *second); @@ -490,10 +496,17 @@ inline double Handle::length() const { inline PathManipulator &Handle::_pm() { return _parent->_pm(); } +inline PathManipulator &Handle::_pm() const { + return _parent->_pm(); +} inline PathManipulator &Node::_pm() { return nodeList().subpathList().pm(); } +inline PathManipulator &Node::_pm() const { + return nodeList().subpathList().pm(); +} + // definitions for node iterator template <typename N> NodeIterator<N>::operator bool() const { diff --git a/src/ui/tool/path-manipulator.cpp b/src/ui/tool/path-manipulator.cpp index 338499672..8b99c33b8 100644 --- a/src/ui/tool/path-manipulator.cpp +++ b/src/ui/tool/path-manipulator.cpp @@ -11,6 +11,7 @@ */ #include "live_effects/lpe-powerstroke.h" +#include "live_effects/lpe-fillet-chamfer.h" #include <string> #include <sstream> #include <deque> @@ -42,6 +43,7 @@ #include "ui/tool/multi-path-manipulator.h" #include "xml/node.h" #include "xml/node-observer.h" +#include "live_effects/lpe-bspline.h" namespace Inkscape { namespace UI { @@ -54,6 +56,10 @@ enum PathChange { }; } // anonymous namespace +const double handleCubicGap = 0.01; +const double noPower = 0.0; +const double defaultStartPower = 0.3334; + /** * Notifies the path manipulator when something changes the path being edited @@ -102,7 +108,6 @@ private: }; void build_segment(Geom::PathBuilder &, Node *, Node *); - PathManipulator::PathManipulator(MultiPathManipulator &mpm, SPPath *path, Geom::Affine const &et, guint32 outline_color, Glib::ustring lpe_key) : PointManipulator(mpm._path_data.node_data.desktop, *mpm._path_data.node_data.selection) @@ -139,12 +144,14 @@ PathManipulator::PathManipulator(MultiPathManipulator &mpm, SPPath *path, _selection.signal_update.connect( sigc::bind(sigc::mem_fun(*this, &PathManipulator::update), false)); - _selection.signal_point_changed.connect( - sigc::mem_fun(*this, &PathManipulator::_selectionChanged)); + _selection.signal_selection_changed.connect( + sigc::mem_fun(*this, &PathManipulator::_selectionChangedM)); _desktop->signal_zoom_changed.connect( sigc::hide( sigc::mem_fun(*this, &PathManipulator::_updateOutlineOnZoomChange))); _createControlPointsFromGeometry(); + //Define if the path is BSpline on construction + isBSpline(true); } PathManipulator::~PathManipulator() @@ -662,6 +669,15 @@ unsigned PathManipulator::_deleteStretch(NodeList::iterator start, NodeList::ite nl.erase(start); start = next; } + // if we are removing, we readjust the handlers + if(isBSpline()){ + if(start.prev()){ + start.prev()->front()->setPosition(BSplineHandleReposition(start.prev()->front(),start.prev()->back())); + } + if(end){ + end->back()->setPosition(BSplineHandleReposition(end->back(),end->front())); + } + } return del_len; } @@ -816,7 +832,6 @@ void PathManipulator::scaleHandle(Node *n, int which, int dir, bool pixel) } h->setRelativePos(relpos); update(); - gchar const *key = which < 0 ? "handle:scale:left" : "handle:scale:right"; _commit(_("Scale handle"), key); } @@ -980,9 +995,36 @@ NodeList::iterator PathManipulator::subdivideSegment(NodeList::iterator first, d // set new handle positions Node *n = new Node(_multi_path_manipulator._path_data.node_data, seg2[0]); - n->back()->setPosition(seg1[2]); - n->front()->setPosition(seg2[1]); - n->setType(NODE_SMOOTH, false); + if(!isBSpline()){ + n->back()->setPosition(seg1[2]); + n->front()->setPosition(seg2[1]); + n->setType(NODE_SMOOTH, false); + } else { + Geom::D2< Geom::SBasis > SBasisInsideNodes; + SPCurve *lineInsideNodes = new SPCurve(); + if(second->back()->isDegenerate()){ + lineInsideNodes->moveto(n->position()); + lineInsideNodes->lineto(second->position()); + SBasisInsideNodes = lineInsideNodes->first_segment()->toSBasis(); + Geom::Point next = SBasisInsideNodes.valueAt(defaultStartPower); + next = Geom::Point(next[Geom::X] + handleCubicGap,next[Geom::Y] + handleCubicGap); + lineInsideNodes->reset(); + n->front()->setPosition(next); + }else{ + n->front()->setPosition(seg2[1]); + } + if(first->front()->isDegenerate()){ + lineInsideNodes->moveto(n->position()); + lineInsideNodes->lineto(first->position()); + SBasisInsideNodes = lineInsideNodes->first_segment()->toSBasis(); + Geom::Point previous = SBasisInsideNodes.valueAt(defaultStartPower); + previous = Geom::Point(previous[Geom::X] + handleCubicGap,previous[Geom::Y] + handleCubicGap); + n->back()->setPosition(previous); + }else{ + n->back()->setPosition(seg1[2]); + } + n->setType(NODE_CUSP, false); + } inserted = list.insert(insert_at, n); first->front()->move(seg1[1]); @@ -1104,7 +1146,6 @@ void PathManipulator::_createControlPointsFromGeometry() Geom::Curve const &cseg = pit->back_closed(); bool fuse_ends = pit->closed() && Geom::are_near(cseg.initialPoint(), cseg.finalPoint()); - for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) { Geom::Point pos = cit->finalPoint(); Node *current_node; @@ -1171,6 +1212,91 @@ void PathManipulator::_createControlPointsFromGeometry() } } +//determines if the trace has a bspline effect and the number of steps that it takes +int PathManipulator::BSplineGetSteps() const { + + LivePathEffect::LPEBSpline const *lpe_bsp = NULL; + + if (SP_IS_LPE_ITEM(_path) && _path->hasPathEffect()){ + Inkscape::LivePathEffect::Effect const *thisEffect = SP_LPE_ITEM(_path)->getPathEffectOfType(Inkscape::LivePathEffect::BSPLINE); + if(thisEffect){ + lpe_bsp = dynamic_cast<LivePathEffect::LPEBSpline const*>(thisEffect->getLPEObj()->get_lpe()); + } + } + int steps = 0; + if(lpe_bsp){ + steps = lpe_bsp->steps+1; + } + return steps; +} + +// determines if the trace has bspline effect +bool PathManipulator::isBSpline(bool recalculate){ + if(recalculate){ + _is_bspline = this->BSplineGetSteps() > 0; + } + return _is_bspline; +} + +bool PathManipulator::isBSpline() const { + return BSplineGetSteps() > 0; +} + +// returns the corresponding strength to the position of the handlers +double PathManipulator::BSplineHandlePosition(Handle *h, Handle *h2){ + using Geom::X; + using Geom::Y; + if(h2){ + h = h2; + } + double pos = noPower; + Node *n = h->parent(); + Node * nextNode = NULL; + nextNode = n->nodeToward(h); + if(nextNode){ + SPCurve *lineInsideNodes = new SPCurve(); + lineInsideNodes->moveto(n->position()); + lineInsideNodes->lineto(nextNode->position()); + if(!are_near(h->position(), n->position())){ + pos = Geom::nearest_point(Geom::Point(h->position()[X] - handleCubicGap, h->position()[Y] - handleCubicGap), *lineInsideNodes->first_segment()); + } + } + if (pos == noPower && !h2){ + return BSplineHandlePosition(h, h->other()); + } + return pos; +} + +// give the location for the handler in the corresponding position +Geom::Point PathManipulator::BSplineHandleReposition(Handle *h, Handle *h2){ + double pos = this->BSplineHandlePosition(h, h2); + return BSplineHandleReposition(h,pos); +} + +// give the location for the handler to the specified position +Geom::Point PathManipulator::BSplineHandleReposition(Handle *h,double pos){ + using Geom::X; + using Geom::Y; + Geom::Point ret = h->position(); + Node *n = h->parent(); + Geom::D2< Geom::SBasis > SBasisInsideNodes; + SPCurve *lineInsideNodes = new SPCurve(); + Node * nextNode = NULL; + nextNode = n->nodeToward(h); + if(nextNode && pos != noPower){ + lineInsideNodes->moveto(n->position()); + lineInsideNodes->lineto(nextNode->position()); + SBasisInsideNodes = lineInsideNodes->first_segment()->toSBasis(); + ret = SBasisInsideNodes.valueAt(pos); + ret = Geom::Point(ret[X] + handleCubicGap,ret[Y] + handleCubicGap); + }else{ + if(pos == noPower){ + ret = n->position(); + } + } + return ret; +} + /** Construct the geometric representation of nodes and handles, update the outline * and display * \param alert_LPE if true, first the LPE is warned what the new path is going to be before updating it @@ -1178,6 +1304,8 @@ void PathManipulator::_createControlPointsFromGeometry() void PathManipulator::_createGeometryFromControlPoints(bool alert_LPE) { Geom::PathBuilder builder; + //Refresh if is bspline some times -think on path change selection, this value get lost + isBSpline(true); for (std::list<SubpathPtr>::iterator spi = _subpaths.begin(); spi != _subpaths.end(); ) { SubpathPtr subpath = *spi; if (subpath->empty()) { @@ -1186,7 +1314,6 @@ void PathManipulator::_createGeometryFromControlPoints(bool alert_LPE) } NodeList::iterator prev = subpath->begin(); builder.moveTo(prev->position()); - for (NodeList::iterator i = ++subpath->begin(); i != subpath->end(); ++i) { build_segment(builder, prev.ptr(), i.ptr()); prev = i; @@ -1207,11 +1334,20 @@ void PathManipulator::_createGeometryFromControlPoints(bool alert_LPE) _spcurve->set_pathvector(pathv); if (alert_LPE) { /// \todo note that _path can be an Inkscape::LivePathEffect::Effect* too, kind of confusing, rework member naming? - if (SP_IS_LPE_ITEM(_path) && _path->hasPathEffect()) { - PathEffectList effect_list = _path->getEffectList(); - LivePathEffect::LPEPowerStroke *lpe_pwr = dynamic_cast<LivePathEffect::LPEPowerStroke*>( effect_list.front()->lpeobject->get_lpe() ); - if (lpe_pwr) { - lpe_pwr->adjustForNewPath(pathv); + if (SP_IS_LPE_ITEM(_path) && _path->hasPathEffect()){ + Inkscape::LivePathEffect::Effect* thisEffect = SP_LPE_ITEM(_path)->getPathEffectOfType(Inkscape::LivePathEffect::POWERSTROKE); + if(thisEffect){ + LivePathEffect::LPEPowerStroke *lpe_pwr = dynamic_cast<LivePathEffect::LPEPowerStroke*>(thisEffect->getLPEObj()->get_lpe()); + if (lpe_pwr) { + lpe_pwr->adjustForNewPath(pathv); + } + } + thisEffect = SP_LPE_ITEM(_path)->getPathEffectOfType(Inkscape::LivePathEffect::FILLET_CHAMFER); + if(thisEffect){ + LivePathEffect::LPEFilletChamfer *lpe_fll = dynamic_cast<LivePathEffect::LPEFilletChamfer*>(thisEffect->getLPEObj()->get_lpe()); + if (lpe_fll) { + lpe_fll->adjustForNewPath(pathv); + } } } } @@ -1419,6 +1555,12 @@ bool PathManipulator::_handleClicked(Handle *h, GdkEventButton *event) return false; } +void PathManipulator::_selectionChangedM(std::vector<SelectableControlPoint *> pvec, bool selected) { + for (size_t n = 0, e = pvec.size(); n < e; ++n) { + _selectionChanged(pvec[n], selected); + } +} + void PathManipulator::_selectionChanged(SelectableControlPoint *p, bool selected) { if (selected) ++_num_selected; diff --git a/src/ui/tool/path-manipulator.h b/src/ui/tool/path-manipulator.h index 7a13ce723..4d2bf4300 100644 --- a/src/ui/tool/path-manipulator.h +++ b/src/ui/tool/path-manipulator.h @@ -19,6 +19,7 @@ #include <boost/weak_ptr.hpp> #include "ui/tool/node.h" #include "ui/tool/manipulator.h" +#include "live_effects/lpe-bspline.h" struct SPCanvasItem; class SPCurve; @@ -95,6 +96,7 @@ public: NodeList::iterator extremeNode(NodeList::iterator origin, bool search_selected, bool search_unselected, bool closest); + int BSplineGetSteps() const; // this is necessary for Tab-selection in MultiPathManipulator SubpathList &subpathList() { return _subpaths; } @@ -104,6 +106,12 @@ private: typedef boost::shared_ptr<NodeList> SubpathPtr; void _createControlPointsFromGeometry(); + + bool isBSpline(bool recalculate = false); + bool isBSpline() const; + double BSplineHandlePosition(Handle *h, Handle *h2 = NULL); + Geom::Point BSplineHandleReposition(Handle *h, Handle *h2 = NULL); + Geom::Point BSplineHandleReposition(Handle *h, double pos); void _createGeometryFromControlPoints(bool alert_LPE = false); unsigned _deleteStretch(NodeList::iterator first, NodeList::iterator last, bool keep_shape); std::string _createTypeString(); @@ -114,7 +122,8 @@ private: Glib::ustring _nodetypesKey(); Inkscape::XML::Node *_getXMLNode(); - void _selectionChanged(SelectableControlPoint *p, bool selected); + void _selectionChangedM(std::vector<SelectableControlPoint *> pvec, bool selected); + void _selectionChanged(SelectableControlPoint * p, bool selected); bool _nodeClicked(Node *, GdkEventButton *); void _handleGrabbed(); bool _handleClicked(Handle *, GdkEventButton *); @@ -145,6 +154,7 @@ private: bool _show_path_direction; bool _live_outline; bool _live_objects; + bool _is_bspline; Glib::ustring _lpe_key; friend class PathManipulatorObserver; diff --git a/src/ui/tool/selectable-control-point.h b/src/ui/tool/selectable-control-point.h index 8acfc1168..362d4addc 100644 --- a/src/ui/tool/selectable-control-point.h +++ b/src/ui/tool/selectable-control-point.h @@ -28,6 +28,7 @@ public: virtual Geom::Rect bounds() const { return Geom::Rect(position(), position()); } + friend class NodeList; protected: |
