Code

show waiting cursor while ExecutionEnv is working
[inkscape.git] / src / nodepath.cpp
1 #define __SP_NODEPATH_C__
3 /** \file
4  * Path handler in node edit mode
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *
10  * Portions of this code are in public domain; node sculpting functions written by bulia byak are under GNU GPL
11  */
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
17 #include <gdk/gdkkeysyms.h>
18 #include "display/canvas-bpath.h"
19 #include "display/curve.h"
20 #include "display/sp-ctrlline.h"
21 #include "display/sodipodi-ctrl.h"
22 #include "display/sp-canvas-util.h"
23 #include <glibmm/i18n.h>
24 #include "2geom/pathvector.h"
25 #include "2geom/sbasis-to-bezier.h"
26 #include "2geom/bezier-curve.h"
27 #include "2geom/hvlinesegment.h"
28 #include "helper/units.h"
29 #include "helper/geom.h"
30 #include "knot.h"
31 #include "inkscape.h"
32 #include "document.h"
33 #include "sp-namedview.h"
34 #include "desktop.h"
35 #include "desktop-handles.h"
36 #include "snap.h"
37 #include "message-stack.h"
38 #include "message-context.h"
39 #include "node-context.h"
40 #include "lpe-tool-context.h"
41 #include "shape-editor.h"
42 #include "selection-chemistry.h"
43 #include "selection.h"
44 #include "xml/repr.h"
45 #include "preferences.h"
46 #include "sp-metrics.h"
47 #include "sp-path.h"
48 #include "libnr/nr-matrix-ops.h"
49 #include "svg/svg.h"
50 #include "verbs.h"
51 #include <2geom/bezier-utils.h>
52 #include <vector>
53 #include <algorithm>
54 #include <cstring>
55 #include <cmath>
56 #include "live_effects/lpeobject.h"
57 #include "live_effects/lpeobject-reference.h"
58 #include "live_effects/effect.h"
59 #include "live_effects/parameter/parameter.h"
60 #include "live_effects/parameter/path.h"
61 #include "util/mathfns.h"
62 #include "display/snap-indicator.h"
63 #include "snapped-point.h"
65 namespace Geom { class Matrix; }
67 /// \todo
68 /// evil evil evil. FIXME: conflict of two different Path classes!
69 /// There is a conflict in the namespace between two classes named Path.
70 /// #include "sp-flowtext.h"
71 /// #include "sp-flowregion.h"
73 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
74 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
75 GType sp_flowregion_get_type (void);
76 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
77 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
78 GType sp_flowtext_get_type (void);
79 // end evil workaround
81 #include "helper/stlport.h"
84 /// \todo fixme: Implement these via preferences */
86 #define NODE_FILL          0xbfbfbf00
87 #define NODE_STROKE        0x000000ff
88 #define NODE_FILL_HI       0xff000000
89 #define NODE_STROKE_HI     0x000000ff
90 #define NODE_FILL_SEL      0x0000ffff
91 #define NODE_STROKE_SEL    0x000000ff
92 #define NODE_FILL_SEL_HI   0xff000000
93 #define NODE_STROKE_SEL_HI 0x000000ff
94 #define KNOT_FILL          0xffffffff
95 #define KNOT_STROKE        0x000000ff
96 #define KNOT_FILL_HI       0xff000000
97 #define KNOT_STROKE_HI     0x000000ff
99 static GMemChunk *nodechunk = NULL;
101 /* Creation from object */
103 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t);
104 static Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length);
106 /* Object updating */
108 static void stamp_repr(Inkscape::NodePath::Path *np);
109 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
110 static gchar *create_typestr(Inkscape::NodePath::Path *np);
112 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
114 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
116 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
118 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
120 /* Adjust handle placement, if the node or the other handle is moved */
121 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
122 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
123 static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node);
125 /* Node event callbacks */
126 static void node_clicked(SPKnot *knot, guint state, gpointer data);
127 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
128 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
129 static gboolean node_request(SPKnot *knot, Geom::Point const &p, guint state, gpointer data);
131 /* Handle event callbacks */
132 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
133 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
134 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
135 static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, gpointer data);
136 static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data);
137 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
139 /* Constructors and destructors */
141 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
142 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
143 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
144 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
145 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
146                                          Geom::Point *ppos, Geom::Point *pos, Geom::Point *npos);
147 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
149 /* Helpers */
151 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
152 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
153 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
155 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
156 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
158 // active_node indicates mouseover node
159 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
161 static SPCanvasItem *
162 sp_nodepath_make_helper_item(Inkscape::NodePath::Path *np, /*SPDesktop *desktop, */const SPCurve *curve, bool show = false) {
163     SPCurve *helper_curve = curve->copy();
164     helper_curve->transform(np->i2d);
165     SPCanvasItem *helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
166     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
167     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helper_path), 0, SP_WIND_RULE_NONZERO);
168     sp_canvas_item_move_to_z(helper_path, 0);
169     if (show) {
170         sp_canvas_item_show(helper_path);
171     }
172     helper_curve->unref();
173     return helper_path;
176 static SPCanvasItem *
177 canvasitem_from_pathvec(Inkscape::NodePath::Path *np, Geom::PathVector const &pathv, bool show) {
178     SPCurve *helper_curve = new SPCurve(pathv);
179     return sp_nodepath_make_helper_item(np, helper_curve, show);
182 static void
183 sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) {
184     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> > helper_path_vec;
185     if (!SP_IS_LPE_ITEM(np->item)) {
186         g_print ("Only LPEItems can have helperpaths!\n");
187         return;
188     }
190     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
191     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
192     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
193         Inkscape::LivePathEffect::LPEObjectReference *lperef = (*i);
194         Inkscape::LivePathEffect::Effect *lpe = lperef->lpeobject->get_lpe();
195         if (lpe) {
196             // create new canvas items from the effect's helper paths
197             std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
198             for (std::vector<Geom::PathVector>::iterator j = hpaths.begin(); j != hpaths.end(); ++j) {
199                 np->helper_path_vec[lpe].push_back(canvasitem_from_pathvec(np, *j, true));
200             }
201         }
202     }
205 void
206 sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np) {
207     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> > helper_path_vec;
208     if (!SP_IS_LPE_ITEM(np->item)) {
209         g_print ("Only LPEItems can have helperpaths!\n");
210         return;
211     }
213     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
214     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
215     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
216         Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->get_lpe();
217         if (lpe) {
218             /* update canvas items from the effect's helper paths; note that this code relies on the
219              * fact that getHelperPaths() will always return the same number of helperpaths in the same
220              * order as during their creation in sp_nodepath_create_helperpaths
221              */
222             std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
223             for (unsigned int j = 0; j < hpaths.size(); ++j) {
224                 SPCurve *curve = new SPCurve(hpaths[j]);
225                 curve->transform(np->i2d);
226                 sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH((np->helper_path_vec[lpe])[j]), curve);
227                 curve = curve->unref();
228             }
229         }
230     }
233 static void
234 sp_nodepath_destroy_helperpaths(Inkscape::NodePath::Path *np) {
235     for (HelperPathList::iterator i = np->helper_path_vec.begin(); i != np->helper_path_vec.end(); ++i) {
236         for (std::vector<SPCanvasItem *>::iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) {
237             GtkObject *temp = *j;
238             *j = NULL;
239             gtk_object_destroy(temp);
240         }
241     }
242     np->helper_path_vec.clear();
246 /**
247  * \brief Creates new nodepath from item
248  *
249  * \todo create proper constructor for nodepath::path, this method returns null a constructor cannot so this cannot be simply converted to constructor.
250  */
251 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
253     Inkscape::XML::Node *repr = object->repr;
255     /** \todo
256      * FIXME: remove this. We don't want to edit paths inside flowtext.
257      * Instead we will build our flowtext with cloned paths, so that the
258      * real paths are outside the flowtext and thus editable as usual.
259      */
260     if (SP_IS_FLOWTEXT(object)) {
261         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
262             if SP_IS_FLOWREGION(child) {
263                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
264                 if (grandchild && SP_IS_PATH(grandchild)) {
265                     object = SP_ITEM(grandchild);
266                     break;
267                 }
268             }
269         }
270     }
272     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
274     if (curve == NULL) {
275         return NULL;
276     }
278     if (curve->get_segment_count() < 1) {
279         curve->unref();
280         return NULL; // prevent crash for one-node paths
281     }
283     //Create new nodepath
284     Inkscape::NodePath::Path *np = new Inkscape::NodePath::Path();
285     if (!np) {
286         curve->unref();
287         return NULL;
288     }
290     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
292     // Set defaults
293     np->desktop     = desktop;
294     np->object      = object;
295     np->subpaths    = NULL;
296     np->selected    = NULL;
297     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
298     np->local_change = 0;
299     np->show_handles = show_handles;
300     np->helper_path = NULL;
301     np->helperpath_rgba = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
302     np->helperpath_width = 1.0;
303     np->curve = curve->copy();
304     np->show_helperpath = prefs->getBool("/tools/nodes/show_helperpath");
305     if (SP_IS_LPE_ITEM(object)) {
306         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
307         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
308             np->show_helperpath = true;
309         }
310     }
311     np->straight_path = false;
312     if (IS_LIVEPATHEFFECT(object) && item) {
313         np->item = item;
314     } else {
315         np->item = SP_ITEM(object);
316     }
318     np->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
320     // we need to update item's transform from the repr here,
321     // because they may be out of sync when we respond
322     // to a change in repr by regenerating nodepath     --bb
323     sp_object_read_attr(SP_OBJECT(np->item), "transform");
325     np->i2d  = sp_item_i2d_affine(np->item);
326     np->d2i  = np->i2d.inverse();
328     np->repr = repr;
329     if (repr_key_in) { // apparently the object is an LPEObject (this is a dirty check, hopefully nobody tries feeding non-lpeobjects into this method with non-null repr_key_in)
330         np->repr_key = g_strdup(repr_key_in);
331         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
332         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(object)->get_lpe();
333         if (!lpe) {
334             g_error("sp_nodepath_new: lpeobject without real lpe passed as argument!");
335             delete np;
336         }
337         Inkscape::LivePathEffect::Parameter *lpeparam = lpe->getParameter(repr_key_in);
338         if (lpeparam) {
339             lpeparam->param_setup_nodepath(np);
340         }
341     } else {
342         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
343         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
344             np->repr_key = g_strdup("inkscape:original-d");
346             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
347             if (lpe) {
348                 lpe->setup_nodepath(np);
349             }
350         } else {
351             np->repr_key = g_strdup("d");
352         }
353     }
355     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
356      * So for example a closed rectangle has a nodetypestring of length 5.
357      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
358     Geom::PathVector pathv_sanitized = pathv_to_linear_and_cubic_beziers(np->curve->get_pathvector());
359     np->curve->set_pathvector(pathv_sanitized);
360     guint length = np->curve->get_segment_count();
361     for (Geom::PathVector::const_iterator pit = pathv_sanitized.begin(); pit != pathv_sanitized.end(); ++pit) {
362         length += pit->empty() ? 0 : 1;
363     }
365     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
366     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
368     // create the subpath(s) from the bpath
369     subpaths_from_pathvector(np, pathv_sanitized, typestr);
371     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
372     np->subpaths = g_list_reverse(np->subpaths);
374     delete[] typestr;
375     curve->unref();
377     // Draw helper curve
378     if (np->show_helperpath) {
379         np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true);
380     }
382     sp_nodepath_create_helperpaths(np);
384     return np;
387 /**
388  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
389  */
390 Inkscape::NodePath::Path::~Path() {
391     while (this->subpaths) {
392         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) this->subpaths->data);
393     }
395     //Inform the ShapeEditor that made me, if any, that I am gone.
396     if (this->shape_editor)
397         this->shape_editor->nodepath_destroyed();
399     g_assert(!this->selected);
401     if (this->helper_path) {
402         GtkObject *temp = this->helper_path;
403         this->helper_path = NULL;
404         gtk_object_destroy(temp);
405     }
406     if (this->curve) {
407         this->curve->unref();
408         this->curve = NULL;
409     }
411     if (this->repr_key) {
412         g_free(this->repr_key);
413         this->repr_key = NULL;
414     }
415     if (this->repr_nodetypes_key) {
416         g_free(this->repr_nodetypes_key);
417         this->repr_nodetypes_key = NULL;
418     }
420     sp_nodepath_destroy_helperpaths(this);
422     this->desktop = NULL;
425 /**
426  *  Return the node count of a given NodeSubPath.
427  */
428 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
430     int nodeCount = 0;
432     if (subpath) {
433         nodeCount = g_list_length(subpath->nodes);
434     }
436     return nodeCount;
439 /**
440  *  Return the node count of a given NodePath.
441  */
442 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
444     gint nodeCount = 0;
445     if (np) {
446         for (GList *item = np->subpaths ; item ; item=item->next) {
447             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
448             nodeCount += g_list_length(subpath->nodes);
449         }
450     }
451     return nodeCount;
454 /**
455  *  Return the subpath count of a given NodePath.
456  */
457 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
459     gint nodeCount = 0;
460     if (np) {
461         nodeCount = g_list_length(np->subpaths);
462     }
463     return nodeCount;
466 /**
467  *  Return the selected node count of a given NodePath.
468  */
469 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
471     gint nodeCount = 0;
472     if (np) {
473         nodeCount = g_list_length(np->selected);
474     }
475     return nodeCount;
478 /**
479  *  Return the number of subpaths where nodes are selected in a given NodePath.
480  */
481 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
483     gint nodeCount = 0;
484     if (np && np->selected) {
485         if (!np->selected->next) {
486             nodeCount = 1;
487         } else {
488             for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
489                 Inkscape::NodePath::SubPath *subpath = static_cast<Inkscape::NodePath::SubPath *>(spl->data);
490                 for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
491                     Inkscape::NodePath::Node *node = static_cast<Inkscape::NodePath::Node *>(nl->data);
492                     if (node->selected) {
493                         nodeCount++;
494                         break;
495                     }
496                 }
497             }
498         }
499     }
500     return nodeCount;
503 /**
504  * Clean up a nodepath after editing.
505  *
506  * Currently we are deleting trivial subpaths.
507  */
508 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
510     GList *badSubPaths = NULL;
512     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
513     for (GList *l = nodepath->subpaths; l ; l=l->next) {
514        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
515        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
516             badSubPaths = g_list_append(badSubPaths, sp);
517     }
519     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
520     //also removes the subpath from nodepath->subpaths
521     for (GList *l = badSubPaths; l ; l=l->next) {
522        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
523         sp_nodepath_subpath_destroy(sp);
524     }
526     g_list_free(badSubPaths);
529 /**
530  * Create new nodepaths from pathvector, make it subpaths of np.
531  * \param t The node type array.
532  */
533 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
535     guint i = 0;  // index into node type array
536     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
537         if (pit->empty())
538             continue;  // don't add single knot paths
540         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
542         Geom::Point ppos = pit->initialPoint() * np->i2d;
543         NRPathcode pcode = NR_MOVETO;
545         /* Johan: Note that this is pretty arcane code. I am pretty sure it is working correctly, be very certain to change it! (better to just rewrite this whole method)*/
546         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
547             if( dynamic_cast<Geom::LineSegment const*>(&*cit) ||
548                 dynamic_cast<Geom::HLineSegment const*>(&*cit) ||
549                 dynamic_cast<Geom::VLineSegment const*>(&*cit) )
550             {
551                 Geom::Point pos = cit->initialPoint() * (Geom::Matrix)np->i2d;
552                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
554                 ppos = cit->finalPoint() * (Geom::Matrix)np->i2d;
555                 pcode = NR_LINETO;
556             }
557             else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
558                 std::vector<Geom::Point> points = cubic_bezier->points();
559                 Geom::Point pos = points[0] * (Geom::Matrix)np->i2d;
560                 Geom::Point npos = points[1] * (Geom::Matrix)np->i2d;
561                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
563                 ppos = points[2] * (Geom::Matrix)np->i2d;
564                 pcode = NR_CURVETO;
565             }
566         }
568         if (pit->closed()) {
569             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
570             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
571              * If the length is zero, don't add it to the nodepath. */
572             Geom::Curve const &closing_seg = pit->back_closed();
573             // Don't use !closing_seg.isDegenerate() as it is too precise, and does not account for floating point rounding probs (LP bug #257289)
574             if ( ! are_near(closing_seg.initialPoint(), closing_seg.finalPoint()) ) {
575                 Geom::Point pos = closing_seg.finalPoint() * (Geom::Matrix)np->i2d;
576                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
577             }
579             sp_nodepath_subpath_close(sp);
580         }
581     }
584 /**
585  * Convert from sodipodi:nodetypes to new style type array.
586  */
587 static
588 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
590     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
592     guint pos = 0;
594     if (types) {
595         for (guint i = 0; types[i] && ( i < length ); i++) {
596             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
597             if (types[i] != '\0') {
598                 switch (types[i]) {
599                     case 's':
600                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
601                         break;
602                     case 'a':
603                         typestr[pos++] =Inkscape::NodePath::NODE_AUTO;
604                         break;
605                     case 'z':
606                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
607                         break;
608                     case 'c':
609                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
610                         break;
611                     default:
612                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
613                         break;
614                 }
615             }
616         }
617     }
619     while (pos < length) {
620         typestr[pos++] = Inkscape::NodePath::NODE_NONE;
621     }
623     return typestr;
626 /**
627  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
628  * updated but repr is not (for speed). Used during curve and node drag.
629  */
630 static void update_object(Inkscape::NodePath::Path *np)
632     g_assert(np);
634     np->curve->unref();
635     np->curve = create_curve(np);
637     sp_nodepath_set_curve(np, np->curve);
639     if (np->show_helperpath) {
640         SPCurve * helper_curve = np->curve->copy();
641         helper_curve->transform(np->i2d);
642         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
643         helper_curve->unref();
644     }
646     // updating helperpaths of LPEItems is now done in sp_lpe_item_update();
647     //sp_nodepath_update_helperpaths(np);
649     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
650     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
651     np->shape_editor->update_knotholder();
654 /**
655  * Update XML path node with data from path object.
656  */
657 static void update_repr_internal(Inkscape::NodePath::Path *np)
659     g_assert(np);
661     Inkscape::XML::Node *repr = np->object->repr;
663     np->curve->unref();
664     np->curve = create_curve(np);
666     gchar *typestr = create_typestr(np);
667     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
669     // determine if path has an effect applied and write to correct "d" attribute.
670     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
671         np->local_change++;
672         repr->setAttribute(np->repr_key, svgpath);
673     }
675     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
676         np->local_change++;
677         repr->setAttribute(np->repr_nodetypes_key, typestr);
678     }
680     g_free(svgpath);
681     g_free(typestr);
683     if (np->show_helperpath) {
684         SPCurve * helper_curve = np->curve->copy();
685         helper_curve->transform(np->i2d);
686         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
687         helper_curve->unref();
688     }
690     // TODO: do we need this call here? after all, update_object() should have been called just before
691     //sp_nodepath_update_helperpaths(np);
694 /**
695  * Update XML path node with data from path object, commit changes forever.
696  */
697 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
699     //fixme: np can be NULL, so check before proceeding
700     g_return_if_fail(np != NULL);
702     update_repr_internal(np);
703     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
705     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
706                      annotation);
709 /**
710  * Update XML path node with data from path object, commit changes with undo.
711  */
712 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
714     update_repr_internal(np);
715     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
716                            annotation);
719 /**
720  * Make duplicate of path, replace corresponding XML node in tree, commit.
721  */
722 static void stamp_repr(Inkscape::NodePath::Path *np)
724     g_assert(np);
726     Inkscape::XML::Node *old_repr = np->object->repr;
727     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
729     // remember the position of the item
730     gint pos = old_repr->position();
731     // remember parent
732     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
734     SPCurve *curve = create_curve(np);
735     gchar *typestr = create_typestr(np);
737     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
739     new_repr->setAttribute(np->repr_key, svgpath);
740     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
742     // add the new repr to the parent
743     parent->appendChild(new_repr);
744     // move to the saved position
745     new_repr->setPosition(pos > 0 ? pos : 0);
747     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
748                      _("Stamp"));
750     Inkscape::GC::release(new_repr);
751     g_free(svgpath);
752     g_free(typestr);
753     curve->unref();
756 /**
757  * Create curve from path.
758  */
759 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
761     SPCurve *curve = new SPCurve();
763     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
764        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
765        curve->moveto(sp->first->pos * np->d2i);
766        Inkscape::NodePath::Node *n = sp->first->n.other;
767         while (n) {
768             Geom::Point const end_pt = n->pos * np->d2i;
769             if (!IS_FINITE(n->pos[0]) || !IS_FINITE(n->pos[1])){
770                 g_message("niet finite");
771             }
772             switch (n->code) {
773                 case NR_LINETO:
774                     curve->lineto(end_pt);
775                     break;
776                 case NR_CURVETO:
777                     curve->curveto(n->p.other->n.pos * np->d2i,
778                                      n->p.pos * np->d2i,
779                                      end_pt);
780                     break;
781                 default:
782                     g_assert_not_reached();
783                     break;
784             }
785             if (n != sp->last) {
786                 n = n->n.other;
787             } else {
788                 n = NULL;
789             }
790         }
791         if (sp->closed) {
792             curve->closepath();
793         }
794     }
796     return curve;
799 /**
800  * Convert path type string to sodipodi:nodetypes style.
801  */
802 static gchar *create_typestr(Inkscape::NodePath::Path *np)
804     gchar *typestr = g_new(gchar, 32);
805     gint len = 32;
806     gint pos = 0;
808     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
809        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
811         if (pos >= len) {
812             typestr = g_renew(gchar, typestr, len + 32);
813             len += 32;
814         }
816         typestr[pos++] = 'c';
818        Inkscape::NodePath::Node *n;
819         n = sp->first->n.other;
820         while (n) {
821             gchar code;
823             switch (n->type) {
824                 case Inkscape::NodePath::NODE_CUSP:
825                     code = 'c';
826                     break;
827                 case Inkscape::NodePath::NODE_SMOOTH:
828                     code = 's';
829                     break;
830                 case Inkscape::NodePath::NODE_AUTO:
831                     code = 'a';
832                     break;
833                 case Inkscape::NodePath::NODE_SYMM:
834                     code = 'z';
835                     break;
836                 default:
837                     g_assert_not_reached();
838                     code = '\0';
839                     break;
840             }
842             if (pos >= len) {
843                 typestr = g_renew(gchar, typestr, len + 32);
844                 len += 32;
845             }
847             typestr[pos++] = code;
849             if (n != sp->last) {
850                 n = n->n.other;
851             } else {
852                 n = NULL;
853             }
854         }
855     }
857     if (pos >= len) {
858         typestr = g_renew(gchar, typestr, len + 1);
859         len += 1;
860     }
862     typestr[pos++] = '\0';
864     return typestr;
867 // Returns different message contexts depending on the current context. This function should only
868 // be called when ec is either a SPNodeContext or SPLPEToolContext, thus we return NULL in all
869 // other cases.
870 static Inkscape::MessageContext *
871 get_message_context(SPEventContext *ec)
873     Inkscape::MessageContext *mc = 0;
875     if (SP_IS_NODE_CONTEXT(ec)) {
876         mc = SP_NODE_CONTEXT(ec)->_node_message_context;
877     } else if (SP_IS_LPETOOL_CONTEXT(ec)) {
878         mc = SP_LPETOOL_CONTEXT(ec)->_lpetool_message_context;
879     } else {
880         g_warning ("Nodepath should only be present in Node tool or Geometric tool.");
881     }
883     return mc;
886 /**
887  \brief Fills node and handle positions for three nodes, splitting line
888   marked by end at distance t.
889  */
890 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
892     g_assert(new_path != NULL);
893     g_assert(end      != NULL);
895     g_assert(end->p.other == new_path);
896    Inkscape::NodePath::Node *start = new_path->p.other;
897     g_assert(start);
899     if (end->code == NR_LINETO) {
900         new_path->type =Inkscape::NodePath::NODE_CUSP;
901         new_path->code = NR_LINETO;
902         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
903     } else {
904         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
905         new_path->code = NR_CURVETO;
906         gdouble s      = 1 - t;
907         for (int dim = 0; dim < 2; dim++) {
908             Geom::Coord const f000 = start->pos[dim];
909             Geom::Coord const f001 = start->n.pos[dim];
910             Geom::Coord const f011 = end->p.pos[dim];
911             Geom::Coord const f111 = end->pos[dim];
912             Geom::Coord const f00t = s * f000 + t * f001;
913             Geom::Coord const f01t = s * f001 + t * f011;
914             Geom::Coord const f11t = s * f011 + t * f111;
915             Geom::Coord const f0tt = s * f00t + t * f01t;
916             Geom::Coord const f1tt = s * f01t + t * f11t;
917             Geom::Coord const fttt = s * f0tt + t * f1tt;
918             start->n.pos[dim]    = f00t;
919             new_path->p.pos[dim] = f0tt;
920             new_path->pos[dim]   = fttt;
921             new_path->n.pos[dim] = f1tt;
922             end->p.pos[dim]      = f11t;
923         }
924     }
927 /**
928  * Adds new node on direct line between two nodes, activates handles of all
929  * three nodes.
930  */
931 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
933     g_assert(end);
934     g_assert(end->subpath);
935     g_assert(g_list_find(end->subpath->nodes, end));
937    Inkscape::NodePath::Node *start = end->p.other;
938     g_assert( start->n.other == end );
939    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
940                                                end,
941                                                (NRPathcode)end->code == NR_LINETO?
942                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
943                                                (NRPathcode)end->code,
944                                                &start->pos, &start->pos, &start->n.pos);
945     sp_nodepath_line_midpoint(newnode, end, t);
947     sp_node_adjust_handles(start);
948     sp_node_update_handles(start);
949     sp_node_update_handles(newnode);
950     sp_node_adjust_handles(end);
951     sp_node_update_handles(end);
953     return newnode;
956 /**
957 \brief Break the path at the node: duplicate the argument node, start a new subpath with the duplicate, and copy all nodes after the argument node to it
958 */
959 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
961     g_assert(node);
962     g_assert(node->subpath);
963     g_assert(g_list_find(node->subpath->nodes, node));
965     Inkscape::NodePath::Node* result = 0;
966     Inkscape::NodePath::SubPath *sp = node->subpath;
967     Inkscape::NodePath::Path *np    = sp->nodepath;
969     if (sp->closed) {
970         sp_nodepath_subpath_open(sp, node);
971         result = sp->first;
972     } else if ( (node == sp->first) || (node == sp->last ) ){
973         // no break for end nodes
974         result = 0;
975     } else {
976         // create a new subpath
977         Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
979         // duplicate the break node as start of the new subpath
980         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL,
981                                                                  static_cast<Inkscape::NodePath::NodeType>(node->type),
982                                                                  NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
984         // attach rest of curve to new node
985         g_assert(node->n.other);
986         newnode->n.other = node->n.other; node->n.other = NULL;
987         newnode->n.other->p.other = newnode;
988         newsubpath->last = sp->last;
989         sp->last = node;
990         node = newnode;
991         while (node->n.other) {
992             node = node->n.other;
993             node->subpath = newsubpath;
994             sp->nodes = g_list_remove(sp->nodes, node);
995             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
996         }
999         result = newnode;
1000     }
1001     return result;
1004 /**
1005  * Duplicate node and connect to neighbours.
1006  */
1007 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
1009     g_assert(node);
1010     g_assert(node->subpath);
1011     g_assert(g_list_find(node->subpath->nodes, node));
1013    Inkscape::NodePath::SubPath *sp = node->subpath;
1015     NRPathcode code = (NRPathcode) node->code;
1016     if (code == NR_MOVETO) { // if node is the endnode,
1017         node->code = NR_LINETO; // new one is inserted before it, so change that to line
1018     }
1020     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
1022     if (!node->n.other || !node->p.other) { // if node is an endnode, select it
1023         return node;
1024     } else {
1025         return newnode; // otherwise select the newly created node
1026     }
1029 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
1031     node->p.pos = (node->pos + (node->pos - node->n.pos));
1034 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
1036     node->n.pos = (node->pos + (node->pos - node->p.pos));
1039 /**
1040  * Change line type at node, with side effects on neighbours.
1041  */
1042 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
1044     g_assert(end);
1045     g_assert(end->subpath);
1046     g_assert(end->p.other);
1048     if (end->code != static_cast<guint>(code) ) {
1049         Inkscape::NodePath::Node *start = end->p.other;
1051         end->code = code;
1053         if (code == NR_LINETO) {
1054             if (start->code == NR_LINETO) {
1055                 sp_nodepath_set_node_type(start, Inkscape::NodePath::NODE_CUSP);
1056             }
1057             if (end->n.other) {
1058                 if (end->n.other->code == NR_LINETO) {
1059                     sp_nodepath_set_node_type(end, Inkscape::NodePath::NODE_CUSP);
1060                 }
1061             }
1063             if (start->type == Inkscape::NodePath::NODE_AUTO)
1064                 start->type = Inkscape::NodePath::NODE_SMOOTH;
1065             if (end->type == Inkscape::NodePath::NODE_AUTO)
1066                 end->type = Inkscape::NodePath::NODE_SMOOTH;
1068             start->n.pos = start->pos;
1069             end->p.pos = end->pos;
1071             sp_node_adjust_handle(start, -1);
1072             sp_node_adjust_handle(end, 1);
1074         } else {
1075             Geom::Point delta = end->pos - start->pos;
1076             start->n.pos = start->pos + delta / 3;
1077             end->p.pos = end->pos - delta / 3;
1078             sp_node_adjust_handle(start, 1);
1079             sp_node_adjust_handle(end, -1);
1080         }
1082         sp_node_update_handles(start);
1083         sp_node_update_handles(end);
1084     }
1087 static void
1088 sp_nodepath_update_node_knot(Inkscape::NodePath::Node *node)
1090     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1091         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1092         node->knot->setSize (node->selected? 11 : 9);
1093         sp_knot_update_ctrl(node->knot);
1094     } else if (node->type == Inkscape::NodePath::NODE_AUTO) {
1095         node->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1096         node->knot->setSize (node->selected? 11 : 9);
1097         sp_knot_update_ctrl(node->knot);
1098     } else {
1099         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1100         node->knot->setSize (node->selected? 9 : 7);
1101         sp_knot_update_ctrl(node->knot);
1102     }
1106 /**
1107  * Change node type, and its handles accordingly.
1108  */
1109 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1111     g_assert(node);
1112     g_assert(node->subpath);
1114     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1115         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1116             type =Inkscape::NodePath::NODE_CUSP;
1117         }
1118     }
1120     node->type = type;
1122     sp_nodepath_update_node_knot(node);
1124     // if one of handles is mouseovered, preserve its position
1125     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1126         sp_node_adjust_handle(node, 1);
1127     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1128         sp_node_adjust_handle(node, -1);
1129     } else {
1130         sp_node_adjust_handles(node);
1131     }
1133     sp_node_update_handles(node);
1135     sp_nodepath_update_statusbar(node->subpath->nodepath);
1137     return node;
1140 bool
1141 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1143 // TODO clean up multiple returns
1144         Inkscape::NodePath::Node *othernode = side->other;
1145         if (!othernode)
1146             return false;
1147         NRPathcode const code = sp_node_path_code_from_side(node, side);
1148         if (code == NR_LINETO)
1149             return true;
1150         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1151         if (&node->p == side) {
1152             other_to_me = &othernode->n;
1153         } else if (&node->n == side) {
1154             other_to_me = &othernode->p;
1155         }
1156         if (!other_to_me)
1157             return false;
1158         bool is_line =
1159              (Geom::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1160               Geom::L2(node->pos - side->pos) < 1e-6);
1161         return is_line;
1164 /**
1165  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1166  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1167  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too.
1168  * If already cusp and set to cusp, retracts handles.
1169 */
1170 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1172     if (type == Inkscape::NodePath::NODE_AUTO) {
1173         if (node->p.other != NULL)
1174             node->code = NR_CURVETO;
1175         if (node->n.other != NULL)
1176             node->n.other->code = NR_CURVETO;
1177     }
1179     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1181 /*
1182   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1184         if (two_handles) {
1185             // do nothing, adjust_handles called via set_node_type will line them up
1186         } else if (one_handle) {
1187             if (opposite_to_handle_is_line) {
1188                 if (lined_up) {
1189                     // already half-smooth; pull opposite handle too making it fully smooth
1190                 } else {
1191                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1192                 }
1193             } else {
1194                 // pull opposite handle in line with the existing one
1195             }
1196         } else if (no_handles) {
1197             if (both_segments_are_lines OR both_segments_are_curves) {
1198                 //pull both handles
1199             } else {
1200                 // pull the handle opposite to line segment, making node half-smooth
1201             }
1202         }
1203 */
1204         bool p_has_handle = (Geom::L2(node->pos  - node->p.pos) > 1e-6);
1205         bool n_has_handle = (Geom::L2(node->pos  - node->n.pos) > 1e-6);
1206         bool p_is_line = sp_node_side_is_line(node, &node->p);
1207         bool n_is_line = sp_node_side_is_line(node, &node->n);
1209         if (p_has_handle && n_has_handle) {
1210             // do nothing, adjust_handles will line them up
1211         } else if (p_has_handle || n_has_handle) {
1212             if (p_has_handle && n_is_line) {
1213                 Radial line (node->n.other->pos - node->pos);
1214                 Radial handle (node->pos - node->p.pos);
1215                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1216                     // already half-smooth; pull opposite handle too making it fully smooth
1217                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1218                 } else {
1219                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1220                 }
1221             } else if (n_has_handle && p_is_line) {
1222                 Radial line (node->p.other->pos - node->pos);
1223                 Radial handle (node->pos - node->n.pos);
1224                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1225                     // already half-smooth; pull opposite handle too making it fully smooth
1226                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1227                 } else {
1228                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1229                 }
1230             } else if (p_has_handle && node->n.other) {
1231                 // pull n handle
1232                 node->n.other->code = NR_CURVETO;
1233                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1234                     Geom::L2(node->p.pos - node->pos) :
1235                     Geom::L2(node->n.other->pos - node->pos) / 3;
1236                 node->n.pos = node->pos - (len / Geom::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1237             } else if (n_has_handle && node->p.other) {
1238                 // pull p handle
1239                 node->code = NR_CURVETO;
1240                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1241                     Geom::L2(node->n.pos - node->pos) :
1242                     Geom::L2(node->p.other->pos - node->pos) / 3;
1243                 node->p.pos = node->pos - (len / Geom::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1244             }
1245         } else if (!p_has_handle && !n_has_handle) {
1246             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1247                 // no handles, but both segments are either lnes or curves:
1248                 //pull both handles
1250                 // convert both to curves:
1251                 node->code = NR_CURVETO;
1252                 node->n.other->code = NR_CURVETO;
1254                 sp_node_adjust_handles_auto(node);
1255             } else {
1256                 // pull the handle opposite to line segment, making it half-smooth
1257                 if (p_is_line && node->n.other) {
1258                     if (type != Inkscape::NodePath::NODE_SYMM) {
1259                         // pull n handle
1260                         node->n.other->code = NR_CURVETO;
1261                         double len =  Geom::L2(node->n.other->pos - node->pos) / 3;
1262                         node->n.pos = node->pos + (len / Geom::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1263                     }
1264                 } else if (n_is_line && node->p.other) {
1265                     if (type != Inkscape::NodePath::NODE_SYMM) {
1266                         // pull p handle
1267                         node->code = NR_CURVETO;
1268                         double len =  Geom::L2(node->p.other->pos - node->pos) / 3;
1269                         node->p.pos = node->pos + (len / Geom::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1270                     }
1271                 }
1272             }
1273         }
1274     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1275         // cusping a cusp: retract nodes
1276         node->p.pos = node->pos;
1277         node->n.pos = node->pos;
1278     }
1280     sp_nodepath_set_node_type (node, type);
1283 /**
1284  * Move node to point, and adjust its and neighbouring handles.
1285  */
1286 void sp_node_moveto(Inkscape::NodePath::Node *node, Geom::Point p)
1288     if (node->type == Inkscape::NodePath::NODE_AUTO) {
1289         node->pos = p;
1290         sp_node_adjust_handles_auto(node);
1291     } else {
1292         Geom::Point delta = p - node->pos;
1293         node->pos = p;
1295         node->p.pos += delta;
1296         node->n.pos += delta;
1297     }
1299     Inkscape::NodePath::Node *node_p = NULL;
1300     Inkscape::NodePath::Node *node_n = NULL;
1302     if (node->p.other) {
1303         if (node->code == NR_LINETO) {
1304             sp_node_adjust_handle(node, 1);
1305             sp_node_adjust_handle(node->p.other, -1);
1306             node_p = node->p.other;
1307         }
1308         if (!node->p.other->selected && node->p.other->type == Inkscape::NodePath::NODE_AUTO) {
1309             sp_node_adjust_handles_auto(node->p.other);
1310             node_p = node->p.other;
1311         }
1312     }
1313     if (node->n.other) {
1314         if (node->n.other->code == NR_LINETO) {
1315             sp_node_adjust_handle(node, -1);
1316             sp_node_adjust_handle(node->n.other, 1);
1317             node_n = node->n.other;
1318         }
1319         if (!node->n.other->selected && node->n.other->type == Inkscape::NodePath::NODE_AUTO) {
1320             sp_node_adjust_handles_auto(node->n.other);
1321             node_n = node->n.other;
1322         }
1323     }
1325     // this function is only called from batch movers that will update display at the end
1326     // themselves, so here we just move all the knots without emitting move signals, for speed
1327     sp_node_update_handles(node, false);
1328     if (node_n) {
1329         sp_node_update_handles(node_n, false);
1330     }
1331     if (node_p) {
1332         sp_node_update_handles(node_p, false);
1333     }
1336 /**
1337  * Call sp_node_moveto() for node selection and handle possible snapping.
1338  */
1339 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, Geom::Coord dx, Geom::Coord dy,
1340                                             bool const snap, bool constrained = false,
1341                                             Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point())
1343     Geom::Point delta(dx, dy);
1344     Geom::Point best_pt = delta;
1345     Inkscape::SnappedPoint best;
1347     if (snap) {
1348         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1349          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we
1350          * must provide that information. */
1352         // Build a list of the unselected nodes to which the snapper should snap
1353         std::vector<Geom::Point> unselected_nodes;
1354         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1355             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1356             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1357                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1358                 if (!node->selected) {
1359                     unselected_nodes.push_back(to_2geom(node->pos));
1360                 }
1361             }
1362         }
1364         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1366         // When only the node closest to the mouse pointer is to be snapped
1367         // then we will not even try to snap to other points and discard those immediately
1368         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1369         bool closest_only = prefs->getBool("/options/snapclosestonly/value", false);
1371         Inkscape::NodePath::Node *closest_node = NULL;
1372         Geom::Coord closest_dist = NR_HUGE;
1374         if (closest_only) {
1375                 for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1376                         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1377                         Geom::Coord dist = Geom::L2(nodepath->drag_origin_mouse - n->origin);
1378                         if (dist < closest_dist) {
1379                                 closest_node = n;
1380                                 closest_dist = dist;
1381                         }
1382                 }
1383         }
1385         // Iterate through all selected nodes
1386         m.setup(nodepath->desktop, false, SP_PATH(nodepath->item), &unselected_nodes);
1387         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1388             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1389             if (!closest_only || n == closest_node) { //try to snap either all selected nodes or only the closest one
1390                     Inkscape::SnappedPoint s;
1391                     if (constrained) {
1392                         Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1393                         dedicated_constraint.setPoint(n->pos);
1394                         s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), dedicated_constraint);
1395                     } else {
1396                         s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta));
1397                     }
1399                     if (s.getSnapped()) {
1400                         s.setPointerDistance(Geom::L2(nodepath->drag_origin_mouse - n->origin));
1401                         if (!s.isOtherSnapBetter(best, true)) {
1402                                 best = s;
1403                                 best_pt = from_2geom(s.getPoint()) - n->pos;
1404                         }
1405                     }
1406             }
1407         }
1409         if (best.getSnapped()) {
1410             nodepath->desktop->snapindicator->set_new_snaptarget(best);
1411         } else {
1412             nodepath->desktop->snapindicator->remove_snaptarget();
1413         }
1414     }
1416     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1417         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1418         sp_node_moveto(n, n->pos + best_pt);
1419     }
1421     // do not update repr here so that node dragging is acceptably fast
1422     update_object(nodepath);
1425 /**
1426 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1427 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1428 near x = 0.
1429  */
1430 double
1431 sculpt_profile (double x, double alpha, guint profile)
1433     double result = 1;
1435     if (x >= 1) {
1436         result = 0;
1437     } else if (x <= 0) {
1438         result = 1;
1439     } else {
1440         switch (profile) {
1441             case SCULPT_PROFILE_LINEAR:
1442                 result = 1 - x;
1443                 break;
1444             case SCULPT_PROFILE_BELL:
1445                 result = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1446                 break;
1447             case SCULPT_PROFILE_ELLIPTIC:
1448                 result = sqrt(1 - x*x);
1449                 break;
1450             default:
1451                 g_assert_not_reached();
1452         }
1453     }
1455     return result;
1458 double
1459 bezier_length (Geom::Point a, Geom::Point ah, Geom::Point bh, Geom::Point b)
1461     // extremely primitive for now, don't have time to look for the real one
1462     double lower = Geom::L2(b - a);
1463     double upper = Geom::L2(ah - a) + Geom::L2(bh - ah) + Geom::L2(bh - b);
1464     return (lower + upper)/2;
1467 void
1468 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, Geom::Point delta, Geom::Point delta_n, Geom::Point delta_p)
1470     n->pos = n->origin + delta;
1471     n->n.pos = n->n.origin + delta_n;
1472     n->p.pos = n->p.origin + delta_p;
1473     sp_node_adjust_handles(n);
1474     sp_node_update_handles(n, false);
1477 /**
1478  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1479  * on how far they are from the dragged node n.
1480  */
1481 static void
1482 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, Geom::Point delta)
1484     g_assert (n);
1485     g_assert (nodepath);
1486     g_assert (n->subpath->nodepath == nodepath);
1488     double pressure = n->knot->pressure;
1489     if (pressure == 0)
1490         pressure = 0.5; // default
1491     pressure = CLAMP (pressure, 0.2, 0.8);
1493     // map pressure to alpha = 1/5 ... 5
1494     double alpha = 1 - 2 * fabs(pressure - 0.5);
1495     if (pressure > 0.5)
1496         alpha = 1/alpha;
1498     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1499     guint profile = prefs->getInt("/tools/nodes/sculpting_profile", SCULPT_PROFILE_BELL);
1501     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1502         // Only one subpath has selected nodes:
1503         // use linear mode, where the distance from n to node being dragged is calculated along the path
1505         double n_sel_range = 0, p_sel_range = 0;
1506         guint n_nodes = 0, p_nodes = 0;
1507         guint n_sel_nodes = 0, p_sel_nodes = 0;
1509         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1510         {
1511             double n_range = 0, p_range = 0;
1512             bool n_going = true, p_going = true;
1513             Inkscape::NodePath::Node *n_node = n;
1514             Inkscape::NodePath::Node *p_node = n;
1515             do {
1516                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1517                 if (n_node && n_going)
1518                     n_node = n_node->n.other;
1519                 if (n_node == NULL) {
1520                     n_going = false;
1521                 } else {
1522                     n_nodes ++;
1523                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1524                     if (n_node->selected) {
1525                         n_sel_nodes ++;
1526                         n_sel_range = n_range;
1527                     }
1528                     if (n_node == p_node) {
1529                         n_going = false;
1530                         p_going = false;
1531                     }
1532                 }
1533                 if (p_node && p_going)
1534                     p_node = p_node->p.other;
1535                 if (p_node == NULL) {
1536                     p_going = false;
1537                 } else {
1538                     p_nodes ++;
1539                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1540                     if (p_node->selected) {
1541                         p_sel_nodes ++;
1542                         p_sel_range = p_range;
1543                     }
1544                     if (p_node == n_node) {
1545                         n_going = false;
1546                         p_going = false;
1547                     }
1548                 }
1549             } while (n_going || p_going);
1550         }
1552         // Second pass: actually move nodes in this subpath
1553         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1554         {
1555             double n_range = 0, p_range = 0;
1556             bool n_going = true, p_going = true;
1557             Inkscape::NodePath::Node *n_node = n;
1558             Inkscape::NodePath::Node *p_node = n;
1559             do {
1560                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1561                 if (n_node && n_going)
1562                     n_node = n_node->n.other;
1563                 if (n_node == NULL) {
1564                     n_going = false;
1565                 } else {
1566                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1567                     if (n_node->selected) {
1568                         sp_nodepath_move_node_and_handles (n_node,
1569                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1570                                                            sculpt_profile ((n_range + Geom::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1571                                                            sculpt_profile ((n_range - Geom::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1572                     }
1573                     if (n_node == p_node) {
1574                         n_going = false;
1575                         p_going = false;
1576                     }
1577                 }
1578                 if (p_node && p_going)
1579                     p_node = p_node->p.other;
1580                 if (p_node == NULL) {
1581                     p_going = false;
1582                 } else {
1583                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1584                     if (p_node->selected) {
1585                         sp_nodepath_move_node_and_handles (p_node,
1586                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1587                                                            sculpt_profile ((p_range - Geom::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1588                                                            sculpt_profile ((p_range + Geom::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1589                     }
1590                     if (p_node == n_node) {
1591                         n_going = false;
1592                         p_going = false;
1593                     }
1594                 }
1595             } while (n_going || p_going);
1596         }
1598     } else {
1599         // Multiple subpaths have selected nodes:
1600         // use spatial mode, where the distance from n to node being dragged is measured directly as Geom::L2.
1601         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1602         // fix the pear-like shape when sculpting e.g. a ring
1604         // First pass: calculate range
1605         gdouble direct_range = 0;
1606         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1607             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1608             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1609                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1610                 if (node->selected) {
1611                     direct_range = MAX(direct_range, Geom::L2(node->origin - n->origin));
1612                 }
1613             }
1614         }
1616         // Second pass: actually move nodes
1617         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1618             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1619             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1620                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1621                 if (node->selected) {
1622                     if (direct_range > 1e-6) {
1623                         sp_nodepath_move_node_and_handles (node,
1624                                                        sculpt_profile (Geom::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1625                                                        sculpt_profile (Geom::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1626                                                        sculpt_profile (Geom::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1627                     } else {
1628                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1629                     }
1631                 }
1632             }
1633         }
1634     }
1636     // do not update repr here so that node dragging is acceptably fast
1637     update_object(nodepath);
1641 /**
1642  * Move node selection to point, adjust its and neighbouring handles,
1643  * handle possible snapping, and commit the change with possible undo.
1644  */
1645 void
1646 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1648     if (!nodepath) return;
1650     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1652     if (dx == 0) {
1653         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1654     } else if (dy == 0) {
1655         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1656     } else {
1657         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1658     }
1661 /**
1662  * Move node selection off screen and commit the change.
1663  */
1664 void
1665 sp_node_selected_move_screen(SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1667     // borrowed from sp_selection_move_screen in selection-chemistry.c
1668     // we find out the current zoom factor and divide deltas by it
1670     gdouble zoom = desktop->current_zoom();
1671     gdouble zdx = dx / zoom;
1672     gdouble zdy = dy / zoom;
1674     if (!nodepath) return;
1676     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1678     if (dx == 0) {
1679         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1680     } else if (dy == 0) {
1681         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1682     } else {
1683         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1684     }
1687 /**
1688  * Move selected nodes to the absolute position given
1689  */
1690 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis)
1692     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1693         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1694         Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]);
1695         sp_node_moveto(n, npos);
1696     }
1698     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1701 /**
1702  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return Geom::Nothing
1703  */
1704 boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1706     boost::optional<Geom::Coord> no_coord;
1707     g_return_val_if_fail(nodepath->selected, no_coord);
1709     // determine coordinate of first selected node
1710     GList *nsel = nodepath->selected;
1711     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1712     Geom::Coord coord = n->pos[axis];
1713     bool coincide = true;
1715     // compare it to the coordinates of all the other selected nodes
1716     for (GList *l = nsel->next; l != NULL; l = l->next) {
1717         n = (Inkscape::NodePath::Node *) l->data;
1718         if (n->pos[axis] != coord) {
1719             coincide = false;
1720         }
1721     }
1722     if (coincide) {
1723         return coord;
1724     } else {
1725         Geom::Rect bbox = sp_node_selected_bbox(nodepath);
1726         // currently we return the coordinate of the bounding box midpoint because I don't know how
1727         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1728         return bbox.midpoint()[axis];
1729     }
1732 /** If they don't yet exist, creates knot and line for the given side of the node */
1733 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1735     if (!side->knot) {
1736         side->knot = sp_knot_new(desktop, _("<b>Node handle</b>: drag to shape the curve; with <b>Ctrl</b> to snap angle; with <b>Alt</b> to lock length; with <b>Shift</b> to rotate both handles"));
1738         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1739         side->knot->setSize (7);
1740         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1741         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1742         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1743         sp_knot_update_ctrl(side->knot);
1745         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1746         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1747         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1748         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1749         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1750         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1751     }
1753     if (!side->line) {
1754         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1755                                         SP_TYPE_CTRLLINE, NULL);
1756     }
1759 /**
1760  * Ensure the given handle of the node is visible/invisible, update its screen position
1761  */
1762 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1764     g_assert(node != NULL);
1766    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1767     NRPathcode code = sp_node_path_code_from_side(node, side);
1769     show_handle = show_handle && (code == NR_CURVETO) && (Geom::L2(side->pos - node->pos) > 1e-6);
1771     if (show_handle) {
1772         if (!side->knot) { // No handle knot at all
1773             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1774             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1775             side->knot->pos = side->pos;
1776             if (side->knot->item)
1777                 SP_CTRL(side->knot->item)->moveto(side->pos);
1778             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1779             sp_knot_show(side->knot);
1780         } else {
1781             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1782                 if (fire_move_signals) {
1783                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1784                 } else {
1785                     sp_knot_moveto(side->knot, side->pos);
1786                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1787                 }
1788             }
1789             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1790                 sp_knot_show(side->knot);
1791             }
1792         }
1793         sp_canvas_item_show(side->line);
1794     } else {
1795         if (side->knot) {
1796             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1797                 sp_knot_hide(side->knot);
1798             }
1799         }
1800         if (side->line) {
1801             sp_canvas_item_hide(side->line);
1802         }
1803     }
1806 /**
1807  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1808  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1809  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1810  * updated; otherwise, just move the knots silently (used in batch moves).
1811  */
1812 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1814     g_assert(node != NULL);
1816     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1817         sp_knot_show(node->knot);
1818     }
1820     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1821         if (fire_move_signals)
1822             sp_knot_set_position(node->knot, node->pos, 0);
1823         else
1824             sp_knot_moveto(node->knot, node->pos);
1825     }
1827     gboolean show_handles = node->selected;
1828     if (node->p.other != NULL) {
1829         if (node->p.other->selected) show_handles = TRUE;
1830     }
1831     if (node->n.other != NULL) {
1832         if (node->n.other->selected) show_handles = TRUE;
1833     }
1835     if (node->subpath->nodepath->show_handles == false)
1836         show_handles = FALSE;
1838     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1839     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1842 /**
1843  * Call sp_node_update_handles() for all nodes on subpath.
1844  */
1845 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1847     g_assert(subpath != NULL);
1849     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1850         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1851     }
1854 /**
1855  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1856  */
1857 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1859     g_assert(nodepath != NULL);
1861     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1862         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1863     }
1866 void
1867 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1869     if (nodepath) {
1870         nodepath->show_handles = show;
1871         sp_nodepath_update_handles(nodepath);
1872     }
1875 /**
1876  * Adds all selected nodes in nodepath to list.
1877  */
1878 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1880     StlConv<Node *>::list(l, selected);
1881 /// \todo this adds a copying, rework when the selection becomes a stl list
1884 /**
1885  * Align selected nodes on the specified axis.
1886  */
1887 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1889     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1890         return;
1891     }
1893     if ( !nodepath->selected->next ) { // only one node selected
1894         return;
1895     }
1896    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1897     Geom::Point dest(pNode->pos);
1898     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1899         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1900         if (pNode) {
1901             dest[axis] = pNode->pos[axis];
1902             sp_node_moveto(pNode, dest);
1903         }
1904     }
1906     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1909 /// Helper struct.
1910 struct NodeSort
1912    Inkscape::NodePath::Node *_node;
1913     Geom::Coord _coord;
1914     /// \todo use vectorof pointers instead of calling copy ctor
1915     NodeSort(Inkscape::NodePath::Node *node, Geom::Dim2 axis) :
1916         _node(node), _coord(node->pos[axis])
1917     {}
1919 };
1921 static bool operator<(NodeSort const &a, NodeSort const &b)
1923     return (a._coord < b._coord);
1926 /**
1927  * Distribute selected nodes on the specified axis.
1928  */
1929 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1931     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1932         return;
1933     }
1935     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1936         return;
1937     }
1939    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1940     std::vector<NodeSort> sorted;
1941     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1942         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1943         if (pNode) {
1944             NodeSort n(pNode, axis);
1945             sorted.push_back(n);
1946             //dest[axis] = pNode->pos[axis];
1947             //sp_node_moveto(pNode, dest);
1948         }
1949     }
1950     std::sort(sorted.begin(), sorted.end());
1951     unsigned int len = sorted.size();
1952     //overall bboxes span
1953     float dist = (sorted.back()._coord -
1954                   sorted.front()._coord);
1955     //new distance between each bbox
1956     float step = (dist) / (len - 1);
1957     float pos = sorted.front()._coord;
1958     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1959           it < sorted.end();
1960           it ++ )
1961     {
1962         Geom::Point dest((*it)._node->pos);
1963         dest[axis] = pos;
1964         sp_node_moveto((*it)._node, dest);
1965         pos += step;
1966     }
1968     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1972 /**
1973  * Call sp_nodepath_line_add_node() for all selected segments.
1974  */
1975 void
1976 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1978     if (!nodepath) {
1979         return;
1980     }
1982     GList *nl = NULL;
1984     int n_added = 0;
1986     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1987        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1988         g_assert(t->selected);
1989         if (t->p.other && t->p.other->selected) {
1990             nl = g_list_prepend(nl, t);
1991         }
1992     }
1994     while (nl) {
1995        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1996        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1997        sp_nodepath_node_select(n, TRUE, FALSE);
1998        n_added ++;
1999        nl = g_list_remove(nl, t);
2000     }
2002     /** \todo fixme: adjust ? */
2003     sp_nodepath_update_handles(nodepath);
2005     if (n_added > 1) {
2006         sp_nodepath_update_repr(nodepath, _("Add nodes"));
2007     } else if (n_added > 0) {
2008         sp_nodepath_update_repr(nodepath, _("Add node"));
2009     }
2011     sp_nodepath_update_statusbar(nodepath);
2014 /**
2015  * Select segment nearest to point
2016  */
2017 void
2018 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p, bool toggle)
2020     if (!nodepath) {
2021         return;
2022     }
2024     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2025     Geom::PathVector const &pathv = curve->get_pathvector();
2026     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2027     if (!pvpos) {
2028         g_print ("Possible error?\n");
2029         return;
2030     }
2032     // calculate index for nodepath's representation.
2033     unsigned int segment_index = floor(pvpos->t) + 1;
2034     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2035         segment_index += pathv[i].size() + 1;
2036         if (pathv[i].closed()) {
2037             segment_index += 1;
2038         }
2039     }
2041     curve->unref();
2043     //find segment to segment
2044     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2046     //fixme: this can return NULL, so check before proceeding.
2047     g_return_if_fail(e != NULL);
2049     gboolean force = FALSE;
2050     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
2051         force = TRUE;
2052     }
2053     sp_nodepath_node_select(e, (gboolean) toggle, force);
2054     if (e->p.other)
2055         sp_nodepath_node_select(e->p.other, TRUE, force);
2057     sp_nodepath_update_handles(nodepath);
2059     sp_nodepath_update_statusbar(nodepath);
2062 /**
2063  * Add a node nearest to point
2064  */
2065 void
2066 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p)
2068     if (!nodepath) {
2069         return;
2070     }
2072     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2073     Geom::PathVector const &pathv = curve->get_pathvector();
2074     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2075     if (!pvpos) {
2076         g_print ("Possible error?\n");
2077         return;
2078     }
2080     // calculate index for nodepath's representation.
2081     double int_part;
2082     double t = std::modf(pvpos->t, &int_part);
2083     unsigned int segment_index = (unsigned int)int_part + 1;
2084     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2085         segment_index += pathv[i].size() + 1;
2086         if (pathv[i].closed()) {
2087             segment_index += 1;
2088         }
2089     }
2091     curve->unref();
2093     //find segment to split
2094     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2095     if (!e) {
2096         return;
2097     }
2099     //don't know why but t seems to flip for lines
2100     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2101         t = 1.0 - t;
2102     }
2104     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2105     sp_nodepath_node_select(n, FALSE, TRUE);
2107     /* fixme: adjust ? */
2108     sp_nodepath_update_handles(nodepath);
2110     sp_nodepath_update_repr(nodepath, _("Add node"));
2112     sp_nodepath_update_statusbar(nodepath);
2115 /*
2116  * Adjusts a segment so that t moves by a certain delta for dragging
2117  * converts lines to curves
2118  *
2119  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2120  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2121  */
2122 void
2123 sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, Geom::Point delta)
2125     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node);
2127     //fixme: e and e->p can be NULL, so check for those before proceeding
2128     g_return_if_fail(e != NULL);
2129     g_return_if_fail(&e->p != NULL);
2131     if (e->type == Inkscape::NodePath::NODE_AUTO) {
2132         e->type = Inkscape::NodePath::NODE_SMOOTH;
2133         sp_nodepath_update_node_knot (e);
2134     }
2135     if (e->p.other->type == Inkscape::NodePath::NODE_AUTO) {
2136         e->p.other->type = Inkscape::NodePath::NODE_SMOOTH;
2137         sp_nodepath_update_node_knot (e->p.other);
2138     }
2140     /* feel good is an arbitrary parameter that distributes the delta between handles
2141      * if t of the drag point is less than 1/6 distance form the endpoint only
2142      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2143      */
2144     double feel_good;
2145     if (t <= 1.0 / 6.0)
2146         feel_good = 0;
2147     else if (t <= 0.5)
2148         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2149     else if (t <= 5.0 / 6.0)
2150         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2151     else
2152         feel_good = 1;
2154     //if we're dragging a line convert it to a curve
2155     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2156         sp_nodepath_set_line_type(e, NR_CURVETO);
2157     }
2159     Geom::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2160     Geom::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2161     e->p.other->n.pos += offsetcoord0;
2162     e->p.pos += offsetcoord1;
2164     // adjust handles of adjacent nodes where necessary
2165     sp_node_adjust_handle(e,1);
2166     sp_node_adjust_handle(e->p.other,-1);
2168     sp_nodepath_update_handles(e->subpath->nodepath);
2170     update_object(e->subpath->nodepath);
2172     sp_nodepath_update_statusbar(e->subpath->nodepath);
2176 /**
2177  * Call sp_nodepath_break() for all selected segments.
2178  */
2179 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2181     if (!nodepath) return;
2183     GList *tempin = g_list_copy(nodepath->selected);
2184     GList *temp = NULL;
2185     for (GList *l = tempin; l != NULL; l = l->next) {
2186        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2187        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2188         if (nn == NULL) continue; // no break, no new node
2189         temp = g_list_prepend(temp, nn);
2190     }
2191     g_list_free(tempin);
2193     if (temp) {
2194         sp_nodepath_deselect(nodepath);
2195     }
2196     for (GList *l = temp; l != NULL; l = l->next) {
2197         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2198     }
2200     sp_nodepath_update_handles(nodepath);
2202     sp_nodepath_update_repr(nodepath, _("Break path"));
2205 /**
2206  * Duplicate the selected node(s).
2207  */
2208 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2210     if (!nodepath) {
2211         return;
2212     }
2214     GList *temp = NULL;
2215     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2216        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2217        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2218         if (nn == NULL) continue; // could not duplicate
2219         temp = g_list_prepend(temp, nn);
2220     }
2222     if (temp) {
2223         sp_nodepath_deselect(nodepath);
2224     }
2225     for (GList *l = temp; l != NULL; l = l->next) {
2226         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2227     }
2229     sp_nodepath_update_handles(nodepath);
2231     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2234 /**
2235  *  Internal function to join two nodes by merging them into one.
2236  */
2237 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2239     /* a and b are endpoints */
2241     // if one of the two nodes is mouseovered, fix its position
2242     Geom::Point c;
2243     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2244         c = a->pos;
2245     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2246         c = b->pos;
2247     } else {
2248         // otherwise, move joined node to the midpoint
2249         c = (a->pos + b->pos) / 2;
2250     }
2252     if (a->subpath == b->subpath) {
2253        Inkscape::NodePath::SubPath *sp = a->subpath;
2254         sp_nodepath_subpath_close(sp);
2255         sp_node_moveto (sp->first, c);
2257         sp_nodepath_update_handles(sp->nodepath);
2258         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2259         return;
2260     }
2262     /* a and b are separate subpaths */
2263     Inkscape::NodePath::SubPath *sa = a->subpath;
2264     Inkscape::NodePath::SubPath *sb = b->subpath;
2265     Geom::Point p;
2266     Inkscape::NodePath::Node *n;
2267     NRPathcode code;
2268     if (a == sa->first) {
2269         // we will now reverse sa, so that a is its last node, not first, and drop that node
2270         p = sa->first->n.pos;
2271         code = (NRPathcode)sa->first->n.other->code;
2272         // create new subpath
2273        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2274        // create a first moveto node on it
2275         n = sa->last;
2276         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2277         n = n->p.other;
2278         if (n == sa->first) n = NULL;
2279         while (n) {
2280             // copy the rest of the nodes from sa to t, going backwards
2281             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2282             n = n->p.other;
2283             if (n == sa->first) n = NULL;
2284         }
2285         // replace sa with t
2286         sp_nodepath_subpath_destroy(sa);
2287         sa = t;
2288     } else if (a == sa->last) {
2289         // a is already last, just drop it
2290         p = sa->last->p.pos;
2291         code = (NRPathcode)sa->last->code;
2292         sp_nodepath_node_destroy(sa->last);
2293     } else {
2294         code = NR_END;
2295         g_assert_not_reached();
2296     }
2298     if (b == sb->first) {
2299         // copy all nodes from b to a, forward
2300         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2301         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2302             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2303         }
2304     } else if (b == sb->last) {
2305         // copy all nodes from b to a, backward
2306         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2307         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2308             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2309         }
2310     } else {
2311         g_assert_not_reached();
2312     }
2313     /* and now destroy sb */
2315     sp_nodepath_subpath_destroy(sb);
2317     sp_nodepath_update_handles(sa->nodepath);
2319     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2321     sp_nodepath_update_statusbar(nodepath);
2324 /**
2325  *  Internal function to join two nodes by adding a segment between them.
2326  */
2327 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2329     if (a->subpath == b->subpath) {
2330        Inkscape::NodePath::SubPath *sp = a->subpath;
2332         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2333         sp->closed = TRUE;
2335         sp->first->p.other = sp->last;
2336         sp->last->n.other  = sp->first;
2338         sp_node_handle_mirror_p_to_n(sp->last);
2339         sp_node_handle_mirror_n_to_p(sp->first);
2341         sp->first->code = sp->last->code;
2342         sp->first       = sp->last;
2344         sp_nodepath_update_handles(sp->nodepath);
2346         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2348         return;
2349     }
2351     /* a and b are separate subpaths */
2352     Inkscape::NodePath::SubPath *sa = a->subpath;
2353     Inkscape::NodePath::SubPath *sb = b->subpath;
2355     Inkscape::NodePath::Node *n;
2356     Geom::Point p;
2357     NRPathcode code;
2358     if (a == sa->first) {
2359         code = (NRPathcode) sa->first->n.other->code;
2360        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2361         n = sa->last;
2362         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2363         for (n = n->p.other; n != NULL; n = n->p.other) {
2364             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2365         }
2366         sp_nodepath_subpath_destroy(sa);
2367         sa = t;
2368     } else if (a == sa->last) {
2369         code = (NRPathcode)sa->last->code;
2370     } else {
2371         code = NR_END;
2372         g_assert_not_reached();
2373     }
2375     if (b == sb->first) {
2376         n = sb->first;
2377         sp_node_handle_mirror_p_to_n(sa->last);
2378         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2379         sp_node_handle_mirror_n_to_p(sa->last);
2380         for (n = n->n.other; n != NULL; n = n->n.other) {
2381             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2382         }
2383     } else if (b == sb->last) {
2384         n = sb->last;
2385         sp_node_handle_mirror_p_to_n(sa->last);
2386         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2387         sp_node_handle_mirror_n_to_p(sa->last);
2388         for (n = n->p.other; n != NULL; n = n->p.other) {
2389             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2390         }
2391     } else {
2392         g_assert_not_reached();
2393     }
2394     /* and now destroy sb */
2396     sp_nodepath_subpath_destroy(sb);
2398     sp_nodepath_update_handles(sa->nodepath);
2400     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2403 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2405 /**
2406  * Internal function to handle joining two nodes.
2407  */
2408 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2410     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2412     if (g_list_length(nodepath->selected) != 2) {
2413         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2414         return;
2415     }
2417     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2418     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2420     g_assert(a != b);
2421     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2422         // someone tried to join an orphan node (i.e. a single-node subpath).
2423         // this is not worth an error message, just fail silently.
2424         return;
2425     }
2427     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2428         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2429         return;
2430     }
2432     switch(mode) {
2433         case NODE_JOIN_ENDPOINTS:
2434             do_node_selected_join(nodepath, a, b);
2435             break;
2436         case NODE_JOIN_SEGMENT:
2437             do_node_selected_join_segment(nodepath, a, b);
2438             break;
2439     }
2442 /**
2443  *  Join two nodes by merging them into one.
2444  */
2445 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2447     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2450 /**
2451  *  Join two nodes by adding a segment between them.
2452  */
2453 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2455     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2458 /**
2459  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2460  */
2461 void sp_node_delete_preserve(GList *nodes_to_delete)
2463     GSList *nodepaths = NULL;
2465     while (nodes_to_delete) {
2466         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2467         Inkscape::NodePath::SubPath *sp = node->subpath;
2468         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2469         Inkscape::NodePath::Node *sample_cursor = NULL;
2470         Inkscape::NodePath::Node *sample_end = NULL;
2471         Inkscape::NodePath::Node *delete_cursor = node;
2472         bool just_delete = false;
2474         //find the start of this contiguous selection
2475         //move left to the first node that is not selected
2476         //or the start of the non-closed path
2477         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2478             delete_cursor = curr;
2479         }
2481         //just delete at the beginning of an open path
2482         if (!delete_cursor->p.other) {
2483             sample_cursor = delete_cursor;
2484             just_delete = true;
2485         } else {
2486             sample_cursor = delete_cursor->p.other;
2487         }
2489         //calculate points for each segment
2490         int rate = 5;
2491         float period = 1.0 / rate;
2492         std::vector<Geom::Point> data;
2493         if (!just_delete) {
2494             data.push_back(sample_cursor->pos);
2495             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2496                 //just delete at the end of an open path
2497                 if (!sp->closed && curr == sp->last) {
2498                     just_delete = true;
2499                     break;
2500                 }
2502                 //sample points on the contiguous selected segment
2503                 Geom::Point *bez;
2504                 bez = new Geom::Point [4];
2505                 bez[0] = curr->pos;
2506                 bez[1] = curr->n.pos;
2507                 bez[2] = curr->n.other->p.pos;
2508                 bez[3] = curr->n.other->pos;
2509                 for (int i=1; i<rate; i++) {
2510                     gdouble t = i * period;
2511                     Geom::Point p = bezier_pt(3, bez, t);
2512                     data.push_back(p);
2513                 }
2514                 data.push_back(curr->n.other->pos);
2516                 sample_end = curr->n.other;
2517                 //break if we've come full circle or hit the end of the selection
2518                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2519                     break;
2520                 }
2521             }
2522         }
2524         if (!just_delete) {
2525             //calculate the best fitting single segment and adjust the endpoints
2526             Geom::Point *adata;
2527             adata = new Geom::Point [data.size()];
2528             copy(data.begin(), data.end(), adata);
2530             Geom::Point *bez;
2531             bez = new Geom::Point [4];
2532             //would decreasing error create a better fitting approximation?
2533             gdouble error = 1.0;
2534             gint ret;
2535             ret = Geom::bezier_fit_cubic (bez, adata, data.size(), error);
2537             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2538             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2539             //the resulting nodes behave as expected.
2540             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2541                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2542             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2543                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2545             //adjust endpoints
2546             sample_cursor->n.pos = bez[1];
2547             sample_end->p.pos = bez[2];
2548         }
2550         //destroy this contiguous selection
2551         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2552             Inkscape::NodePath::Node *temp = delete_cursor;
2553             if (delete_cursor->n.other == delete_cursor) {
2554                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2555                 delete_cursor = NULL;
2556             } else {
2557                 delete_cursor = delete_cursor->n.other;
2558             }
2559             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2560             sp_nodepath_node_destroy(temp);
2561         }
2563         sp_nodepath_update_handles(nodepath);
2565         if (!g_slist_find(nodepaths, nodepath))
2566             nodepaths = g_slist_prepend (nodepaths, nodepath);
2567     }
2569     for (GSList *i = nodepaths; i; i = i->next) {
2570         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2571         // different nodepaths will give us one undo event per nodepath
2572         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2574         // if the entire nodepath is removed, delete the selected object.
2575         if (nodepath->subpaths == NULL ||
2576             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2577             //at least 2
2578             sp_nodepath_get_node_count(nodepath) < 2) {
2579             SPDocument *document = sp_desktop_document (nodepath->desktop);
2580             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2581             //delete this nodepath's object, not the entire selection! (though at this time, this
2582             //does not matter)
2583             sp_selection_delete(nodepath->desktop);
2584             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2585                               _("Delete nodes"));
2586         } else {
2587             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2588             sp_nodepath_update_statusbar(nodepath);
2589         }
2590     }
2592     g_slist_free (nodepaths);
2595 /**
2596  * Delete one or more selected nodes.
2597  */
2598 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2600     if (!nodepath) return;
2601     if (!nodepath->selected) return;
2603     /** \todo fixme: do it the right way */
2604     while (nodepath->selected) {
2605        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2606         sp_nodepath_node_destroy(node);
2607     }
2610     //clean up the nodepath (such as for trivial subpaths)
2611     sp_nodepath_cleanup(nodepath);
2613     sp_nodepath_update_handles(nodepath);
2615     // if the entire nodepath is removed, delete the selected object.
2616     if (nodepath->subpaths == NULL ||
2617         sp_nodepath_get_node_count(nodepath) < 2) {
2618         SPDocument *document = sp_desktop_document (nodepath->desktop);
2619         sp_selection_delete(nodepath->desktop);
2620         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2621                           _("Delete nodes"));
2622         return;
2623     }
2625     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2627     sp_nodepath_update_statusbar(nodepath);
2630 /**
2631  * Delete one or more segments between two selected nodes.
2632  * This is the code for 'split'.
2633  */
2634 void
2635 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2637    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2638    Inkscape::NodePath::Node *curr, *next;     //Iterators
2640     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2642     if (g_list_length(nodepath->selected) != 2) {
2643         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2644                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2645         return;
2646     }
2648     //Selected nodes, not inclusive
2649    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2650    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2652     if ( ( a==b)                       ||  //same node
2653          (a->subpath  != b->subpath )  ||  //not the same path
2654          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2655          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2656     {
2657         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2658                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2659         return;
2660     }
2662     //###########################################
2663     //# BEGIN EDITS
2664     //###########################################
2665     //##################################
2666     //# CLOSED PATH
2667     //##################################
2668     if (a->subpath->closed) {
2671         gboolean reversed = FALSE;
2673         //Since we can go in a circle, we need to find the shorter distance.
2674         //  a->b or b->a
2675         start = end = NULL;
2676         int distance    = 0;
2677         int minDistance = 0;
2678         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2679             if (curr==b) {
2680                 //printf("a to b:%d\n", distance);
2681                 start = a;//go from a to b
2682                 end   = b;
2683                 minDistance = distance;
2684                 //printf("A to B :\n");
2685                 break;
2686             }
2687             distance++;
2688         }
2690         //try again, the other direction
2691         distance = 0;
2692         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2693             if (curr==a) {
2694                 //printf("b to a:%d\n", distance);
2695                 if (distance < minDistance) {
2696                     start    = b;  //we go from b to a
2697                     end      = a;
2698                     reversed = TRUE;
2699                     //printf("B to A\n");
2700                 }
2701                 break;
2702             }
2703             distance++;
2704         }
2707         //Copy everything from 'end' to 'start' to a new subpath
2708        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2709         for (curr=end ; curr ; curr=curr->n.other) {
2710             NRPathcode code = (NRPathcode) curr->code;
2711             if (curr == end)
2712                 code = NR_MOVETO;
2713             sp_nodepath_node_new(t, NULL,
2714                                  (Inkscape::NodePath::NodeType)curr->type, code,
2715                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2716             if (curr == start)
2717                 break;
2718         }
2719         sp_nodepath_subpath_destroy(a->subpath);
2722     }
2726     //##################################
2727     //# OPEN PATH
2728     //##################################
2729     else {
2731         //We need to get the direction of the list between A and B
2732         //Can we walk from a to b?
2733         start = end = NULL;
2734         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2735             if (curr==b) {
2736                 start = a;  //did it!  we go from a to b
2737                 end   = b;
2738                 //printf("A to B\n");
2739                 break;
2740             }
2741         }
2742         if (!start) {//didn't work?  let's try the other direction
2743             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2744                 if (curr==a) {
2745                     start = b;  //did it!  we go from b to a
2746                     end   = a;
2747                     //printf("B to A\n");
2748                     break;
2749                 }
2750             }
2751         }
2752         if (!start) {
2753             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2754                                                      _("Cannot find path between nodes."));
2755             return;
2756         }
2760         //Copy everything after 'end' to a new subpath
2761        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2762         for (curr=end ; curr ; curr=curr->n.other) {
2763             NRPathcode code = (NRPathcode) curr->code;
2764             if (curr == end)
2765                 code = NR_MOVETO;
2766             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2767                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2768         }
2770         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2771         for (curr = start->n.other ; curr  ; curr=next) {
2772             next = curr->n.other;
2773             sp_nodepath_node_destroy(curr);
2774         }
2776     }
2777     //###########################################
2778     //# END EDITS
2779     //###########################################
2781     //clean up the nodepath (such as for trivial subpaths)
2782     sp_nodepath_cleanup(nodepath);
2784     sp_nodepath_update_handles(nodepath);
2786     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2788     sp_nodepath_update_statusbar(nodepath);
2791 /**
2792  * Call sp_nodepath_set_line() for all selected segments.
2793  */
2794 void
2795 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2797     if (nodepath == NULL) return;
2799     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2800        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2801         g_assert(n->selected);
2802         if (n->p.other && n->p.other->selected) {
2803             sp_nodepath_set_line_type(n, code);
2804         }
2805     }
2807     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2810 /**
2811  * Call sp_nodepath_convert_node_type() for all selected nodes.
2812  */
2813 void
2814 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2816     if (nodepath == NULL) return;
2818     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2820     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2821         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2822     }
2824     sp_nodepath_update_repr(nodepath, _("Change node type"));
2827 /**
2828  * Change select status of node, update its own and neighbour handles.
2829  */
2830 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2832     node->selected = selected;
2834     if (selected) {
2835         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 11 : 9);
2836         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2837         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2838         sp_knot_update_ctrl(node->knot);
2839     } else {
2840         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 9 : 7);
2841         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2842         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2843         sp_knot_update_ctrl(node->knot);
2844     }
2846     sp_node_update_handles(node);
2847     if (node->n.other) sp_node_update_handles(node->n.other);
2848     if (node->p.other) sp_node_update_handles(node->p.other);
2851 /**
2852 \brief Select a node
2853 \param node     The node to select
2854 \param incremental   If true, add to selection, otherwise deselect others
2855 \param override   If true, always select this node, otherwise toggle selected status
2856 */
2857 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2859     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2861     if (incremental) {
2862         if (override) {
2863             if (!g_list_find(nodepath->selected, node)) {
2864                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2865             }
2866             sp_node_set_selected(node, TRUE);
2867         } else { // toggle
2868             if (node->selected) {
2869                 g_assert(g_list_find(nodepath->selected, node));
2870                 nodepath->selected = g_list_remove(nodepath->selected, node);
2871             } else {
2872                 g_assert(!g_list_find(nodepath->selected, node));
2873                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2874             }
2875             sp_node_set_selected(node, !node->selected);
2876         }
2877     } else {
2878         sp_nodepath_deselect(nodepath);
2879         nodepath->selected = g_list_prepend(nodepath->selected, node);
2880         sp_node_set_selected(node, TRUE);
2881     }
2883     sp_nodepath_update_statusbar(nodepath);
2887 /**
2888 \brief Deselect all nodes in the nodepath
2889 */
2890 void
2891 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2893     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2895     while (nodepath->selected) {
2896         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2897         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2898     }
2899     sp_nodepath_update_statusbar(nodepath);
2902 /**
2903 \brief Select or invert selection of all nodes in the nodepath
2904 */
2905 void
2906 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2908     if (!nodepath) return;
2910     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2911        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2912         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2913            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2914            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2915         }
2916     }
2919 /**
2920  * If nothing selected, does the same as sp_nodepath_select_all();
2921  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2922  * (i.e., similar to "select all in layer", with the "selected" subpaths
2923  * being treated as "layers" in the path).
2924  */
2925 void
2926 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2928     if (!nodepath) return;
2930     if (g_list_length (nodepath->selected) == 0) {
2931         sp_nodepath_select_all (nodepath, invert);
2932         return;
2933     }
2935     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2936     GSList *subpaths = NULL;
2938     for (GList *l = copy; l != NULL; l = l->next) {
2939         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2940         Inkscape::NodePath::SubPath *subpath = n->subpath;
2941         if (!g_slist_find (subpaths, subpath))
2942             subpaths = g_slist_prepend (subpaths, subpath);
2943     }
2945     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2946         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2947         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2948             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2949             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2950         }
2951     }
2953     g_slist_free (subpaths);
2954     g_list_free (copy);
2957 /**
2958  * \brief Select the node after the last selected; if none is selected,
2959  * select the first within path.
2960  */
2961 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2963     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2965    Inkscape::NodePath::Node *last = NULL;
2966     if (nodepath->selected) {
2967         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2968            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2969             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2970             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2971                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2972                 if (node->selected) {
2973                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2974                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2975                             if (spl->next) { // there's a next subpath
2976                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2977                                 last = subpath_next->first;
2978                             } else if (spl->prev) { // there's a previous subpath
2979                                 last = NULL; // to be set later to the first node of first subpath
2980                             } else {
2981                                 last = node->n.other;
2982                             }
2983                         } else {
2984                             last = node->n.other;
2985                         }
2986                     } else {
2987                         if (node->n.other) {
2988                             last = node->n.other;
2989                         } else {
2990                             if (spl->next) { // there's a next subpath
2991                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2992                                 last = subpath_next->first;
2993                             } else if (spl->prev) { // there's a previous subpath
2994                                 last = NULL; // to be set later to the first node of first subpath
2995                             } else {
2996                                 last = (Inkscape::NodePath::Node *) subpath->first;
2997                             }
2998                         }
2999                     }
3000                 }
3001             }
3002         }
3003         sp_nodepath_deselect(nodepath);
3004     }
3006     if (last) { // there's at least one more node after selected
3007         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3008     } else { // no more nodes, select the first one in first subpath
3009        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
3010         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
3011     }
3014 /**
3015  * \brief Select the node before the first selected; if none is selected,
3016  * select the last within path
3017  */
3018 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
3020     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
3022    Inkscape::NodePath::Node *last = NULL;
3023     if (nodepath->selected) {
3024         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
3025            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3026             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
3027                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3028                 if (node->selected) {
3029                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
3030                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
3031                             if (spl->prev) { // there's a prev subpath
3032                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3033                                 last = subpath_prev->last;
3034                             } else if (spl->next) { // there's a next subpath
3035                                 last = NULL; // to be set later to the last node of last subpath
3036                             } else {
3037                                 last = node->p.other;
3038                             }
3039                         } else {
3040                             last = node->p.other;
3041                         }
3042                     } else {
3043                         if (node->p.other) {
3044                             last = node->p.other;
3045                         } else {
3046                             if (spl->prev) { // there's a prev subpath
3047                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3048                                 last = subpath_prev->last;
3049                             } else if (spl->next) { // there's a next subpath
3050                                 last = NULL; // to be set later to the last node of last subpath
3051                             } else {
3052                                 last = (Inkscape::NodePath::Node *) subpath->last;
3053                             }
3054                         }
3055                     }
3056                 }
3057             }
3058         }
3059         sp_nodepath_deselect(nodepath);
3060     }
3062     if (last) { // there's at least one more node before selected
3063         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3064     } else { // no more nodes, select the last one in last subpath
3065         GList *spl = g_list_last(nodepath->subpaths);
3066        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3067         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
3068     }
3071 /**
3072  * \brief Select all nodes that are within the rectangle.
3073  */
3074 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, Geom::Rect const &b, gboolean incremental)
3076     if (!incremental) {
3077         sp_nodepath_deselect(nodepath);
3078     }
3080     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3081        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3082         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3083            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3085             if (b.contains(node->pos)) {
3086                 sp_nodepath_node_select(node, TRUE, TRUE);
3087             }
3088         }
3089     }
3093 void
3094 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3096     g_assert (n);
3097     g_assert (nodepath);
3098     g_assert (n->subpath->nodepath == nodepath);
3100     if (g_list_length (nodepath->selected) == 0) {
3101         if (grow > 0) {
3102             sp_nodepath_node_select(n, TRUE, TRUE);
3103         }
3104         return;
3105     }
3107     if (g_list_length (nodepath->selected) == 1) {
3108         if (grow < 0) {
3109             sp_nodepath_deselect (nodepath);
3110             return;
3111         }
3112     }
3114         double n_sel_range = 0, p_sel_range = 0;
3115             Inkscape::NodePath::Node *farthest_n_node = n;
3116             Inkscape::NodePath::Node *farthest_p_node = n;
3118         // Calculate ranges
3119         {
3120             double n_range = 0, p_range = 0;
3121             bool n_going = true, p_going = true;
3122             Inkscape::NodePath::Node *n_node = n;
3123             Inkscape::NodePath::Node *p_node = n;
3124             do {
3125                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3126                 if (n_node && n_going)
3127                     n_node = n_node->n.other;
3128                 if (n_node == NULL) {
3129                     n_going = false;
3130                 } else {
3131                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3132                     if (n_node->selected) {
3133                         n_sel_range = n_range;
3134                         farthest_n_node = n_node;
3135                     }
3136                     if (n_node == p_node) {
3137                         n_going = false;
3138                         p_going = false;
3139                     }
3140                 }
3141                 if (p_node && p_going)
3142                     p_node = p_node->p.other;
3143                 if (p_node == NULL) {
3144                     p_going = false;
3145                 } else {
3146                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3147                     if (p_node->selected) {
3148                         p_sel_range = p_range;
3149                         farthest_p_node = p_node;
3150                     }
3151                     if (p_node == n_node) {
3152                         n_going = false;
3153                         p_going = false;
3154                     }
3155                 }
3156             } while (n_going || p_going);
3157         }
3159     if (grow > 0) {
3160         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3161                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3162         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3163                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3164         }
3165     } else {
3166         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3167                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3168         } else if (farthest_p_node && farthest_p_node->selected) {
3169                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3170         }
3171     }
3174 void
3175 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3177     g_assert (n);
3178     g_assert (nodepath);
3179     g_assert (n->subpath->nodepath == nodepath);
3181     if (g_list_length (nodepath->selected) == 0) {
3182         if (grow > 0) {
3183             sp_nodepath_node_select(n, TRUE, TRUE);
3184         }
3185         return;
3186     }
3188     if (g_list_length (nodepath->selected) == 1) {
3189         if (grow < 0) {
3190             sp_nodepath_deselect (nodepath);
3191             return;
3192         }
3193     }
3195     Inkscape::NodePath::Node *farthest_selected = NULL;
3196     double farthest_dist = 0;
3198     Inkscape::NodePath::Node *closest_unselected = NULL;
3199     double closest_dist = NR_HUGE;
3201     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3202        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3203         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3204            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3205            if (node == n)
3206                continue;
3207            if (node->selected) {
3208                if (Geom::L2(node->pos - n->pos) > farthest_dist) {
3209                    farthest_dist = Geom::L2(node->pos - n->pos);
3210                    farthest_selected = node;
3211                }
3212            } else {
3213                if (Geom::L2(node->pos - n->pos) < closest_dist) {
3214                    closest_dist = Geom::L2(node->pos - n->pos);
3215                    closest_unselected = node;
3216                }
3217            }
3218         }
3219     }
3221     if (grow > 0) {
3222         if (closest_unselected) {
3223             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3224         }
3225     } else {
3226         if (farthest_selected) {
3227             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3228         }
3229     }
3233 /**
3234 \brief  Saves all nodes' and handles' current positions in their origin members
3235 */
3236 void
3237 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3239     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3240        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3241         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3242            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3243            n->origin = n->pos;
3244            n->p.origin = n->p.pos;
3245            n->n.origin = n->n.pos;
3246         }
3247     }
3250 /**
3251 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3252 */
3253 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3255     GList *r = NULL;
3256     if (nodepath->selected) {
3257         guint i = 0;
3258         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3259             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3260             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3261                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3262                 i++;
3263                 if (node->selected) {
3264                     r = g_list_append(r, GINT_TO_POINTER(i));
3265                 }
3266             }
3267         }
3268     }
3269     return r;
3272 /**
3273 \brief  Restores selection by selecting nodes whose positions are in the list
3274 */
3275 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3277     sp_nodepath_deselect(nodepath);
3279     guint i = 0;
3280     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3281        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3282         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3283            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3284             i++;
3285             if (g_list_find(r, GINT_TO_POINTER(i))) {
3286                 sp_nodepath_node_select(node, TRUE, TRUE);
3287             }
3288         }
3289     }
3293 /**
3294 \brief Adjusts handle according to node type and line code.
3295 */
3296 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3298     g_assert(node);
3300     // nothing to do for auto nodes (sp_node_adjust_handles() does the job)
3301     if (node->type == Inkscape::NodePath::NODE_AUTO)
3302         return;
3304    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3305    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3307    // nothing to do if we are an end node
3308     if (me->other == NULL) return;
3309     if (other->other == NULL) return;
3311     // nothing to do if we are a cusp node
3312     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3314     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3315     NRPathcode mecode;
3316     if (which_adjust == 1) {
3317         mecode = (NRPathcode)me->other->code;
3318     } else {
3319         mecode = (NRPathcode)node->code;
3320     }
3321     if (mecode == NR_LINETO) return;
3323     if (sp_node_side_is_line(node, other)) {
3324         // other is a line, and we are either smooth or symm
3325        Inkscape::NodePath::Node *othernode = other->other;
3326         double len = Geom::L2(me->pos - node->pos);
3327         Geom::Point delta = node->pos - othernode->pos;
3328         double linelen = Geom::L2(delta);
3329         if (linelen < 1e-18)
3330             return;
3331         me->pos = node->pos + (len / linelen)*delta;
3332         return;
3333     }
3335     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3336         // symmetrize
3337         me->pos = 2 * node->pos - other->pos;
3338         return;
3339     } else {
3340         // smoothify
3341         double len = Geom::L2(me->pos - node->pos);
3342         Geom::Point delta = other->pos - node->pos;
3343         double otherlen = Geom::L2(delta);
3344         if (otherlen < 1e-18) return;
3345         me->pos = node->pos - (len / otherlen) * delta;
3346     }
3349 /**
3350  \brief Adjusts both handles according to node type and line code
3351  */
3352 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3354     g_assert(node);
3356     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3358     /* we are either smooth or symm */
3360     if (node->p.other == NULL) return;
3361     if (node->n.other == NULL) return;
3363     if (node->type == Inkscape::NodePath::NODE_AUTO) {
3364         sp_node_adjust_handles_auto(node);
3365         return;
3366     }
3368     if (sp_node_side_is_line(node, &node->p)) {
3369         sp_node_adjust_handle(node, 1);
3370         return;
3371     }
3373     if (sp_node_side_is_line(node, &node->n)) {
3374         sp_node_adjust_handle(node, -1);
3375         return;
3376     }
3378     /* both are curves */
3379     Geom::Point const delta( node->n.pos - node->p.pos );
3381     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3382         node->p.pos = node->pos - delta / 2;
3383         node->n.pos = node->pos + delta / 2;
3384         return;
3385     }
3387     /* We are smooth */
3388     double plen = Geom::L2(node->p.pos - node->pos);
3389     if (plen < 1e-18) return;
3390     double nlen = Geom::L2(node->n.pos - node->pos);
3391     if (nlen < 1e-18) return;
3392     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3393     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3396 static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node)
3398     if (node->p.other == NULL || node->n.other == NULL) {
3399         node->p.pos = node->pos;
3400         node->n.pos = node->pos;
3401         return;
3402     }
3404     Geom::Point leg_prev = to_2geom(node->p.other->pos - node->pos);
3405     Geom::Point leg_next = to_2geom(node->n.other->pos - node->pos);
3407     double norm_leg_prev = Geom::L2(leg_prev);
3408     double norm_leg_next = Geom::L2(leg_next);
3410     Geom::Point delta;
3411     if (norm_leg_next > 0.0) {
3412         delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
3413         delta.normalize();
3414     }
3416     node->p.pos = node->pos - norm_leg_prev / 3 * delta;
3417     node->n.pos = node->pos + norm_leg_next / 3 * delta;
3420 /**
3421  * Node event callback.
3422  */
3423 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3425     gboolean ret = FALSE;
3426     switch (event->type) {
3427         case GDK_ENTER_NOTIFY:
3428             Inkscape::NodePath::Path::active_node = n;
3429             break;
3430         case GDK_LEAVE_NOTIFY:
3431             Inkscape::NodePath::Path::active_node = NULL;
3432             break;
3433         case GDK_SCROLL:
3434             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3435                 switch (event->scroll.direction) {
3436                     case GDK_SCROLL_UP:
3437                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3438                         break;
3439                     case GDK_SCROLL_DOWN:
3440                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3441                         break;
3442                     default:
3443                         break;
3444                 }
3445                 ret = TRUE;
3446             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3447                 switch (event->scroll.direction) {
3448                     case GDK_SCROLL_UP:
3449                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3450                         break;
3451                     case GDK_SCROLL_DOWN:
3452                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3453                         break;
3454                     default:
3455                         break;
3456                 }
3457                 ret = TRUE;
3458             }
3459             break;
3460         case GDK_KEY_PRESS:
3461             switch (get_group0_keyval (&event->key)) {
3462                 case GDK_space:
3463                     if (event->key.state & GDK_BUTTON1_MASK) {
3464                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3465                         stamp_repr(nodepath);
3466                         ret = TRUE;
3467                     }
3468                     break;
3469                 case GDK_Page_Up:
3470                     if (event->key.state & GDK_CONTROL_MASK) {
3471                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3472                     } else {
3473                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3474                     }
3475                     break;
3476                 case GDK_Page_Down:
3477                     if (event->key.state & GDK_CONTROL_MASK) {
3478                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3479                     } else {
3480                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3481                     }
3482                     break;
3483                 default:
3484                     break;
3485             }
3486             break;
3487         default:
3488             break;
3489     }
3491     return ret;
3494 /**
3495  * Handle keypress on node; directly called.
3496  */
3497 gboolean node_key(GdkEvent *event)
3499     Inkscape::NodePath::Path *np;
3501     // there is no way to verify nodes so set active_node to nil when deleting!!
3502     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3504     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3505         gint ret = FALSE;
3506         switch (get_group0_keyval (&event->key)) {
3507             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3508             case GDK_BackSpace:
3509                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3510                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3511                 sp_nodepath_update_repr(np, _("Delete node"));
3512                 Inkscape::NodePath::Path::active_node = NULL;
3513                 ret = TRUE;
3514                 break;
3515             case GDK_c:
3516                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3517                 ret = TRUE;
3518                 break;
3519             case GDK_s:
3520                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3521                 ret = TRUE;
3522                 break;
3523             case GDK_a:
3524                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_AUTO);
3525                 ret = TRUE;
3526                 break;
3527             case GDK_y:
3528                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3529                 ret = TRUE;
3530                 break;
3531             case GDK_b:
3532                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3533                 ret = TRUE;
3534                 break;
3535         }
3536         return ret;
3537     }
3538     return FALSE;
3541 /**
3542  * Mouseclick on node callback.
3543  */
3544 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3546    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3548     if (state & GDK_CONTROL_MASK) {
3549         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3551         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3552             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3553                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3554             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3555                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3556             } else if (n->type == Inkscape::NodePath::NODE_SYMM) {
3557                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_AUTO);
3558             } else {
3559                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3560             }
3561             sp_nodepath_update_repr(nodepath, _("Change node type"));
3562             sp_nodepath_update_statusbar(nodepath);
3564         } else { //ctrl+alt+click: delete node
3565             GList *node_to_delete = NULL;
3566             node_to_delete = g_list_append(node_to_delete, n);
3567             sp_node_delete_preserve(node_to_delete);
3568         }
3570     } else {
3571         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3572     }
3575 /**
3576  * Mouse grabbed node callback.
3577  */
3578 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3580    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3582     if (!n->selected) {
3583         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3584     }
3586     n->is_dragging = true;
3587     sp_canvas_set_snap_delay_active(n->subpath->nodepath->desktop->canvas, true);
3588     // Reconstruct and store the location of the mouse pointer at the time when we started dragging (needed for snapping)
3589     n->subpath->nodepath->drag_origin_mouse = knot->grabbed_rel_pos + knot->drag_origin;
3591     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3593     sp_nodepath_remember_origins (n->subpath->nodepath);
3596 /**
3597  * Mouse ungrabbed node callback.
3598  */
3599 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3601    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3603    n->dragging_out = NULL;
3604    n->is_dragging = false;
3605    sp_canvas_set_snap_delay_active(n->subpath->nodepath->desktop->canvas, false);
3606    n->subpath->nodepath->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
3607    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3609    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3612 /**
3613  * The point on a line, given by its angle, closest to the given point.
3614  * \param p  A point.
3615  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3616  * \param closest  Pointer to the point struct where the result is stored.
3617  * \todo FIXME: use dot product perhaps?
3618  */
3619 static void point_line_closest(Geom::Point *p, double a, Geom::Point *closest)
3621     if (a == HUGE_VAL) { // vertical
3622         *closest = Geom::Point(0, (*p)[Geom::Y]);
3623     } else {
3624         (*closest)[Geom::X] = ( a * (*p)[Geom::Y] + (*p)[Geom::X]) / (a*a + 1);
3625         (*closest)[Geom::Y] = a * (*closest)[Geom::X];
3626     }
3629 /**
3630  * Distance from the point to a line given by its angle.
3631  * \param p  A point.
3632  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3633  */
3634 static double point_line_distance(Geom::Point *p, double a)
3636     Geom::Point c;
3637     point_line_closest(p, a, &c);
3638     return sqrt(((*p)[Geom::X] - c[Geom::X])*((*p)[Geom::X] - c[Geom::X]) + ((*p)[Geom::Y] - c[Geom::Y])*((*p)[Geom::Y] - c[Geom::Y]));
3641 /**
3642  * Callback for node "request" signal.
3643  * \todo fixme: This goes to "moved" event? (lauris)
3644  */
3645 static gboolean
3646 node_request(SPKnot */*knot*/, Geom::Point const &p, guint state, gpointer data)
3648     double yn, xn, yp, xp;
3649     double an, ap, na, pa;
3650     double d_an, d_ap, d_na, d_pa;
3651     gboolean collinear = FALSE;
3652     Geom::Point c;
3653     Geom::Point pr;
3655     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3657     n->subpath->nodepath->desktop->snapindicator->remove_snaptarget();
3659     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3660     if ( (!n->subpath->nodepath->straight_path) &&
3661          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3662            || n->dragging_out ) )
3663     {
3664        Geom::Point mouse = p;
3666        if (!n->dragging_out) {
3667            // This is the first drag-out event; find out which handle to drag out
3668            double appr_n = (n->n.other ? Geom::L2(n->n.other->pos - n->pos) - Geom::L2(n->n.other->pos - p) : -HUGE_VAL);
3669            double appr_p = (n->p.other ? Geom::L2(n->p.other->pos - n->pos) - Geom::L2(n->p.other->pos - p) : -HUGE_VAL);
3671            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3672                return FALSE;
3674            Inkscape::NodePath::NodeSide *opposite;
3675            if (appr_p > appr_n) { // closer to p
3676                n->dragging_out = &n->p;
3677                opposite = &n->n;
3678                n->code = NR_CURVETO;
3679            } else if (appr_p < appr_n) { // closer to n
3680                n->dragging_out = &n->n;
3681                opposite = &n->p;
3682                n->n.other->code = NR_CURVETO;
3683            } else { // p and n nodes are the same
3684                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3685                    n->dragging_out = &n->p;
3686                    opposite = &n->n;
3687                    n->code = NR_CURVETO;
3688                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3689                    n->dragging_out = &n->n;
3690                    opposite = &n->p;
3691                    n->n.other->code = NR_CURVETO;
3692                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3693                    double appr_other_n = (n->n.other ? Geom::L2(n->n.other->n.pos - n->pos) - Geom::L2(n->n.other->n.pos - p) : -HUGE_VAL);
3694                    double appr_other_p = (n->n.other ? Geom::L2(n->n.other->p.pos - n->pos) - Geom::L2(n->n.other->p.pos - p) : -HUGE_VAL);
3695                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3696                        n->dragging_out = &n->n;
3697                        opposite = &n->p;
3698                        n->n.other->code = NR_CURVETO;
3699                    } else { // closer to other's n handle
3700                        n->dragging_out = &n->p;
3701                        opposite = &n->n;
3702                        n->code = NR_CURVETO;
3703                    }
3704                }
3705            }
3707            // if there's another handle, make sure the one we drag out starts parallel to it
3708            if (opposite->pos != n->pos) {
3709                mouse = n->pos - Geom::L2(mouse - n->pos) * Geom::unit_vector(opposite->pos - n->pos);
3710            }
3712            // knots might not be created yet!
3713            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3714            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3715        }
3717        // pass this on to the handle-moved callback
3718        node_handle_moved(n->dragging_out->knot, mouse, state, (gpointer) n);
3719        sp_node_update_handles(n);
3720        return TRUE;
3721    }
3723     if (state & GDK_CONTROL_MASK) { // constrained motion
3725         // calculate relative distances of handles
3726         // n handle:
3727         yn = n->n.pos[Geom::Y] - n->pos[Geom::Y];
3728         xn = n->n.pos[Geom::X] - n->pos[Geom::X];
3729         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3730         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3731             if (n->n.other) { // if there is the next point
3732                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3733                     yn = n->n.other->origin[Geom::Y] - n->origin[Geom::Y]; // use origin because otherwise the direction will change as you drag
3734                     xn = n->n.other->origin[Geom::X] - n->origin[Geom::X];
3735             }
3736         }
3737         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3738         if (yn < 0) { xn = -xn; yn = -yn; }
3740         // p handle:
3741         yp = n->p.pos[Geom::Y] - n->pos[Geom::Y];
3742         xp = n->p.pos[Geom::X] - n->pos[Geom::X];
3743         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3744         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3745             if (n->p.other) {
3746                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3747                     yp = n->p.other->origin[Geom::Y] - n->origin[Geom::Y];
3748                     xp = n->p.other->origin[Geom::X] - n->origin[Geom::X];
3749             }
3750         }
3751         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3752         if (yp < 0) { xp = -xp; yp = -yp; }
3754         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3755             // sliding on handles, only if at least one of the handles is non-vertical
3756             // (otherwise it's the same as ctrl+drag anyway)
3758             // calculate angles of the handles
3759             if (xn == 0) {
3760                 if (yn == 0) { // no handle, consider it the continuation of the other one
3761                     an = 0;
3762                     collinear = TRUE;
3763                 }
3764                 else an = 0; // vertical; set the angle to horizontal
3765             } else an = yn/xn;
3767             if (xp == 0) {
3768                 if (yp == 0) { // no handle, consider it the continuation of the other one
3769                     ap = an;
3770                 }
3771                 else ap = 0; // vertical; set the angle to horizontal
3772             } else  ap = yp/xp;
3774             if (collinear) an = ap;
3776             // angles of the perpendiculars; HUGE_VAL means vertical
3777             if (an == 0) na = HUGE_VAL; else na = -1/an;
3778             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3780             // mouse point relative to the node's original pos
3781             pr = p - n->origin;
3783             // distances to the four lines (two handles and two perpendiculars)
3784             d_an = point_line_distance(&pr, an);
3785             d_na = point_line_distance(&pr, na);
3786             d_ap = point_line_distance(&pr, ap);
3787             d_pa = point_line_distance(&pr, pa);
3789             // find out which line is the closest, save its closest point in c
3790             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3791                 point_line_closest(&pr, an, &c);
3792             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3793                 point_line_closest(&pr, ap, &c);
3794             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3795                 point_line_closest(&pr, na, &c);
3796             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3797                 point_line_closest(&pr, pa, &c);
3798             }
3800             // move the node to the closest point
3801             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3802                                             n->origin[Geom::X] + c[Geom::X] - n->pos[Geom::X],
3803                                             n->origin[Geom::Y] + c[Geom::Y] - n->pos[Geom::Y],
3804                                             true);
3806         } else {  // constraining to hor/vert
3808             if (fabs(p[Geom::X] - n->origin[Geom::X]) > fabs(p[Geom::Y] - n->origin[Geom::Y])) { // snap to hor
3809                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3810                                                 p[Geom::X] - n->pos[Geom::X],
3811                                                 n->origin[Geom::Y] - n->pos[Geom::Y],
3812                                                 true,
3813                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::X]));
3814             } else { // snap to vert
3815                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3816                                                 n->origin[Geom::X] - n->pos[Geom::X],
3817                                                 p[Geom::Y] - n->pos[Geom::Y],
3818                                                 true,
3819                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::Y]));
3820             }
3821         }
3822     } else { // move freely
3823         if (n->is_dragging) {
3824             if (state & GDK_MOD1_MASK) { // sculpt
3825                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, p - n->origin);
3826             } else {
3827                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3828                                             p[Geom::X] - n->pos[Geom::X],
3829                                             p[Geom::Y] - n->pos[Geom::Y],
3830                                             (state & GDK_SHIFT_MASK) == 0);
3831             }
3832         }
3833     }
3835     n->subpath->nodepath->desktop->scroll_to_point(p);
3837     return TRUE;
3840 /**
3841  * Node handle clicked callback.
3842  */
3843 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3845    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3847     if (state & GDK_CONTROL_MASK) { // "delete" handle
3848         if (n->p.knot == knot) {
3849             n->p.pos = n->pos;
3850         } else if (n->n.knot == knot) {
3851             n->n.pos = n->pos;
3852         }
3853         sp_node_update_handles(n);
3854         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3855         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3856         sp_nodepath_update_statusbar(nodepath);
3858     } else { // just select or add to selection, depending in Shift
3859         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3860     }
3863 /**
3864  * Node handle grabbed callback.
3865  */
3866 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3868    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3870     // convert auto -> smooth when dragging handle
3871    if (n->type == Inkscape::NodePath::NODE_AUTO) {
3872         n->type = Inkscape::NodePath::NODE_SMOOTH;
3873         sp_nodepath_update_node_knot (n);
3874    }
3876     if (!n->selected) {
3877         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3878     }
3880     // remember the origin point of the handle
3881     if (n->p.knot == knot) {
3882         n->p.origin_radial = n->p.pos - n->pos;
3883     } else if (n->n.knot == knot) {
3884         n->n.origin_radial = n->n.pos - n->pos;
3885     } else {
3886         g_assert_not_reached();
3887     }
3889     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3892 /**
3893  * Node handle ungrabbed callback.
3894  */
3895 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3897    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3899     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3900     if (n->p.knot == knot) {
3901         n->p.origin_radial.a = 0;
3902         sp_knot_set_position(knot, n->p.pos, state);
3903     } else if (n->n.knot == knot) {
3904         n->n.origin_radial.a = 0;
3905         sp_knot_set_position(knot, n->n.pos, state);
3906     } else {
3907         g_assert_not_reached();
3908     }
3910     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3913 /**
3914  * Node handle "request" signal callback.
3915  */
3916 static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, gpointer data)
3918     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3920     Inkscape::NodePath::NodeSide *me, *opposite;
3921     gint which;
3922     if (n->p.knot == knot) {
3923         me = &n->p;
3924         opposite = &n->n;
3925         which = -1;
3926     } else if (n->n.knot == knot) {
3927         me = &n->n;
3928         opposite = &n->p;
3929         which = 1;
3930     } else {
3931         me = opposite = NULL;
3932         which = 0;
3933         g_assert_not_reached();
3934     }
3936     SPDesktop *desktop = n->subpath->nodepath->desktop;
3937     SnapManager &m = desktop->namedview->snap_manager;
3938     m.setup(desktop, true, n->subpath->nodepath->item);
3939     Inkscape::SnappedPoint s;
3941     if ((state & GDK_SHIFT_MASK) != 0) {
3942         // We will not try to snap when the shift-key is pressed
3943         // so remove the old snap indicator and don't wait for it to time-out
3944         desktop->snapindicator->remove_snaptarget();
3945     }
3947     Inkscape::NodePath::Node *othernode = opposite->other;
3948     if (othernode) {
3949         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3950             /* We are smooth node adjacent with line */
3951             Geom::Point const delta = p - n->pos;
3952             Geom::Coord const len = Geom::L2(delta);
3953             Inkscape::NodePath::Node *othernode = opposite->other;
3954             Geom::Point const ndelta = n->pos - othernode->pos;
3955             Geom::Coord const linelen = Geom::L2(ndelta);
3956             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3957                 Geom::Coord const scal = dot(delta, ndelta) / linelen;
3958                 p = n->pos + (scal / linelen) * ndelta;
3959             }
3960             if ((state & GDK_SHIFT_MASK) == 0) {
3961                 s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::Snapper::ConstraintLine(p, ndelta));
3962             }
3963         } else {
3964             if ((state & GDK_SHIFT_MASK) == 0) {
3965                 s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p);
3966             }
3967         }
3968     } else {
3969         if ((state & GDK_SHIFT_MASK) == 0) {
3970             s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p);
3971         }
3972     }
3974     s.getPoint(p);
3976     sp_node_adjust_handle(n, -which);
3978     return FALSE;
3981 /**
3982  * Node handle moved callback.
3983  */
3984 static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data)
3986    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3987    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3989    Inkscape::NodePath::NodeSide *me;
3990    Inkscape::NodePath::NodeSide *other;
3991     if (n->p.knot == knot) {
3992         me = &n->p;
3993         other = &n->n;
3994     } else if (n->n.knot == knot) {
3995         me = &n->n;
3996         other = &n->p;
3997     } else {
3998         me = NULL;
3999         other = NULL;
4000         g_assert_not_reached();
4001     }
4003     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
4004     Radial rme(me->pos - n->pos);
4005     Radial rother(other->pos - n->pos);
4006     Radial rnew(p - n->pos);
4008     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
4009         int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
4010         /* 0 interpreted as "no snapping". */
4012         // 1. Snap to the closest PI/snaps angle, starting from zero.
4013         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
4015         // 2. Snap to the original angle, its opposite and perpendiculars
4016         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
4017             /* The closest PI/2 angle, starting from original angle */
4018             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
4020             // Snap to the closest.
4021             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
4022                        ? a_snapped
4023                        : a_ortho );
4024         }
4026         // 3. Snap to the angle of the opposite line, if any
4027         Inkscape::NodePath::Node *othernode = other->other;
4028         if (othernode) {
4029             Geom::Point other_to_snap(0,0);
4030             if (sp_node_side_is_line(n, other)) {
4031                 other_to_snap = othernode->pos - n->pos;
4032             } else {
4033                 other_to_snap = other->pos - n->pos;
4034             }
4035             if (Geom::L2(other_to_snap) > 1e-3) {
4036                 Radial rother_to_snap(other_to_snap);
4037                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
4038                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
4040                 // Snap to the closest.
4041                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
4042                        ? a_snapped
4043                        : a_oppo );
4044             }
4045         }
4047         rnew.a = a_snapped;
4048     }
4050     if (state & GDK_MOD1_MASK) {
4051         // lock handle length
4052         rnew.r = me->origin_radial.r;
4053     }
4055     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
4056         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
4057         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
4058         rother.a += rnew.a - rme.a;
4059         other->pos = Geom::Point(rother) + n->pos;
4060         if (other->knot) {
4061             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
4062             sp_knot_moveto(other->knot, other->pos);
4063         }
4064     }
4066     me->pos = Geom::Point(rnew) + n->pos;
4067     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
4069     // move knot, but without emitting the signal:
4070     // we cannot emit a "moved" signal because we're now processing it
4071     sp_knot_moveto(me->knot, me->pos);
4073     update_object(n->subpath->nodepath);
4075     /* status text */
4076     SPDesktop *desktop = n->subpath->nodepath->desktop;
4077     if (!desktop) return;
4078     SPEventContext *ec = desktop->event_context;
4079     if (!ec) return;
4081     Inkscape::MessageContext *mc = get_message_context(ec);
4083     if (!mc) return;
4085     double degrees = 180 / M_PI * rnew.a;
4086     if (degrees > 180) degrees -= 360;
4087     if (degrees < -180) degrees += 360;
4088     if (prefs->getBool("/options/compassangledisplay/value"))
4089         degrees = angle_to_compass (degrees);
4091     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
4093     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
4094          _("<b>Node handle</b>: angle %0.2f&#176;, length %s; with <b>Ctrl</b> to snap angle; with <b>Alt</b> to lock length; with <b>Shift</b> to rotate both handles"), degrees, length->str);
4096     g_string_free(length, TRUE);
4099 /**
4100  * Node handle event callback.
4101  */
4102 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
4104     gboolean ret = FALSE;
4105     switch (event->type) {
4106         case GDK_KEY_PRESS:
4107             switch (get_group0_keyval (&event->key)) {
4108                 case GDK_space:
4109                     if (event->key.state & GDK_BUTTON1_MASK) {
4110                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
4111                         stamp_repr(nodepath);
4112                         ret = TRUE;
4113                     }
4114                     break;
4115                 default:
4116                     break;
4117             }
4118             break;
4119         case GDK_ENTER_NOTIFY:
4120             // we use an experimentally determined threshold that seems to work fine
4121             if (Geom::L2(n->pos - knot->pos) < 0.75)
4122                 Inkscape::NodePath::Path::active_node = n;
4123             break;
4124         case GDK_LEAVE_NOTIFY:
4125             // we use an experimentally determined threshold that seems to work fine
4126             if (Geom::L2(n->pos - knot->pos) < 0.75)
4127                 Inkscape::NodePath::Path::active_node = NULL;
4128             break;
4129         default:
4130             break;
4131     }
4133     return ret;
4136 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
4137                                  Radial &rme, Radial &rother, gboolean const both)
4139     rme.a += angle;
4140     if ( both
4141          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4142          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4143     {
4144         rother.a += angle;
4145     }
4148 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
4149                                         Radial &rme, Radial &rother, gboolean const both)
4151     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
4153     gdouble r;
4154     if ( both
4155          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4156          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4157     {
4158         r = MAX(rme.r, rother.r);
4159     } else {
4160         r = rme.r;
4161     }
4163     gdouble const weird_angle = atan2(norm_angle, r);
4164 /* Bulia says norm_angle is just the visible distance that the
4165  * object's end must travel on the screen.  Left as 'angle' for want of
4166  * a better name.*/
4168     rme.a += weird_angle;
4169     if ( both
4170          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4171          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4172     {
4173         rother.a += weird_angle;
4174     }
4177 /**
4178  * Rotate one node.
4179  */
4180 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4182     Inkscape::NodePath::NodeSide *me, *other;
4183     bool both = false;
4185     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4186     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4188     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4189         me = &(n->p);
4190         other = &(n->n);
4191     } else if (!n->p.other) {
4192         me = &(n->n);
4193         other = &(n->p);
4194     } else {
4195         if (which > 0) { // right handle
4196             if (xn > xp) {
4197                 me = &(n->n);
4198                 other = &(n->p);
4199             } else {
4200                 me = &(n->p);
4201                 other = &(n->n);
4202             }
4203         } else if (which < 0){ // left handle
4204             if (xn <= xp) {
4205                 me = &(n->n);
4206                 other = &(n->p);
4207             } else {
4208                 me = &(n->p);
4209                 other = &(n->n);
4210             }
4211         } else { // both handles
4212             me = &(n->n);
4213             other = &(n->p);
4214             both = true;
4215         }
4216     }
4218     Radial rme(me->pos - n->pos);
4219     Radial rother(other->pos - n->pos);
4221     if (screen) {
4222         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4223     } else {
4224         node_rotate_one_internal (*n, angle, rme, rother, both);
4225     }
4227     me->pos = n->pos + Geom::Point(rme);
4229     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4230         other->pos =  n->pos + Geom::Point(rother);
4231     }
4233     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4234     // so here we just move all the knots without emitting move signals, for speed
4235     sp_node_update_handles(n, false);
4238 /**
4239  * Rotate selected nodes.
4240  */
4241 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4243     if (!nodepath || !nodepath->selected) return;
4245     if (g_list_length(nodepath->selected) == 1) {
4246        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4247         node_rotate_one (n, angle, which, screen);
4248     } else {
4249        // rotate as an object:
4251         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4252         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4253         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4254             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4255             box.expandTo (n->pos); // contain all selected nodes
4256         }
4258         gdouble rot;
4259         if (screen) {
4260             gdouble const zoom = nodepath->desktop->current_zoom();
4261             gdouble const zmove = angle / zoom;
4262             gdouble const r = Geom::L2(box.max() - box.midpoint());
4263             rot = atan2(zmove, r);
4264         } else {
4265             rot = angle;
4266         }
4268         Geom::Point rot_center;
4269         if (Inkscape::NodePath::Path::active_node == NULL)
4270             rot_center = box.midpoint();
4271         else
4272             rot_center = Inkscape::NodePath::Path::active_node->pos;
4274         Geom::Matrix t =
4275             Geom::Matrix (Geom::Translate(-rot_center)) *
4276             Geom::Matrix (Geom::Rotate(rot)) *
4277             Geom::Matrix (Geom::Translate(rot_center));
4279         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4280             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4281             n->pos *= t;
4282             n->n.pos *= t;
4283             n->p.pos *= t;
4284             sp_node_update_handles(n, false);
4285         }
4286     }
4288     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4291 /**
4292  * Scale one node.
4293  */
4294 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4296     bool both = false;
4297     Inkscape::NodePath::NodeSide *me, *other;
4299     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4300     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4302     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4303         me = &(n->p);
4304         other = &(n->n);
4305         n->code = NR_CURVETO;
4306     } else if (!n->p.other) {
4307         me = &(n->n);
4308         other = &(n->p);
4309         if (n->n.other)
4310             n->n.other->code = NR_CURVETO;
4311     } else {
4312         if (which > 0) { // right handle
4313             if (xn > xp) {
4314                 me = &(n->n);
4315                 other = &(n->p);
4316                 if (n->n.other)
4317                     n->n.other->code = NR_CURVETO;
4318             } else {
4319                 me = &(n->p);
4320                 other = &(n->n);
4321                 n->code = NR_CURVETO;
4322             }
4323         } else if (which < 0){ // left handle
4324             if (xn <= xp) {
4325                 me = &(n->n);
4326                 other = &(n->p);
4327                 if (n->n.other)
4328                     n->n.other->code = NR_CURVETO;
4329             } else {
4330                 me = &(n->p);
4331                 other = &(n->n);
4332                 n->code = NR_CURVETO;
4333             }
4334         } else { // both handles
4335             me = &(n->n);
4336             other = &(n->p);
4337             both = true;
4338             n->code = NR_CURVETO;
4339             if (n->n.other)
4340                 n->n.other->code = NR_CURVETO;
4341         }
4342     }
4344     Radial rme(me->pos - n->pos);
4345     Radial rother(other->pos - n->pos);
4347     rme.r += grow;
4348     if (rme.r < 0) rme.r = 0;
4349     if (rme.a == HUGE_VAL) {
4350         if (me->other) { // if direction is unknown, initialize it towards the next node
4351             Radial rme_next(me->other->pos - n->pos);
4352             rme.a = rme_next.a;
4353         } else { // if there's no next, initialize to 0
4354             rme.a = 0;
4355         }
4356     }
4357     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4358         rother.r += grow;
4359         if (rother.r < 0) rother.r = 0;
4360         if (rother.a == HUGE_VAL) {
4361             rother.a = rme.a + M_PI;
4362         }
4363     }
4365     me->pos = n->pos + Geom::Point(rme);
4367     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4368         other->pos = n->pos + Geom::Point(rother);
4369     }
4371     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4372     // so here we just move all the knots without emitting move signals, for speed
4373     sp_node_update_handles(n, false);
4376 /**
4377  * Scale selected nodes.
4378  */
4379 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4381     if (!nodepath || !nodepath->selected) return;
4383     if (g_list_length(nodepath->selected) == 1) {
4384         // scale handles of the single selected node
4385         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4386         node_scale_one (n, grow, which);
4387     } else {
4388         // scale nodes as an "object":
4390         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4391         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4392         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4393             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4394             box.expandTo (n->pos); // contain all selected nodes
4395         }
4397         if ( Geom::are_near(box.maxExtent(), 0) ) {
4398             SPEventContext *ec = nodepath->desktop->event_context;
4399             if (!ec) return;
4400             Inkscape::MessageContext *mc = get_message_context(ec);
4401             if (!mc) return;
4402             mc->setF(Inkscape::WARNING_MESSAGE,
4403                              _("Cannot scale nodes when all are at the same location."));
4404             return;
4405         }
4406         double scale = (box.maxExtent() + grow)/box.maxExtent();
4409         Geom::Point scale_center;
4410         if (Inkscape::NodePath::Path::active_node == NULL)
4411             scale_center = box.midpoint();
4412         else
4413             scale_center = Inkscape::NodePath::Path::active_node->pos;
4415         Geom::Matrix t =
4416             Geom::Translate(-scale_center) *
4417             Geom::Scale(scale, scale) *
4418             Geom::Translate(scale_center);
4420         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4421             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4422             n->pos *= t;
4423             n->n.pos *= t;
4424             n->p.pos *= t;
4425             sp_node_update_handles(n, false);
4426         }
4427     }
4429     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4432 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4434     if (!nodepath) return;
4435     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4438 /**
4439  * Flip selected nodes horizontally/vertically.
4440  */
4441 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis, boost::optional<Geom::Point> center)
4443     if (!nodepath || !nodepath->selected) return;
4445     if (g_list_length(nodepath->selected) == 1 && !center) {
4446         // flip handles of the single selected node
4447         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4448         double temp = n->p.pos[axis];
4449         n->p.pos[axis] = n->n.pos[axis];
4450         n->n.pos[axis] = temp;
4451         sp_node_update_handles(n, false);
4452     } else {
4453         // scale nodes as an "object":
4455         Geom::Rect box = sp_node_selected_bbox (nodepath);
4456         if (!center) {
4457             center = box.midpoint();
4458         }
4459         Geom::Matrix t =
4460             Geom::Matrix (Geom::Translate(- *center)) *
4461             Geom::Matrix ((axis == Geom::X)? Geom::Scale(-1, 1) : Geom::Scale(1, -1)) *
4462             Geom::Matrix (Geom::Translate(*center));
4464         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4465             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4466             n->pos *= t;
4467             n->n.pos *= t;
4468             n->p.pos *= t;
4469             sp_node_update_handles(n, false);
4470         }
4471     }
4473     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4476 Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4478     g_assert (nodepath->selected);
4480     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4481     Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4482     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4483         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4484         box.expandTo (n->pos); // contain all selected nodes
4485     }
4486     return box;
4489 //-----------------------------------------------
4490 /**
4491  * Return new subpath under given nodepath.
4492  */
4493 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4495     g_assert(nodepath);
4496     g_assert(nodepath->desktop);
4498    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4500     s->nodepath = nodepath;
4501     s->closed = FALSE;
4502     s->nodes = NULL;
4503     s->first = NULL;
4504     s->last = NULL;
4506     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4507     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4508     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4510     return s;
4513 /**
4514  * Destroy nodes in subpath, then subpath itself.
4515  */
4516 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4518     g_assert(subpath);
4519     g_assert(subpath->nodepath);
4520     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4522     while (subpath->nodes) {
4523         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4524     }
4526     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4528     g_free(subpath);
4531 /**
4532  * Link head to tail in subpath.
4533  */
4534 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4536     g_assert(!sp->closed);
4537     g_assert(sp->last != sp->first);
4538     g_assert(sp->first->code == NR_MOVETO);
4540     sp->closed = TRUE;
4542     //Link the head to the tail
4543     sp->first->p.other = sp->last;
4544     sp->last->n.other  = sp->first;
4545     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4546     sp->first          = sp->last;
4548     //Remove the extra end node
4549     sp_nodepath_node_destroy(sp->last->n.other);
4552 /**
4553  * Open closed (loopy) subpath at node.
4554  */
4555 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4557     g_assert(sp->closed);
4558     g_assert(n->subpath == sp);
4559     g_assert(sp->first == sp->last);
4561     /* We create new startpoint, current node will become last one */
4563    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4564                                                 &n->pos, &n->pos, &n->n.pos);
4567     sp->closed        = FALSE;
4569     //Unlink to make a head and tail
4570     sp->first         = new_path;
4571     sp->last          = n;
4572     n->n.other        = NULL;
4573     new_path->p.other = NULL;
4576 /**
4577  * Return new node in subpath with given properties.
4578  * \param pos Position of node.
4579  * \param ppos Handle position in previous direction
4580  * \param npos Handle position in previous direction
4581  */
4582 Inkscape::NodePath::Node *
4583 sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp, Inkscape::NodePath::Node *next, Inkscape::NodePath::NodeType type, NRPathcode code, Geom::Point *ppos, Geom::Point *pos, Geom::Point *npos)
4585     g_assert(sp);
4586     g_assert(sp->nodepath);
4587     g_assert(sp->nodepath->desktop);
4589     if (nodechunk == NULL)
4590         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4592     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4594     n->subpath  = sp;
4596     if (type != Inkscape::NodePath::NODE_NONE) {
4597         // use the type from sodipodi:nodetypes
4598         n->type = type;
4599     } else {
4600         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4601             // points are (almost) collinear
4602             if (Geom::L2(*pos - *ppos) < 1e-6 || Geom::L2(*pos - *npos) < 1e-6) {
4603                 // endnode, or a node with a retracted handle
4604                 n->type = Inkscape::NodePath::NODE_CUSP;
4605             } else {
4606                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4607             }
4608         } else {
4609             n->type = Inkscape::NodePath::NODE_CUSP;
4610         }
4611     }
4613     n->code     = code;
4614     n->selected = FALSE;
4615     n->pos      = *pos;
4616     n->p.pos    = *ppos;
4617     n->n.pos    = *npos;
4619     n->dragging_out = NULL;
4621     Inkscape::NodePath::Node *prev;
4622     if (next) {
4623         //g_assert(g_list_find(sp->nodes, next));
4624         prev = next->p.other;
4625     } else {
4626         prev = sp->last;
4627     }
4629     if (prev)
4630         prev->n.other = n;
4631     else
4632         sp->first = n;
4634     if (next)
4635         next->p.other = n;
4636     else
4637         sp->last = n;
4639     n->p.other = prev;
4640     n->n.other = next;
4642     n->knot = sp_knot_new(sp->nodepath->desktop, _("<b>Node</b>: drag to edit the path; with <b>Ctrl</b> to snap to horizontal/vertical; with <b>Ctrl+Alt</b> to snap to handles' directions"));
4643     sp_knot_set_position(n->knot, *pos, 0);
4645     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4646     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4647     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4649     sp_nodepath_update_node_knot(n);
4651     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4652     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4653     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4654     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4655     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4656     sp_knot_show(n->knot);
4658     // We only create handle knots and lines on demand
4659     n->p.knot = NULL;
4660     n->p.line = NULL;
4661     n->n.knot = NULL;
4662     n->n.line = NULL;
4664     sp->nodes = g_list_prepend(sp->nodes, n);
4666     return n;
4669 /**
4670  * Destroy node and its knots, link neighbors in subpath.
4671  */
4672 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4674     g_assert(node);
4675     g_assert(node->subpath);
4676     g_assert(SP_IS_KNOT(node->knot));
4678    Inkscape::NodePath::SubPath *sp = node->subpath;
4680     if (node->selected) { // first, deselect
4681         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4682         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4683     }
4685     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4687     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4688     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4689     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4690     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4691     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4692     g_object_unref(G_OBJECT(node->knot));
4694     if (node->p.knot) {
4695         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4696         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4697         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4698         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4699         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4700         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4701         g_object_unref(G_OBJECT(node->p.knot));
4702         node->p.knot = NULL;
4703     }
4705     if (node->n.knot) {
4706         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4707         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4708         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4709         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4710         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4711         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4712         g_object_unref(G_OBJECT(node->n.knot));
4713         node->n.knot = NULL;
4714     }
4716     if (node->p.line)
4717         gtk_object_destroy(GTK_OBJECT(node->p.line));
4718     if (node->n.line)
4719         gtk_object_destroy(GTK_OBJECT(node->n.line));
4721     if (sp->nodes) { // there are others nodes on the subpath
4722         if (sp->closed) {
4723             if (sp->first == node) {
4724                 g_assert(sp->last == node);
4725                 sp->first = node->n.other;
4726                 sp->last = sp->first;
4727             }
4728             node->p.other->n.other = node->n.other;
4729             node->n.other->p.other = node->p.other;
4730         } else {
4731             if (sp->first == node) {
4732                 sp->first = node->n.other;
4733                 sp->first->code = NR_MOVETO;
4734             }
4735             if (sp->last == node) sp->last = node->p.other;
4736             if (node->p.other) node->p.other->n.other = node->n.other;
4737             if (node->n.other) node->n.other->p.other = node->p.other;
4738         }
4739     } else { // this was the last node on subpath
4740         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4741     }
4743     g_mem_chunk_free(nodechunk, node);
4746 /**
4747  * Returns one of the node's two sides.
4748  * \param which Indicates which side.
4749  * \return Pointer to previous node side if which==-1, next if which==1.
4750  */
4751 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4753     g_assert(node);
4754     Inkscape::NodePath::NodeSide * result = 0;
4755     switch (which) {
4756         case -1:
4757             result = &node->p;
4758             break;
4759         case 1:
4760             result = &node->n;
4761             break;
4762         default:
4763             g_assert_not_reached();
4764     }
4766     return result;
4769 /**
4770  * Return the other side of the node, given one of its sides.
4771  */
4772 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4774     g_assert(node);
4775     Inkscape::NodePath::NodeSide *result = 0;
4777     if (me == &node->p) {
4778         result = &node->n;
4779     } else if (me == &node->n) {
4780         result = &node->p;
4781     } else {
4782         g_assert_not_reached();
4783     }
4785     return result;
4788 /**
4789  * Return NRPathcode on the given side of the node.
4790  */
4791 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4793     g_assert(node);
4795     NRPathcode result = NR_END;
4796     if (me == &node->p) {
4797         if (node->p.other) {
4798             result = (NRPathcode)node->code;
4799         } else {
4800             result = NR_MOVETO;
4801         }
4802     } else if (me == &node->n) {
4803         if (node->n.other) {
4804             result = (NRPathcode)node->n.other->code;
4805         } else {
4806             result = NR_MOVETO;
4807         }
4808     } else {
4809         g_assert_not_reached();
4810     }
4812     return result;
4815 /**
4816  * Return node with the given index
4817  */
4818 Inkscape::NodePath::Node *
4819 sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index)
4821     Inkscape::NodePath::Node *e = NULL;
4823     if (!nodepath) {
4824         return e;
4825     }
4827     //find segment
4828     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4830         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4831         int n = g_list_length(sp->nodes);
4832         if (sp->closed) {
4833             n++;
4834         }
4836         //if the piece belongs to this subpath grab it
4837         //otherwise move onto the next subpath
4838         if (index < n) {
4839             e = sp->first;
4840             for (int i = 0; i < index; ++i) {
4841                 e = e->n.other;
4842             }
4843             break;
4844         } else {
4845             if (sp->closed) {
4846                 index -= (n+1);
4847             } else {
4848                 index -= n;
4849             }
4850         }
4851     }
4853     return e;
4856 /**
4857  * Returns plain text meaning of node type.
4858  */
4859 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4861     unsigned retracted = 0;
4862     bool endnode = false;
4864     for (int which = -1; which <= 1; which += 2) {
4865         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4866         if (side->other && Geom::L2(side->pos - node->pos) < 1e-6)
4867             retracted ++;
4868         if (!side->other)
4869             endnode = true;
4870     }
4872     if (retracted == 0) {
4873         if (endnode) {
4874                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4875                 return _("end node");
4876         } else {
4877             switch (node->type) {
4878                 case Inkscape::NodePath::NODE_CUSP:
4879                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4880                     return _("cusp");
4881                 case Inkscape::NodePath::NODE_SMOOTH:
4882                     // TRANSLATORS: "smooth" is an adjective here
4883                     return _("smooth");
4884                 case Inkscape::NodePath::NODE_AUTO:
4885                     return _("auto");
4886                 case Inkscape::NodePath::NODE_SYMM:
4887                     return _("symmetric");
4888             }
4889         }
4890     } else if (retracted == 1) {
4891         if (endnode) {
4892             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4893             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4894         } else {
4895             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4896         }
4897     } else {
4898         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4899     }
4901     return NULL;
4904 /**
4905  * Handles content of statusbar as long as node tool is active.
4906  */
4907 void
4908 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4910     gchar const *when_selected = _("<b>Drag</b> nodes or node handles; <b>Alt+drag</b> nodes to sculpt; <b>arrow</b> keys to move nodes, <b>&lt; &gt;</b> to scale, <b>[ ]</b> to rotate");
4911     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4913     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4914     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4915     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4916     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4918     SPDesktop *desktop = NULL;
4919     if (nodepath) {
4920         desktop = nodepath->desktop;
4921     } else {
4922         desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above
4923     }
4925     SPEventContext *ec = desktop->event_context;
4926     if (!ec) return;
4928     Inkscape::MessageContext *mc = get_message_context(ec);
4929     if (!mc) return;
4931     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4933     if (selected_nodes == 0) {
4934         Inkscape::Selection *sel = desktop->selection;
4935         if (!sel || sel->isEmpty()) {
4936             mc->setF(Inkscape::NORMAL_MESSAGE,
4937                      _("Select a single object to edit its nodes or handles."));
4938         } else {
4939             if (nodepath) {
4940             mc->setF(Inkscape::NORMAL_MESSAGE,
4941                      ngettext("<b>0</b> out of <b>%i</b> node selected. <b>Click</b>, <b>Shift+click</b>, or <b>drag around</b> nodes to select.",
4942                               "<b>0</b> out of <b>%i</b> nodes selected. <b>Click</b>, <b>Shift+click</b>, or <b>drag around</b> nodes to select.",
4943                               total_nodes),
4944                      total_nodes);
4945             } else {
4946                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4947                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4948                 } else {
4949                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4950                 }
4951             }
4952         }
4953     } else if (nodepath && selected_nodes == 1) {
4954         mc->setF(Inkscape::NORMAL_MESSAGE,
4955                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4956                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4957                           total_nodes),
4958                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4959     } else {
4960         if (selected_subpaths > 1) {
4961             mc->setF(Inkscape::NORMAL_MESSAGE,
4962                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4963                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4964                               total_nodes),
4965                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4966         } else {
4967             mc->setF(Inkscape::NORMAL_MESSAGE,
4968                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4969                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4970                               total_nodes),
4971                      selected_nodes, total_nodes, when_selected);
4972         }
4973     }
4976 /*
4977  * returns a *copy* of the curve of that object.
4978  */
4979 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4980     if (!object)
4981         return NULL;
4983     SPCurve *curve = NULL;
4984     if (SP_IS_PATH(object)) {
4985         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4986         curve = curve_new->copy();
4987     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4988         const gchar *svgd = object->repr->attribute(key);
4989         if (svgd) {
4990             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4991             SPCurve *curve_new = new SPCurve(pv);
4992             if (curve_new) {
4993                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4994             }
4995         }
4996     }
4998     return curve;
5001 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
5002     if (!np || !np->object || !curve)
5003         return;
5005     if (SP_IS_PATH(np->object)) {
5006         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
5007             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
5008         } else {
5009             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
5010         }
5011     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
5012         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(np->object)->get_lpe();
5013         if (lpe) {
5014             Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( lpe->getParameter(np->repr_key) );
5015             if (pathparam) {
5016                 pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
5017                 np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
5018             }
5019         }
5020     }
5023 /*
5024 SPCanvasItem *
5025 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
5026     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
5028 */
5030 /*
5031 SPCanvasItem *
5032 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) {
5033     SPCurve *flash_curve = curve->copy();
5034     Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity();
5035     flash_curve->transform(i2d);
5036     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
5037     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
5038     // unless we also flash the nodes...
5039     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
5040     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
5041     sp_canvas_item_show(canvasitem);
5042     flash_curve->unref();
5043     return canvasitem;
5046 SPCanvasItem *
5047 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) {
5048     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5049     return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path),
5050                                            prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff));
5052 */
5054 SPCanvasItem *
5055 sp_nodepath_helperpath_from_path(SPDesktop *desktop, SPPath *path) {
5056     SPCurve *flash_curve = sp_path_get_curve_for_edit(path)->copy();
5057     Geom::Matrix i2d = sp_item_i2d_affine(SP_ITEM(path));
5058     flash_curve->transform(i2d);
5059     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
5060     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
5061     // unless we also flash the nodes...
5062     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5063     guint32 color = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
5064     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
5065     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
5066     sp_canvas_item_show(canvasitem);
5067     flash_curve->unref();
5068     return canvasitem;
5071 // TODO: Merge this with sp_nodepath_make_helper_item()!
5072 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
5073     np->show_helperpath = show;
5075     if (show) {
5076         SPCurve *helper_curve = np->curve->copy();
5077         helper_curve->transform(np->i2d);
5078         if (!np->helper_path) {
5079             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
5081             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
5082             sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(np->helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
5083             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
5084             sp_canvas_item_move_to_z(np->helper_path, 0);
5085             sp_canvas_item_show(np->helper_path);
5086         } else {
5087             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
5088         }
5089         helper_curve->unref();
5090     } else {
5091         if (np->helper_path) {
5092             GtkObject *temp = np->helper_path;
5093             np->helper_path = NULL;
5094             gtk_object_destroy(temp);
5095         }
5096     }
5099 /* sp_nodepath_make_straight_path:
5100  *   Prevents user from curving the path by dragging a segment or activating handles etc.
5101  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
5102  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
5103  */
5104 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
5105     np->straight_path = true;
5106     np->show_handles = false;
5107     g_message("add code to make the path straight.");
5108     // do sp_nodepath_convert_node_type on all nodes?
5109     // coding tip: search for this text : "Make selected segments lines"
5112 /*
5113   Local Variables:
5114   mode:c++
5115   c-file-style:"stroustrup"
5116   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5117   indent-tabs-mode:nil
5118   fill-column:99
5119   End:
5120 */
5121 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :