summaryrefslogtreecommitdiffstats
path: root/src/ui/tool
diff options
context:
space:
mode:
authorJabier Arraiza Cenoz <jabier.arraiza@marker.es>2014-11-23 23:36:49 +0000
committerJabiertxof <jtx@jtx.marker.es>2014-11-23 23:36:49 +0000
commit0969085ddf607a7a98cf7fd6d9b10da5fbebe62d (patch)
tree59b2bc9ed3412ab2de4c703ef30342dfe2401704 /src/ui/tool
parentrefactor from lastApplied (diff)
parentFixed a bug pointed by suv running from comand line, also removed another des... (diff)
downloadinkscape-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.cpp49
-rw-r--r--src/ui/tool/control-point-selection.h13
-rw-r--r--src/ui/tool/curve-drag-point.cpp17
-rw-r--r--src/ui/tool/multi-path-manipulator.cpp21
-rw-r--r--src/ui/tool/multi-path-manipulator.h5
-rw-r--r--src/ui/tool/node.cpp248
-rw-r--r--src/ui/tool/node.h15
-rw-r--r--src/ui/tool/path-manipulator.cpp170
-rw-r--r--src/ui/tool/path-manipulator.h12
-rw-r--r--src/ui/tool/selectable-control-point.h1
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: