Code

Move all of the snapper code to 2geom
[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 "shape-editor.h"
41 #include "selection-chemistry.h"
42 #include "selection.h"
43 #include "xml/repr.h"
44 #include "prefs-utils.h"
45 #include "sp-metrics.h"
46 #include "sp-path.h"
47 #include "libnr/nr-matrix-ops.h"
48 #include "svg/svg.h"
49 #include "verbs.h"
50 #include "display/bezier-utils.h"
51 #include <vector>
52 #include <algorithm>
53 #include <cstring>
54 #include <cmath>
55 #include <string>
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 class NR::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);
124 /* Node event callbacks */
125 static void node_clicked(SPKnot *knot, guint state, gpointer data);
126 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
127 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
128 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
130 /* Handle event callbacks */
131 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
132 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
133 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
134 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
135 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
136 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
138 /* Constructors and destructors */
140 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
141 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
142 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
143 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
144 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
145                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
146 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
148 /* Helpers */
150 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
151 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
152 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
154 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
155 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
157 // active_node indicates mouseover node
158 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
160 static SPCanvasItem *
161 sp_nodepath_make_helper_item(Inkscape::NodePath::Path *np, /*SPDesktop *desktop, */const SPCurve *curve, bool show = false) {
162     SPCurve *helper_curve = curve->copy();
163     helper_curve->transform(np->i2d);
164     SPCanvasItem *helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
165     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
166     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helper_path), 0, SP_WIND_RULE_NONZERO);
167     sp_canvas_item_move_to_z(helper_path, 0);
168     if (show) {
169         sp_canvas_item_show(helper_path);
170     }
171     helper_curve->unref();
172     return helper_path;
175 static SPCanvasItem *
176 canvasitem_from_pathvec(Inkscape::NodePath::Path *np, Geom::PathVector const &pathv, bool show) {
177     SPCurve *helper_curve = new SPCurve(pathv);
178     return sp_nodepath_make_helper_item(np, helper_curve, show);
181 static void
182 sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) {
183     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> >* helper_path_vec;
184     if (!SP_IS_LPE_ITEM(np->item)) {
185         g_print ("Only LPEItems can have helperpaths!\n");
186         return;
187     }
189     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
190     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
191     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
192         Inkscape::LivePathEffect::LPEObjectReference *lperef = (*i);
193         Inkscape::LivePathEffect::Effect *lpe = lperef->lpeobject->lpe;
194         // create new canvas items from the effect's helper paths
195         std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
196         for (std::vector<Geom::PathVector>::iterator j = hpaths.begin(); j != hpaths.end(); ++j) {
197             (*np->helper_path_vec)[lpe].push_back(canvasitem_from_pathvec(np, *j, true));
198         }
199     }
202 void
203 sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np) {
204     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> >* helper_path_vec;
205     if (!SP_IS_LPE_ITEM(np->item)) {
206         g_print ("Only LPEItems can have helperpaths!\n");
207         return;
208     }
210     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
211     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
212     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
213         Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->lpe;
214         /* update canvas items from the effect's helper paths; note that this code relies on the
215          * fact that getHelperPaths() will always return the same number of helperpaths in the same
216          * order as during their creation in sp_nodepath_create_helperpaths
217          */
218         std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
219         for (unsigned int j = 0; j < hpaths.size(); ++j) {
220             SPCurve *curve = new SPCurve(hpaths[j]);
221             curve->transform(np->i2d);
222             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(((*np->helper_path_vec)[lpe])[j]), curve);
223             curve = curve->unref();
224         }
225     }
228 static void
229 sp_nodepath_destroy_helperpaths(Inkscape::NodePath::Path *np) {
230     for (HelperPathList::iterator i = np->helper_path_vec->begin(); i != np->helper_path_vec->end(); ++i) {
231         for (std::vector<SPCanvasItem *>::iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) {
232             GtkObject *temp = *j;
233             *j = NULL;
234             gtk_object_destroy(temp);
235         }
236     }
240 /**
241  * \brief Creates new nodepath from item
242  */
243 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
245     Inkscape::XML::Node *repr = object->repr;
247     /** \todo
248      * FIXME: remove this. We don't want to edit paths inside flowtext.
249      * Instead we will build our flowtext with cloned paths, so that the
250      * real paths are outside the flowtext and thus editable as usual.
251      */
252     if (SP_IS_FLOWTEXT(object)) {
253         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
254             if SP_IS_FLOWREGION(child) {
255                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
256                 if (grandchild && SP_IS_PATH(grandchild)) {
257                     object = SP_ITEM(grandchild);
258                     break;
259                 }
260             }
261         }
262     }
264     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
266     if (curve == NULL)
267         return NULL;
269     if (curve->get_segment_count() < 1) {
270         curve->unref();
271         return NULL; // prevent crash for one-node paths
272     }
274     //Create new nodepath
275     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
276     if (!np) {
277         curve->unref();
278         return NULL;
279     }
281     // Set defaults
282     np->desktop     = desktop;
283     np->object      = object;
284     np->subpaths    = NULL;
285     np->selected    = NULL;
286     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
287     np->local_change = 0;
288     np->show_handles = show_handles;
289     np->helper_path = NULL;
290     np->helper_path_vec = new HelperPathList;
291     np->helperpath_rgba = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
292     np->helperpath_width = 1.0;
293     np->curve = curve->copy();
294     np->show_helperpath = (prefs_get_int_attribute ("tools.nodes", "show_helperpath",  0) == 1);
295     if (SP_IS_LPE_ITEM(object)) {
296         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
297         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
298             np->show_helperpath = true;
299         }            
300     }
301     np->straight_path = false;
302     if (IS_LIVEPATHEFFECT(object) && item) {
303         np->item = item;
304     } else {
305         np->item = SP_ITEM(object);
306     }
308     // we need to update item's transform from the repr here,
309     // because they may be out of sync when we respond
310     // to a change in repr by regenerating nodepath     --bb
311     sp_object_read_attr(SP_OBJECT(np->item), "transform");
313     np->i2d  = sp_item_i2d_affine(np->item);
314     np->d2i  = np->i2d.inverse();
316     np->repr = repr;
317     if (repr_key_in) { // apparantly the object is an LPEObject
318         np->repr_key = g_strdup(repr_key_in);
319         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
320         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
321         if (lpeparam) {
322             lpeparam->param_setup_nodepath(np);
323         }
324     } else {
325         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
326         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
327             np->repr_key = g_strdup("inkscape:original-d");
329             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
330             if (lpe) {
331                 lpe->setup_nodepath(np);
332             }
333         } else {
334             np->repr_key = g_strdup("d");
335         }
336     }
338     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
339      * So for example a closed rectangle has a nodetypestring of length 5.
340      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
341     Geom::PathVector pathv_sanitized = pathv_to_linear_and_cubic_beziers(np->curve->get_pathvector());
342     np->curve->set_pathvector(pathv_sanitized);
343     guint length = np->curve->get_segment_count();
344     for (Geom::PathVector::const_iterator pit = pathv_sanitized.begin(); pit != pathv_sanitized.end(); ++pit) {
345         length += pit->empty() ? 0 : 1;
346     }
348     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
349     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
351     // create the subpath(s) from the bpath
352     subpaths_from_pathvector(np, pathv_sanitized, typestr);
354     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
355     np->subpaths = g_list_reverse(np->subpaths);
357     delete[] typestr;
358     curve->unref();
360     // Draw helper curve
361     if (np->show_helperpath) {
362         np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true);
363     }
365     sp_nodepath_create_helperpaths(np);
367     return np;
370 /**
371  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
372  */
373 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
375     if (!np)  //soft fail, like delete
376         return;
378     while (np->subpaths) {
379         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
380     }
382     //Inform the ShapeEditor that made me, if any, that I am gone.
383     if (np->shape_editor)
384         np->shape_editor->nodepath_destroyed();
386     g_assert(!np->selected);
388     if (np->helper_path) {
389         GtkObject *temp = np->helper_path;
390         np->helper_path = NULL;
391         gtk_object_destroy(temp);
392     }
393     if (np->curve) {
394         np->curve->unref();
395         np->curve = NULL;
396     }
398     if (np->repr_key) {
399         g_free(np->repr_key);
400         np->repr_key = NULL;
401     }
402     if (np->repr_nodetypes_key) {
403         g_free(np->repr_nodetypes_key);
404         np->repr_nodetypes_key = NULL;
405     }
407     sp_nodepath_destroy_helperpaths(np);
408     delete np->helper_path_vec;
409     np->helper_path_vec = NULL;
411     np->desktop = NULL;
413     g_free(np);
416 /**
417  *  Return the node count of a given NodeSubPath.
418  */
419 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
421     if (!subpath)
422         return 0;
423     gint nodeCount = g_list_length(subpath->nodes);
424     return nodeCount;
427 /**
428  *  Return the node count of a given NodePath.
429  */
430 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
432     if (!np)
433         return 0;
434     gint nodeCount = 0;
435     for (GList *item = np->subpaths ; item ; item=item->next) {
436        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
437         nodeCount += g_list_length(subpath->nodes);
438     }
439     return nodeCount;
442 /**
443  *  Return the subpath count of a given NodePath.
444  */
445 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
447     if (!np)
448         return 0;
449     return g_list_length (np->subpaths);
452 /**
453  *  Return the selected node count of a given NodePath.
454  */
455 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
457     if (!np)
458         return 0;
459     return g_list_length (np->selected);
462 /**
463  *  Return the number of subpaths where nodes are selected in a given NodePath.
464  */
465 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
467     if (!np)
468         return 0;
469     if (!np->selected)
470         return 0;
471     if (!np->selected->next)
472         return 1;
473     gint count = 0;
474     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
475         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
476         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
477             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
478             if (node->selected) {
479                 count ++;
480                 break;
481             }
482         }
483     }
484     return count;
487 /**
488  * Clean up a nodepath after editing.
489  *
490  * Currently we are deleting trivial subpaths.
491  */
492 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
494     GList *badSubPaths = NULL;
496     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
497     for (GList *l = nodepath->subpaths; l ; l=l->next) {
498        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
499        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
500             badSubPaths = g_list_append(badSubPaths, sp);
501     }
503     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
504     //also removes the subpath from nodepath->subpaths
505     for (GList *l = badSubPaths; l ; l=l->next) {
506        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
507         sp_nodepath_subpath_destroy(sp);
508     }
510     g_list_free(badSubPaths);
513 /**
514  * Create new nodepaths from pathvector, make it subpaths of np.
515  * \param t The node type array.
516  */
517 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
519     guint i = 0;  // index into node type array
520     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
521         if (pit->empty())
522             continue;  // don't add single knot paths
524         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
526         NR::Point ppos = pit->initialPoint() * (Geom::Matrix)np->i2d;
527         NRPathcode pcode = NR_MOVETO;
529         /* 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)*/
530         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
531             if( dynamic_cast<Geom::LineSegment const*>(&*cit) ||
532                 dynamic_cast<Geom::HLineSegment const*>(&*cit) ||
533                 dynamic_cast<Geom::VLineSegment const*>(&*cit) )
534             {
535                 NR::Point pos = cit->initialPoint() * (Geom::Matrix)np->i2d;
536                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
538                 ppos = cit->finalPoint() * (Geom::Matrix)np->i2d;
539                 pcode = NR_LINETO;
540             }
541             else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
542                 std::vector<Geom::Point> points = cubic_bezier->points();
543                 NR::Point pos = points[0] * (Geom::Matrix)np->i2d;
544                 NR::Point npos = points[1] * (Geom::Matrix)np->i2d;
545                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
547                 ppos = points[2] * (Geom::Matrix)np->i2d;
548                 pcode = NR_CURVETO;
549             }
550         }
552         if (pit->closed()) {
553             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
554             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
555              * If the length is zero, don't add it to the nodepath. */
556             Geom::Curve const &closing_seg = pit->back_closed();
557             if ( ! closing_seg.isDegenerate() ) {
558                 NR::Point pos = closing_seg.finalPoint() * (Geom::Matrix)np->i2d;
559                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
560             }
562             sp_nodepath_subpath_close(sp);
563         }
564     }
567 /**
568  * Convert from sodipodi:nodetypes to new style type array.
569  */
570 static
571 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
573     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
575     guint pos = 0;
577     if (types) {
578         for (guint i = 0; types[i] && ( i < length ); i++) {
579             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
580             if (types[i] != '\0') {
581                 switch (types[i]) {
582                     case 's':
583                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
584                         break;
585                     case 'z':
586                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
587                         break;
588                     case 'c':
589                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
590                         break;
591                     default:
592                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
593                         break;
594                 }
595             }
596         }
597     }
599     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
601     return typestr;
604 /**
605  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
606  * updated but repr is not (for speed). Used during curve and node drag.
607  */
608 static void update_object(Inkscape::NodePath::Path *np)
610     g_assert(np);
612     np->curve->unref();
613     np->curve = create_curve(np);
615     sp_nodepath_set_curve(np, np->curve);
617     if (np->show_helperpath) {
618         SPCurve * helper_curve = np->curve->copy();
619         helper_curve->transform(np->i2d);
620         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
621         helper_curve->unref();
622     }
624     // updating helperpaths of LPEItems is now done in sp_lpe_item_update();
625     //sp_nodepath_update_helperpaths(np);
627     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
628     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
629     np->shape_editor->update_knotholder();
632 /**
633  * Update XML path node with data from path object.
634  */
635 static void update_repr_internal(Inkscape::NodePath::Path *np)
637     g_assert(np);
639     Inkscape::XML::Node *repr = np->object->repr;
641     np->curve->unref();
642     np->curve = create_curve(np);
644     gchar *typestr = create_typestr(np);
645     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
647     // determine if path has an effect applied and write to correct "d" attribute.
648     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
649         np->local_change++;
650         repr->setAttribute(np->repr_key, svgpath);
651     }
653     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
654         np->local_change++;
655         repr->setAttribute(np->repr_nodetypes_key, typestr);
656     }
658     g_free(svgpath);
659     g_free(typestr);
661     if (np->show_helperpath) {
662         SPCurve * helper_curve = np->curve->copy();
663         helper_curve->transform(np->i2d);
664         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
665         helper_curve->unref();
666     }
668     // TODO: do we need this call here? after all, update_object() should have been called just before
669     //sp_nodepath_update_helperpaths(np);
672 /**
673  * Update XML path node with data from path object, commit changes forever.
674  */
675 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
677     //fixme: np can be NULL, so check before proceeding
678     g_return_if_fail(np != NULL);
680     update_repr_internal(np);
681     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
683     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
684                      annotation);
687 /**
688  * Update XML path node with data from path object, commit changes with undo.
689  */
690 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
692     update_repr_internal(np);
693     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
694                            annotation);
697 /**
698  * Make duplicate of path, replace corresponding XML node in tree, commit.
699  */
700 static void stamp_repr(Inkscape::NodePath::Path *np)
702     g_assert(np);
704     Inkscape::XML::Node *old_repr = np->object->repr;
705     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
707     // remember the position of the item
708     gint pos = old_repr->position();
709     // remember parent
710     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
712     SPCurve *curve = create_curve(np);
713     gchar *typestr = create_typestr(np);
715     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
717     new_repr->setAttribute(np->repr_key, svgpath);
718     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
720     // add the new repr to the parent
721     parent->appendChild(new_repr);
722     // move to the saved position
723     new_repr->setPosition(pos > 0 ? pos : 0);
725     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
726                      _("Stamp"));
728     Inkscape::GC::release(new_repr);
729     g_free(svgpath);
730     g_free(typestr);
731     curve->unref();
734 /**
735  * Create curve from path.
736  */
737 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
739     SPCurve *curve = new SPCurve();
741     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
742        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
743         curve->moveto(sp->first->pos * np->d2i);
744        Inkscape::NodePath::Node *n = sp->first->n.other;
745         while (n) {
746             NR::Point const end_pt = n->pos * np->d2i;
747             switch (n->code) {
748                 case NR_LINETO:
749                     curve->lineto(end_pt);
750                     break;
751                 case NR_CURVETO:
752                     curve->curveto(n->p.other->n.pos * np->d2i,
753                                      n->p.pos * np->d2i,
754                                      end_pt);
755                     break;
756                 default:
757                     g_assert_not_reached();
758                     break;
759             }
760             if (n != sp->last) {
761                 n = n->n.other;
762             } else {
763                 n = NULL;
764             }
765         }
766         if (sp->closed) {
767             curve->closepath();
768         }
769     }
771     return curve;
774 /**
775  * Convert path type string to sodipodi:nodetypes style.
776  */
777 static gchar *create_typestr(Inkscape::NodePath::Path *np)
779     gchar *typestr = g_new(gchar, 32);
780     gint len = 32;
781     gint pos = 0;
783     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
784        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
786         if (pos >= len) {
787             typestr = g_renew(gchar, typestr, len + 32);
788             len += 32;
789         }
791         typestr[pos++] = 'c';
793        Inkscape::NodePath::Node *n;
794         n = sp->first->n.other;
795         while (n) {
796             gchar code;
798             switch (n->type) {
799                 case Inkscape::NodePath::NODE_CUSP:
800                     code = 'c';
801                     break;
802                 case Inkscape::NodePath::NODE_SMOOTH:
803                     code = 's';
804                     break;
805                 case Inkscape::NodePath::NODE_SYMM:
806                     code = 'z';
807                     break;
808                 default:
809                     g_assert_not_reached();
810                     code = '\0';
811                     break;
812             }
814             if (pos >= len) {
815                 typestr = g_renew(gchar, typestr, len + 32);
816                 len += 32;
817             }
819             typestr[pos++] = code;
821             if (n != sp->last) {
822                 n = n->n.other;
823             } else {
824                 n = NULL;
825             }
826         }
827     }
829     if (pos >= len) {
830         typestr = g_renew(gchar, typestr, len + 1);
831         len += 1;
832     }
834     typestr[pos++] = '\0';
836     return typestr;
839 /**
840  * Returns current path in context. // later eliminate this function at all!
841  */
842 static Inkscape::NodePath::Path *sp_nodepath_current()
844     if (!SP_ACTIVE_DESKTOP) {
845         return NULL;
846     }
848     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
850     if (!SP_IS_NODE_CONTEXT(event_context)) {
851         return NULL;
852     }
854     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
859 /**
860  \brief Fills node and handle positions for three nodes, splitting line
861   marked by end at distance t.
862  */
863 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
865     g_assert(new_path != NULL);
866     g_assert(end      != NULL);
868     g_assert(end->p.other == new_path);
869    Inkscape::NodePath::Node *start = new_path->p.other;
870     g_assert(start);
872     if (end->code == NR_LINETO) {
873         new_path->type =Inkscape::NodePath::NODE_CUSP;
874         new_path->code = NR_LINETO;
875         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
876     } else {
877         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
878         new_path->code = NR_CURVETO;
879         gdouble s      = 1 - t;
880         for (int dim = 0; dim < 2; dim++) {
881             NR::Coord const f000 = start->pos[dim];
882             NR::Coord const f001 = start->n.pos[dim];
883             NR::Coord const f011 = end->p.pos[dim];
884             NR::Coord const f111 = end->pos[dim];
885             NR::Coord const f00t = s * f000 + t * f001;
886             NR::Coord const f01t = s * f001 + t * f011;
887             NR::Coord const f11t = s * f011 + t * f111;
888             NR::Coord const f0tt = s * f00t + t * f01t;
889             NR::Coord const f1tt = s * f01t + t * f11t;
890             NR::Coord const fttt = s * f0tt + t * f1tt;
891             start->n.pos[dim]    = f00t;
892             new_path->p.pos[dim] = f0tt;
893             new_path->pos[dim]   = fttt;
894             new_path->n.pos[dim] = f1tt;
895             end->p.pos[dim]      = f11t;
896         }
897     }
900 /**
901  * Adds new node on direct line between two nodes, activates handles of all
902  * three nodes.
903  */
904 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
906     g_assert(end);
907     g_assert(end->subpath);
908     g_assert(g_list_find(end->subpath->nodes, end));
910    Inkscape::NodePath::Node *start = end->p.other;
911     g_assert( start->n.other == end );
912    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
913                                                end,
914                                                (NRPathcode)end->code == NR_LINETO?
915                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
916                                                (NRPathcode)end->code,
917                                                &start->pos, &start->pos, &start->n.pos);
918     sp_nodepath_line_midpoint(newnode, end, t);
920     sp_node_adjust_handles(start);
921     sp_node_update_handles(start);
922     sp_node_update_handles(newnode);
923     sp_node_adjust_handles(end);
924     sp_node_update_handles(end);
926     return newnode;
929 /**
930 \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
931 */
932 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
934     g_assert(node);
935     g_assert(node->subpath);
936     g_assert(g_list_find(node->subpath->nodes, node));
938    Inkscape::NodePath::SubPath *sp = node->subpath;
939     Inkscape::NodePath::Path *np    = sp->nodepath;
941     if (sp->closed) {
942         sp_nodepath_subpath_open(sp, node);
943         return sp->first;
944     } else {
945         // no break for end nodes
946         if (node == sp->first) return NULL;
947         if (node == sp->last ) return NULL;
949         // create a new subpath
950        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
952         // duplicate the break node as start of the new subpath
953         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
955         // attach rest of curve to new node
956         g_assert(node->n.other);
957         newnode->n.other = node->n.other; node->n.other = NULL;
958         newnode->n.other->p.other = newnode;
959         newsubpath->last = sp->last;
960         sp->last = node;
961         node = newnode;
962         while (node->n.other) {
963             node = node->n.other;
964             node->subpath = newsubpath;
965             sp->nodes = g_list_remove(sp->nodes, node);
966             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
967         }
970         return newnode;
971     }
974 /**
975  * Duplicate node and connect to neighbours.
976  */
977 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
979     g_assert(node);
980     g_assert(node->subpath);
981     g_assert(g_list_find(node->subpath->nodes, node));
983    Inkscape::NodePath::SubPath *sp = node->subpath;
985     NRPathcode code = (NRPathcode) node->code;
986     if (code == NR_MOVETO) { // if node is the endnode,
987         node->code = NR_LINETO; // new one is inserted before it, so change that to line
988     }
990     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
992     if (!node->n.other || !node->p.other) // if node is an endnode, select it
993         return node;
994     else
995         return newnode; // otherwise select the newly created node
998 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
1000     node->p.pos = (node->pos + (node->pos - node->n.pos));
1003 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
1005     node->n.pos = (node->pos + (node->pos - node->p.pos));
1008 /**
1009  * Change line type at node, with side effects on neighbours.
1010  */
1011 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
1013     g_assert(end);
1014     g_assert(end->subpath);
1015     g_assert(end->p.other);
1017     if (end->code == static_cast< guint > ( code ) )
1018         return;
1020    Inkscape::NodePath::Node *start = end->p.other;
1022     end->code = code;
1024     if (code == NR_LINETO) {
1025         if (start->code == NR_LINETO) {
1026             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
1027         }
1028         if (end->n.other) {
1029             if (end->n.other->code == NR_LINETO) {
1030                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
1031             }
1032         }
1033     } else {
1034         NR::Point delta = end->pos - start->pos;
1035         start->n.pos = start->pos + delta / 3;
1036         end->p.pos = end->pos - delta / 3;
1037         sp_node_adjust_handle(start, 1);
1038         sp_node_adjust_handle(end, -1);
1039     }
1041     sp_node_update_handles(start);
1042     sp_node_update_handles(end);
1045 /**
1046  * Change node type, and its handles accordingly.
1047  */
1048 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1050     g_assert(node);
1051     g_assert(node->subpath);
1053     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1054         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1055             type =Inkscape::NodePath::NODE_CUSP;
1056         }
1057     }
1059     node->type = type;
1061     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1062         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1063         node->knot->setSize (node->selected? 11 : 9);
1064         sp_knot_update_ctrl(node->knot);
1065     } else {
1066         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1067         node->knot->setSize (node->selected? 9 : 7);
1068         sp_knot_update_ctrl(node->knot);
1069     }
1071     // if one of handles is mouseovered, preserve its position
1072     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1073         sp_node_adjust_handle(node, 1);
1074     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1075         sp_node_adjust_handle(node, -1);
1076     } else {
1077         sp_node_adjust_handles(node);
1078     }
1080     sp_node_update_handles(node);
1082     sp_nodepath_update_statusbar(node->subpath->nodepath);
1084     return node;
1087 bool
1088 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1090         Inkscape::NodePath::Node *othernode = side->other;
1091         if (!othernode)
1092             return false;
1093         NRPathcode const code = sp_node_path_code_from_side(node, side);
1094         if (code == NR_LINETO)
1095             return true;
1096         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1097         if (&node->p == side) {
1098             other_to_me = &othernode->n;
1099         } else if (&node->n == side) {
1100             other_to_me = &othernode->p;
1101         } 
1102         if (!other_to_me)
1103             return false;
1104         bool is_line = 
1105              (NR::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1106               NR::L2(node->pos - side->pos) < 1e-6);
1107         return is_line;
1110 /**
1111  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1112  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1113  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1114  * If already cusp and set to cusp, retracts handles.
1115 */
1116 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1118     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1120 /* 
1121   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1122  
1123         if (two_handles) {
1124             // do nothing, adjust_handles called via set_node_type will line them up
1125         } else if (one_handle) {
1126             if (opposite_to_handle_is_line) {
1127                 if (lined_up) {
1128                     // already half-smooth; pull opposite handle too making it fully smooth
1129                 } else {
1130                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1131                 }
1132             } else {
1133                 // pull opposite handle in line with the existing one
1134             }
1135         } else if (no_handles) {
1136             if (both_segments_are_lines OR both_segments_are_curves) {
1137                 //pull both handles
1138             } else {
1139                 // pull the handle opposite to line segment, making node half-smooth
1140             }
1141         }
1142 */
1143         bool p_has_handle = (NR::L2(node->pos  - node->p.pos) > 1e-6);
1144         bool n_has_handle = (NR::L2(node->pos  - node->n.pos) > 1e-6);
1145         bool p_is_line = sp_node_side_is_line(node, &node->p);
1146         bool n_is_line = sp_node_side_is_line(node, &node->n);
1148         if (p_has_handle && n_has_handle) {
1149             // do nothing, adjust_handles will line them up
1150         } else if (p_has_handle || n_has_handle) {
1151             if (p_has_handle && n_is_line) {
1152                 Radial line (node->n.other->pos - node->pos);
1153                 Radial handle (node->pos - node->p.pos);
1154                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1155                     // already half-smooth; pull opposite handle too making it fully smooth
1156                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1157                 } else {
1158                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1159                 }
1160             } else if (n_has_handle && p_is_line) {
1161                 Radial line (node->p.other->pos - node->pos);
1162                 Radial handle (node->pos - node->n.pos);
1163                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1164                     // already half-smooth; pull opposite handle too making it fully smooth
1165                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1166                 } else {
1167                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1168                 }
1169             } else if (p_has_handle && node->n.other) {
1170                 // pull n handle
1171                 node->n.other->code = NR_CURVETO;
1172                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1173                     NR::L2(node->p.pos - node->pos) :
1174                     NR::L2(node->n.other->pos - node->pos) / 3;
1175                 node->n.pos = node->pos - (len / NR::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1176             } else if (n_has_handle && node->p.other) {
1177                 // pull p handle
1178                 node->code = NR_CURVETO;
1179                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1180                     NR::L2(node->n.pos - node->pos) :
1181                     NR::L2(node->p.other->pos - node->pos) / 3;
1182                 node->p.pos = node->pos - (len / NR::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1183             }
1184         } else if (!p_has_handle && !n_has_handle) {
1185             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1186                 // no handles, but both segments are either lnes or curves:
1187                 //pull both handles
1189                 // convert both to curves:
1190                 node->code = NR_CURVETO;
1191                 node->n.other->code = NR_CURVETO;
1193                 NR::Point leg_prev = node->pos - node->p.other->pos;
1194                 NR::Point leg_next = node->pos - node->n.other->pos;
1196                 double norm_leg_prev = L2(leg_prev);
1197                 double norm_leg_next = L2(leg_next);
1199                 NR::Point delta;
1200                 if (norm_leg_next > 0.0) {
1201                     delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1202                     (&delta)->normalize();
1203                 }
1205                 if (type == Inkscape::NodePath::NODE_SYMM) {
1206                     double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1207                     node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1208                     node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1209                 } else {
1210                     // length of handle is proportional to distance to adjacent node
1211                     node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1212                     node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1213                 }
1215             } else {
1216                 // pull the handle opposite to line segment, making it half-smooth
1217                 if (p_is_line && node->n.other) {
1218                     if (type != Inkscape::NodePath::NODE_SYMM) {
1219                         // pull n handle
1220                         node->n.other->code = NR_CURVETO;
1221                         double len =  NR::L2(node->n.other->pos - node->pos) / 3;
1222                         node->n.pos = node->pos + (len / NR::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1223                     }
1224                 } else if (n_is_line && node->p.other) {
1225                     if (type != Inkscape::NodePath::NODE_SYMM) {
1226                         // pull p handle
1227                         node->code = NR_CURVETO;
1228                         double len =  NR::L2(node->p.other->pos - node->pos) / 3;
1229                         node->p.pos = node->pos + (len / NR::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1230                     }
1231                 }
1232             }
1233         }
1234     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1235         // cusping a cusp: retract nodes
1236         node->p.pos = node->pos;
1237         node->n.pos = node->pos;
1238     }
1240     sp_nodepath_set_node_type (node, type);
1243 /**
1244  * Move node to point, and adjust its and neighbouring handles.
1245  */
1246 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1248     NR::Point delta = p - node->pos;
1249     node->pos = p;
1251     node->p.pos += delta;
1252     node->n.pos += delta;
1254     Inkscape::NodePath::Node *node_p = NULL;
1255     Inkscape::NodePath::Node *node_n = NULL;
1257     if (node->p.other) {
1258         if (node->code == NR_LINETO) {
1259             sp_node_adjust_handle(node, 1);
1260             sp_node_adjust_handle(node->p.other, -1);
1261             node_p = node->p.other;
1262         }
1263     }
1264     if (node->n.other) {
1265         if (node->n.other->code == NR_LINETO) {
1266             sp_node_adjust_handle(node, -1);
1267             sp_node_adjust_handle(node->n.other, 1);
1268             node_n = node->n.other;
1269         }
1270     }
1272     // this function is only called from batch movers that will update display at the end
1273     // themselves, so here we just move all the knots without emitting move signals, for speed
1274     sp_node_update_handles(node, false);
1275     if (node_n) {
1276         sp_node_update_handles(node_n, false);
1277     }
1278     if (node_p) {
1279         sp_node_update_handles(node_p, false);
1280     }
1283 /**
1284  * Call sp_node_moveto() for node selection and handle possible snapping.
1285  */
1286 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1287                                             bool const snap, bool constrained = false, 
1288                                             Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point())
1290     NR::Coord best = NR_HUGE;
1291     NR::Point delta(dx, dy);
1292     NR::Point best_pt = delta;
1293     Inkscape::SnappedPoint best_abs;
1294     
1295     if (snap) {    
1296         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1297          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1298          * must provide that information. */
1299           
1300         // Build a list of the unselected nodes to which the snapper should snap 
1301         std::vector<Geom::Point> unselected_nodes;
1302         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1303             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1304             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1305                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1306                 if (!node->selected) {
1307                     unselected_nodes.push_back(to_2geom(node->pos));
1308                 }    
1309             }
1310         }        
1311         
1312         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1313         
1314         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1315             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1316             m.setup(NULL, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1317             Inkscape::SnappedPoint s;
1318             if (constrained) {
1319                 Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1320                 dedicated_constraint.setPoint(n->pos);
1321                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(n->pos + delta), dedicated_constraint);
1322             } else {
1323                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(n->pos + delta));
1324             }            
1325             if (s.getSnapped() && (s.getDistance() < best)) {
1326                 best = s.getDistance();
1327                 best_abs = s;
1328                 best_pt = from_2geom(s.getPoint()) - n->pos;
1329             }
1330         }
1331                         
1332         if (best_abs.getSnapped()) {
1333             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1334         } else {
1335             nodepath->desktop->snapindicator->remove_snappoint();    
1336         }
1337     }
1339     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1340         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1341         sp_node_moveto(n, n->pos + best_pt);
1342     }
1344     // do not update repr here so that node dragging is acceptably fast
1345     update_object(nodepath);
1348 /**
1349 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1350 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1351 near x = 0.
1352  */
1353 double
1354 sculpt_profile (double x, double alpha, guint profile)
1356     if (x >= 1)
1357         return 0;
1358     if (x <= 0)
1359         return 1;
1361     switch (profile) {
1362         case SCULPT_PROFILE_LINEAR:
1363         return 1 - x;
1364         case SCULPT_PROFILE_BELL:
1365         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1366         case SCULPT_PROFILE_ELLIPTIC:
1367         return sqrt(1 - x*x);
1368     }
1370     return 1;
1373 double
1374 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1376     // extremely primitive for now, don't have time to look for the real one
1377     double lower = NR::L2(b - a);
1378     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1379     return (lower + upper)/2;
1382 void
1383 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1385     n->pos = n->origin + delta;
1386     n->n.pos = n->n.origin + delta_n;
1387     n->p.pos = n->p.origin + delta_p;
1388     sp_node_adjust_handles(n);
1389     sp_node_update_handles(n, false);
1392 /**
1393  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1394  * on how far they are from the dragged node n.
1395  */
1396 static void
1397 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1399     g_assert (n);
1400     g_assert (nodepath);
1401     g_assert (n->subpath->nodepath == nodepath);
1403     double pressure = n->knot->pressure;
1404     if (pressure == 0)
1405         pressure = 0.5; // default
1406     pressure = CLAMP (pressure, 0.2, 0.8);
1408     // map pressure to alpha = 1/5 ... 5
1409     double alpha = 1 - 2 * fabs(pressure - 0.5);
1410     if (pressure > 0.5)
1411         alpha = 1/alpha;
1413     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1415     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1416         // Only one subpath has selected nodes:
1417         // use linear mode, where the distance from n to node being dragged is calculated along the path
1419         double n_sel_range = 0, p_sel_range = 0;
1420         guint n_nodes = 0, p_nodes = 0;
1421         guint n_sel_nodes = 0, p_sel_nodes = 0;
1423         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1424         {
1425             double n_range = 0, p_range = 0;
1426             bool n_going = true, p_going = true;
1427             Inkscape::NodePath::Node *n_node = n;
1428             Inkscape::NodePath::Node *p_node = n;
1429             do {
1430                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1431                 if (n_node && n_going)
1432                     n_node = n_node->n.other;
1433                 if (n_node == NULL) {
1434                     n_going = false;
1435                 } else {
1436                     n_nodes ++;
1437                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1438                     if (n_node->selected) {
1439                         n_sel_nodes ++;
1440                         n_sel_range = n_range;
1441                     }
1442                     if (n_node == p_node) {
1443                         n_going = false;
1444                         p_going = false;
1445                     }
1446                 }
1447                 if (p_node && p_going)
1448                     p_node = p_node->p.other;
1449                 if (p_node == NULL) {
1450                     p_going = false;
1451                 } else {
1452                     p_nodes ++;
1453                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1454                     if (p_node->selected) {
1455                         p_sel_nodes ++;
1456                         p_sel_range = p_range;
1457                     }
1458                     if (p_node == n_node) {
1459                         n_going = false;
1460                         p_going = false;
1461                     }
1462                 }
1463             } while (n_going || p_going);
1464         }
1466         // Second pass: actually move nodes in this subpath
1467         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1468         {
1469             double n_range = 0, p_range = 0;
1470             bool n_going = true, p_going = true;
1471             Inkscape::NodePath::Node *n_node = n;
1472             Inkscape::NodePath::Node *p_node = n;
1473             do {
1474                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1475                 if (n_node && n_going)
1476                     n_node = n_node->n.other;
1477                 if (n_node == NULL) {
1478                     n_going = false;
1479                 } else {
1480                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1481                     if (n_node->selected) {
1482                         sp_nodepath_move_node_and_handles (n_node,
1483                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1484                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1485                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1486                     }
1487                     if (n_node == p_node) {
1488                         n_going = false;
1489                         p_going = false;
1490                     }
1491                 }
1492                 if (p_node && p_going)
1493                     p_node = p_node->p.other;
1494                 if (p_node == NULL) {
1495                     p_going = false;
1496                 } else {
1497                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1498                     if (p_node->selected) {
1499                         sp_nodepath_move_node_and_handles (p_node,
1500                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1501                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1502                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1503                     }
1504                     if (p_node == n_node) {
1505                         n_going = false;
1506                         p_going = false;
1507                     }
1508                 }
1509             } while (n_going || p_going);
1510         }
1512     } else {
1513         // Multiple subpaths have selected nodes:
1514         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1515         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1516         // fix the pear-like shape when sculpting e.g. a ring
1518         // First pass: calculate range
1519         gdouble direct_range = 0;
1520         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1521             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1522             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1523                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1524                 if (node->selected) {
1525                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1526                 }
1527             }
1528         }
1530         // Second pass: actually move nodes
1531         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1532             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1533             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1534                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1535                 if (node->selected) {
1536                     if (direct_range > 1e-6) {
1537                         sp_nodepath_move_node_and_handles (node,
1538                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1539                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1540                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1541                     } else {
1542                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1543                     }
1545                 }
1546             }
1547         }
1548     }
1550     // do not update repr here so that node dragging is acceptably fast
1551     update_object(nodepath);
1555 /**
1556  * Move node selection to point, adjust its and neighbouring handles,
1557  * handle possible snapping, and commit the change with possible undo.
1558  */
1559 void
1560 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1562     if (!nodepath) return;
1564     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1566     if (dx == 0) {
1567         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1568     } else if (dy == 0) {
1569         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1570     } else {
1571         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1572     }
1575 /**
1576  * Move node selection off screen and commit the change.
1577  */
1578 void
1579 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1581     // borrowed from sp_selection_move_screen in selection-chemistry.c
1582     // we find out the current zoom factor and divide deltas by it
1583     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1585     gdouble zoom = desktop->current_zoom();
1586     gdouble zdx = dx / zoom;
1587     gdouble zdy = dy / zoom;
1589     if (!nodepath) return;
1591     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1593     if (dx == 0) {
1594         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1595     } else if (dy == 0) {
1596         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1597     } else {
1598         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1599     }
1602 /**
1603  * Move selected nodes to the absolute position given
1604  */
1605 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis)
1607     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1608         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1609         Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]);
1610         sp_node_moveto(n, npos);
1611     }
1613     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1616 /**
1617  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1618  */
1619 boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1621     boost::optional<Geom::Coord> no_coord;
1622     g_return_val_if_fail(nodepath->selected, no_coord);
1624     // determine coordinate of first selected node
1625     GList *nsel = nodepath->selected;
1626     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1627     NR::Coord coord = n->pos[axis];
1628     bool coincide = true;
1630     // compare it to the coordinates of all the other selected nodes
1631     for (GList *l = nsel->next; l != NULL; l = l->next) {
1632         n = (Inkscape::NodePath::Node *) l->data;
1633         if (n->pos[axis] != coord) {
1634             coincide = false;
1635         }
1636     }
1637     if (coincide) {
1638         return coord;
1639     } else {
1640         Geom::Rect bbox = sp_node_selected_bbox(nodepath);
1641         // currently we return the coordinate of the bounding box midpoint because I don't know how
1642         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1643         return bbox.midpoint()[axis];
1644     }
1647 /** If they don't yet exist, creates knot and line for the given side of the node */
1648 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1650     if (!side->knot) {
1651         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"));
1653         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1654         side->knot->setSize (7);
1655         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1656         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1657         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1658         sp_knot_update_ctrl(side->knot);
1660         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1661         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1662         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1663         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1664         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1665         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1666     }
1668     if (!side->line) {
1669         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1670                                         SP_TYPE_CTRLLINE, NULL);
1671     }
1674 /**
1675  * Ensure the given handle of the node is visible/invisible, update its screen position
1676  */
1677 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1679     g_assert(node != NULL);
1681    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1682     NRPathcode code = sp_node_path_code_from_side(node, side);
1684     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1686     if (show_handle) {
1687         if (!side->knot) { // No handle knot at all
1688             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1689             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1690             side->knot->pos = side->pos;
1691             if (side->knot->item)
1692                 SP_CTRL(side->knot->item)->moveto(side->pos);
1693             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1694             sp_knot_show(side->knot);
1695         } else {
1696             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1697                 if (fire_move_signals) {
1698                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1699                 } else {
1700                     sp_knot_moveto(side->knot, side->pos);
1701                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1702                 }
1703             }
1704             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1705                 sp_knot_show(side->knot);
1706             }
1707         }
1708         sp_canvas_item_show(side->line);
1709     } else {
1710         if (side->knot) {
1711             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1712                 sp_knot_hide(side->knot);
1713             }
1714         }
1715         if (side->line) {
1716             sp_canvas_item_hide(side->line);
1717         }
1718     }
1721 /**
1722  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1723  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1724  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1725  * updated; otherwise, just move the knots silently (used in batch moves).
1726  */
1727 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1729     g_assert(node != NULL);
1731     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1732         sp_knot_show(node->knot);
1733     }
1735     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1736         if (fire_move_signals)
1737             sp_knot_set_position(node->knot, node->pos, 0);
1738         else
1739             sp_knot_moveto(node->knot, node->pos);
1740     }
1742     gboolean show_handles = node->selected;
1743     if (node->p.other != NULL) {
1744         if (node->p.other->selected) show_handles = TRUE;
1745     }
1746     if (node->n.other != NULL) {
1747         if (node->n.other->selected) show_handles = TRUE;
1748     }
1750     if (node->subpath->nodepath->show_handles == false)
1751         show_handles = FALSE;
1753     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1754     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1757 /**
1758  * Call sp_node_update_handles() for all nodes on subpath.
1759  */
1760 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1762     g_assert(subpath != NULL);
1764     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1765         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1766     }
1769 /**
1770  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1771  */
1772 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1774     g_assert(nodepath != NULL);
1776     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1777         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1778     }
1781 void
1782 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1784     if (nodepath == NULL) return;
1786     nodepath->show_handles = show;
1787     sp_nodepath_update_handles(nodepath);
1790 /**
1791  * Adds all selected nodes in nodepath to list.
1792  */
1793 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1795     StlConv<Node *>::list(l, selected);
1796 /// \todo this adds a copying, rework when the selection becomes a stl list
1799 /**
1800  * Align selected nodes on the specified axis.
1801  */
1802 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1804     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1805         return;
1806     }
1808     if ( !nodepath->selected->next ) { // only one node selected
1809         return;
1810     }
1811    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1812     NR::Point dest(pNode->pos);
1813     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1814         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1815         if (pNode) {
1816             dest[axis] = pNode->pos[axis];
1817             sp_node_moveto(pNode, dest);
1818         }
1819     }
1821     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1824 /// Helper struct.
1825 struct NodeSort
1827    Inkscape::NodePath::Node *_node;
1828     NR::Coord _coord;
1829     /// \todo use vectorof pointers instead of calling copy ctor
1830     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1831         _node(node), _coord(node->pos[axis])
1832     {}
1834 };
1836 static bool operator<(NodeSort const &a, NodeSort const &b)
1838     return (a._coord < b._coord);
1841 /**
1842  * Distribute selected nodes on the specified axis.
1843  */
1844 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1846     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1847         return;
1848     }
1850     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1851         return;
1852     }
1854    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1855     std::vector<NodeSort> sorted;
1856     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1857         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1858         if (pNode) {
1859             NodeSort n(pNode, axis);
1860             sorted.push_back(n);
1861             //dest[axis] = pNode->pos[axis];
1862             //sp_node_moveto(pNode, dest);
1863         }
1864     }
1865     std::sort(sorted.begin(), sorted.end());
1866     unsigned int len = sorted.size();
1867     //overall bboxes span
1868     float dist = (sorted.back()._coord -
1869                   sorted.front()._coord);
1870     //new distance between each bbox
1871     float step = (dist) / (len - 1);
1872     float pos = sorted.front()._coord;
1873     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1874           it < sorted.end();
1875           it ++ )
1876     {
1877         NR::Point dest((*it)._node->pos);
1878         dest[axis] = pos;
1879         sp_node_moveto((*it)._node, dest);
1880         pos += step;
1881     }
1883     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1887 /**
1888  * Call sp_nodepath_line_add_node() for all selected segments.
1889  */
1890 void
1891 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1893     if (!nodepath) {
1894         return;
1895     }
1897     GList *nl = NULL;
1899     int n_added = 0;
1901     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1902        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1903         g_assert(t->selected);
1904         if (t->p.other && t->p.other->selected) {
1905             nl = g_list_prepend(nl, t);
1906         }
1907     }
1909     while (nl) {
1910        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1911        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1912        sp_nodepath_node_select(n, TRUE, FALSE);
1913        n_added ++;
1914        nl = g_list_remove(nl, t);
1915     }
1917     /** \todo fixme: adjust ? */
1918     sp_nodepath_update_handles(nodepath);
1920     if (n_added > 1) {
1921         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1922     } else if (n_added > 0) {
1923         sp_nodepath_update_repr(nodepath, _("Add node"));
1924     }
1926     sp_nodepath_update_statusbar(nodepath);
1929 /**
1930  * Select segment nearest to point
1931  */
1932 void
1933 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1935     if (!nodepath) {
1936         return;
1937     }
1939     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
1940     Geom::PathVector const &pathv = curve->get_pathvector();
1941     Geom::PathVectorPosition pvpos = Geom::nearestPoint(pathv, p);
1943     // calculate index for nodepath's representation.
1944     unsigned int segment_index = floor(pvpos.t) + 1;
1945     for (unsigned int i = 0; i < pvpos.path_nr; ++i) {
1946         segment_index += pathv[i].size() + 1;
1947         if (pathv[i].closed()) {
1948             segment_index += 1;
1949         }
1950     }
1952     curve->unref();
1954     //find segment to segment
1955     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(segment_index);
1957     //fixme: this can return NULL, so check before proceeding.
1958     g_return_if_fail(e != NULL);
1960     gboolean force = FALSE;
1961     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1962         force = TRUE;
1963     }
1964     sp_nodepath_node_select(e, (gboolean) toggle, force);
1965     if (e->p.other)
1966         sp_nodepath_node_select(e->p.other, TRUE, force);
1968     sp_nodepath_update_handles(nodepath);
1970     sp_nodepath_update_statusbar(nodepath);
1973 /**
1974  * Add a node nearest to point
1975  */
1976 void
1977 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1979     if (!nodepath) {
1980         return;
1981     }
1983     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
1984     Geom::PathVector const &pathv = curve->get_pathvector();
1985     Geom::PathVectorPosition pvpos = Geom::nearestPoint(pathv, p);
1987     // calculate index for nodepath's representation.
1988     double int_part;
1989     double t = std::modf(pvpos.t, &int_part);
1990     unsigned int segment_index = (unsigned int)int_part + 1;
1991     for (unsigned int i = 0; i < pvpos.path_nr; ++i) {
1992         segment_index += pathv[i].size() + 1;
1993         if (pathv[i].closed()) {
1994             segment_index += 1;
1995         }
1996     }
1998     curve->unref();
2000     //find segment to split
2001     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(segment_index);
2003     //don't know why but t seems to flip for lines
2004     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2005         t = 1.0 - t;
2006     }
2008     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2009     sp_nodepath_node_select(n, FALSE, TRUE);
2011     /* fixme: adjust ? */
2012     sp_nodepath_update_handles(nodepath);
2014     sp_nodepath_update_repr(nodepath, _("Add node"));
2016     sp_nodepath_update_statusbar(nodepath);
2019 /*
2020  * Adjusts a segment so that t moves by a certain delta for dragging
2021  * converts lines to curves
2022  *
2023  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2024  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2025  */
2026 void
2027 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
2029     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
2031     //fixme: e and e->p can be NULL, so check for those before proceeding
2032     g_return_if_fail(e != NULL);
2033     g_return_if_fail(&e->p != NULL);
2035     /* feel good is an arbitrary parameter that distributes the delta between handles
2036      * if t of the drag point is less than 1/6 distance form the endpoint only
2037      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2038      */
2039     double feel_good;
2040     if (t <= 1.0 / 6.0)
2041         feel_good = 0;
2042     else if (t <= 0.5)
2043         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2044     else if (t <= 5.0 / 6.0)
2045         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2046     else
2047         feel_good = 1;
2049     //if we're dragging a line convert it to a curve
2050     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2051         sp_nodepath_set_line_type(e, NR_CURVETO);
2052     }
2054     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2055     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2056     e->p.other->n.pos += offsetcoord0;
2057     e->p.pos += offsetcoord1;
2059     // adjust handles of adjacent nodes where necessary
2060     sp_node_adjust_handle(e,1);
2061     sp_node_adjust_handle(e->p.other,-1);
2063     sp_nodepath_update_handles(e->subpath->nodepath);
2065     update_object(e->subpath->nodepath);
2067     sp_nodepath_update_statusbar(e->subpath->nodepath);
2071 /**
2072  * Call sp_nodepath_break() for all selected segments.
2073  */
2074 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2076     if (!nodepath) return;
2078     GList *tempin = g_list_copy(nodepath->selected);
2079     GList *temp = NULL;
2080     for (GList *l = tempin; l != NULL; l = l->next) {
2081        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2082        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2083         if (nn == NULL) continue; // no break, no new node
2084         temp = g_list_prepend(temp, nn);
2085     }
2086     g_list_free(tempin);
2088     if (temp) {
2089         sp_nodepath_deselect(nodepath);
2090     }
2091     for (GList *l = temp; l != NULL; l = l->next) {
2092         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2093     }
2095     sp_nodepath_update_handles(nodepath);
2097     sp_nodepath_update_repr(nodepath, _("Break path"));
2100 /**
2101  * Duplicate the selected node(s).
2102  */
2103 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2105     if (!nodepath) {
2106         return;
2107     }
2109     GList *temp = NULL;
2110     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2111        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2112        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2113         if (nn == NULL) continue; // could not duplicate
2114         temp = g_list_prepend(temp, nn);
2115     }
2117     if (temp) {
2118         sp_nodepath_deselect(nodepath);
2119     }
2120     for (GList *l = temp; l != NULL; l = l->next) {
2121         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2122     }
2124     sp_nodepath_update_handles(nodepath);
2126     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2129 /**
2130  *  Internal function to join two nodes by merging them into one.
2131  */
2132 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2134     /* a and b are endpoints */
2136     // if one of the two nodes is mouseovered, fix its position
2137     NR::Point c;
2138     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2139         c = a->pos;
2140     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2141         c = b->pos;
2142     } else {
2143         // otherwise, move joined node to the midpoint
2144         c = (a->pos + b->pos) / 2;
2145     }
2147     if (a->subpath == b->subpath) {
2148        Inkscape::NodePath::SubPath *sp = a->subpath;
2149         sp_nodepath_subpath_close(sp);
2150         sp_node_moveto (sp->first, c);
2152         sp_nodepath_update_handles(sp->nodepath);
2153         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2154         return;
2155     }
2157     /* a and b are separate subpaths */
2158     Inkscape::NodePath::SubPath *sa = a->subpath;
2159     Inkscape::NodePath::SubPath *sb = b->subpath;
2160     NR::Point p;
2161     Inkscape::NodePath::Node *n;
2162     NRPathcode code;
2163     if (a == sa->first) {
2164         // we will now reverse sa, so that a is its last node, not first, and drop that node
2165         p = sa->first->n.pos;
2166         code = (NRPathcode)sa->first->n.other->code;
2167         // create new subpath
2168        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2169        // create a first moveto node on it
2170         n = sa->last;
2171         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2172         n = n->p.other;
2173         if (n == sa->first) n = NULL;
2174         while (n) {
2175             // copy the rest of the nodes from sa to t, going backwards
2176             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2177             n = n->p.other;
2178             if (n == sa->first) n = NULL;
2179         }
2180         // replace sa with t
2181         sp_nodepath_subpath_destroy(sa);
2182         sa = t;
2183     } else if (a == sa->last) {
2184         // a is already last, just drop it
2185         p = sa->last->p.pos;
2186         code = (NRPathcode)sa->last->code;
2187         sp_nodepath_node_destroy(sa->last);
2188     } else {
2189         code = NR_END;
2190         g_assert_not_reached();
2191     }
2193     if (b == sb->first) {
2194         // copy all nodes from b to a, forward 
2195         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2196         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2197             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2198         }
2199     } else if (b == sb->last) {
2200         // copy all nodes from b to a, backward 
2201         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2202         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2203             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2204         }
2205     } else {
2206         g_assert_not_reached();
2207     }
2208     /* and now destroy sb */
2210     sp_nodepath_subpath_destroy(sb);
2212     sp_nodepath_update_handles(sa->nodepath);
2214     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2216     sp_nodepath_update_statusbar(nodepath);
2219 /**
2220  *  Internal function to join two nodes by adding a segment between them.
2221  */
2222 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2224     if (a->subpath == b->subpath) {
2225        Inkscape::NodePath::SubPath *sp = a->subpath;
2227         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2228         sp->closed = TRUE;
2230         sp->first->p.other = sp->last;
2231         sp->last->n.other  = sp->first;
2233         sp_node_handle_mirror_p_to_n(sp->last);
2234         sp_node_handle_mirror_n_to_p(sp->first);
2236         sp->first->code = sp->last->code;
2237         sp->first       = sp->last;
2239         sp_nodepath_update_handles(sp->nodepath);
2241         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2243         return;
2244     }
2246     /* a and b are separate subpaths */
2247     Inkscape::NodePath::SubPath *sa = a->subpath;
2248     Inkscape::NodePath::SubPath *sb = b->subpath;
2250     Inkscape::NodePath::Node *n;
2251     NR::Point p;
2252     NRPathcode code;
2253     if (a == sa->first) {
2254         code = (NRPathcode) sa->first->n.other->code;
2255        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2256         n = sa->last;
2257         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2258         for (n = n->p.other; n != NULL; n = n->p.other) {
2259             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2260         }
2261         sp_nodepath_subpath_destroy(sa);
2262         sa = t;
2263     } else if (a == sa->last) {
2264         code = (NRPathcode)sa->last->code;
2265     } else {
2266         code = NR_END;
2267         g_assert_not_reached();
2268     }
2270     if (b == sb->first) {
2271         n = sb->first;
2272         sp_node_handle_mirror_p_to_n(sa->last);
2273         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2274         sp_node_handle_mirror_n_to_p(sa->last);
2275         for (n = n->n.other; n != NULL; n = n->n.other) {
2276             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2277         }
2278     } else if (b == sb->last) {
2279         n = sb->last;
2280         sp_node_handle_mirror_p_to_n(sa->last);
2281         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2282         sp_node_handle_mirror_n_to_p(sa->last);
2283         for (n = n->p.other; n != NULL; n = n->p.other) {
2284             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2285         }
2286     } else {
2287         g_assert_not_reached();
2288     }
2289     /* and now destroy sb */
2291     sp_nodepath_subpath_destroy(sb);
2293     sp_nodepath_update_handles(sa->nodepath);
2295     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2298 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2300 /**
2301  * Internal function to handle joining two nodes.
2302  */
2303 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2305     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2307     if (g_list_length(nodepath->selected) != 2) {
2308         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2309         return;
2310     }
2312     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2313     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2315     g_assert(a != b);
2316     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2317         // someone tried to join an orphan node (i.e. a single-node subpath).
2318         // this is not worth an error message, just fail silently.
2319         return;
2320     }
2322     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2323         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2324         return;
2325     }
2327     switch(mode) {
2328         case NODE_JOIN_ENDPOINTS:
2329             do_node_selected_join(nodepath, a, b);
2330             break;
2331         case NODE_JOIN_SEGMENT:
2332             do_node_selected_join_segment(nodepath, a, b);
2333             break;
2334     }
2337 /**
2338  *  Join two nodes by merging them into one.
2339  */
2340 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2342     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2345 /**
2346  *  Join two nodes by adding a segment between them.
2347  */
2348 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2350     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2353 /**
2354  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2355  */
2356 void sp_node_delete_preserve(GList *nodes_to_delete)
2358     GSList *nodepaths = NULL;
2360     while (nodes_to_delete) {
2361         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2362         Inkscape::NodePath::SubPath *sp = node->subpath;
2363         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2364         Inkscape::NodePath::Node *sample_cursor = NULL;
2365         Inkscape::NodePath::Node *sample_end = NULL;
2366         Inkscape::NodePath::Node *delete_cursor = node;
2367         bool just_delete = false;
2369         //find the start of this contiguous selection
2370         //move left to the first node that is not selected
2371         //or the start of the non-closed path
2372         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2373             delete_cursor = curr;
2374         }
2376         //just delete at the beginning of an open path
2377         if (!delete_cursor->p.other) {
2378             sample_cursor = delete_cursor;
2379             just_delete = true;
2380         } else {
2381             sample_cursor = delete_cursor->p.other;
2382         }
2384         //calculate points for each segment
2385         int rate = 5;
2386         float period = 1.0 / rate;
2387         std::vector<NR::Point> data;
2388         if (!just_delete) {
2389             data.push_back(sample_cursor->pos);
2390             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2391                 //just delete at the end of an open path
2392                 if (!sp->closed && curr == sp->last) {
2393                     just_delete = true;
2394                     break;
2395                 }
2397                 //sample points on the contiguous selected segment
2398                 NR::Point *bez;
2399                 bez = new NR::Point [4];
2400                 bez[0] = curr->pos;
2401                 bez[1] = curr->n.pos;
2402                 bez[2] = curr->n.other->p.pos;
2403                 bez[3] = curr->n.other->pos;
2404                 for (int i=1; i<rate; i++) {
2405                     gdouble t = i * period;
2406                     NR::Point p = bezier_pt(3, bez, t);
2407                     data.push_back(p);
2408                 }
2409                 data.push_back(curr->n.other->pos);
2411                 sample_end = curr->n.other;
2412                 //break if we've come full circle or hit the end of the selection
2413                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2414                     break;
2415                 }
2416             }
2417         }
2419         if (!just_delete) {
2420             //calculate the best fitting single segment and adjust the endpoints
2421             NR::Point *adata;
2422             adata = new NR::Point [data.size()];
2423             copy(data.begin(), data.end(), adata);
2425             NR::Point *bez;
2426             bez = new NR::Point [4];
2427             //would decreasing error create a better fitting approximation?
2428             gdouble error = 1.0;
2429             gint ret;
2430             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2432             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2433             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2434             //the resulting nodes behave as expected.
2435             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2436                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2437             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2438                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2440             //adjust endpoints
2441             sample_cursor->n.pos = bez[1];
2442             sample_end->p.pos = bez[2];
2443         }
2445         //destroy this contiguous selection
2446         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2447             Inkscape::NodePath::Node *temp = delete_cursor;
2448             if (delete_cursor->n.other == delete_cursor) {
2449                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2450                 delete_cursor = NULL;
2451             } else {
2452                 delete_cursor = delete_cursor->n.other;
2453             }
2454             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2455             sp_nodepath_node_destroy(temp);
2456         }
2458         sp_nodepath_update_handles(nodepath);
2460         if (!g_slist_find(nodepaths, nodepath))
2461             nodepaths = g_slist_prepend (nodepaths, nodepath);
2462     }
2464     for (GSList *i = nodepaths; i; i = i->next) {
2465         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2466         // different nodepaths will give us one undo event per nodepath
2467         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2469         // if the entire nodepath is removed, delete the selected object.
2470         if (nodepath->subpaths == NULL ||
2471             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2472             //at least 2
2473             sp_nodepath_get_node_count(nodepath) < 2) {
2474             SPDocument *document = sp_desktop_document (nodepath->desktop);
2475             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2476             //delete this nodepath's object, not the entire selection! (though at this time, this
2477             //does not matter)
2478             sp_selection_delete();
2479             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2480                               _("Delete nodes"));
2481         } else {
2482             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2483             sp_nodepath_update_statusbar(nodepath);
2484         }
2485     }
2487     g_slist_free (nodepaths);
2490 /**
2491  * Delete one or more selected nodes.
2492  */
2493 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2495     if (!nodepath) return;
2496     if (!nodepath->selected) return;
2498     /** \todo fixme: do it the right way */
2499     while (nodepath->selected) {
2500        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2501         sp_nodepath_node_destroy(node);
2502     }
2505     //clean up the nodepath (such as for trivial subpaths)
2506     sp_nodepath_cleanup(nodepath);
2508     sp_nodepath_update_handles(nodepath);
2510     // if the entire nodepath is removed, delete the selected object.
2511     if (nodepath->subpaths == NULL ||
2512         sp_nodepath_get_node_count(nodepath) < 2) {
2513         SPDocument *document = sp_desktop_document (nodepath->desktop);
2514         sp_selection_delete();
2515         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2516                           _("Delete nodes"));
2517         return;
2518     }
2520     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2522     sp_nodepath_update_statusbar(nodepath);
2525 /**
2526  * Delete one or more segments between two selected nodes.
2527  * This is the code for 'split'.
2528  */
2529 void
2530 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2532    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2533    Inkscape::NodePath::Node *curr, *next;     //Iterators
2535     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2537     if (g_list_length(nodepath->selected) != 2) {
2538         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2539                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2540         return;
2541     }
2543     //Selected nodes, not inclusive
2544    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2545    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2547     if ( ( a==b)                       ||  //same node
2548          (a->subpath  != b->subpath )  ||  //not the same path
2549          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2550          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2551     {
2552         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2553                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2554         return;
2555     }
2557     //###########################################
2558     //# BEGIN EDITS
2559     //###########################################
2560     //##################################
2561     //# CLOSED PATH
2562     //##################################
2563     if (a->subpath->closed) {
2566         gboolean reversed = FALSE;
2568         //Since we can go in a circle, we need to find the shorter distance.
2569         //  a->b or b->a
2570         start = end = NULL;
2571         int distance    = 0;
2572         int minDistance = 0;
2573         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2574             if (curr==b) {
2575                 //printf("a to b:%d\n", distance);
2576                 start = a;//go from a to b
2577                 end   = b;
2578                 minDistance = distance;
2579                 //printf("A to B :\n");
2580                 break;
2581             }
2582             distance++;
2583         }
2585         //try again, the other direction
2586         distance = 0;
2587         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2588             if (curr==a) {
2589                 //printf("b to a:%d\n", distance);
2590                 if (distance < minDistance) {
2591                     start    = b;  //we go from b to a
2592                     end      = a;
2593                     reversed = TRUE;
2594                     //printf("B to A\n");
2595                 }
2596                 break;
2597             }
2598             distance++;
2599         }
2602         //Copy everything from 'end' to 'start' to a new subpath
2603        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2604         for (curr=end ; curr ; curr=curr->n.other) {
2605             NRPathcode code = (NRPathcode) curr->code;
2606             if (curr == end)
2607                 code = NR_MOVETO;
2608             sp_nodepath_node_new(t, NULL,
2609                                  (Inkscape::NodePath::NodeType)curr->type, code,
2610                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2611             if (curr == start)
2612                 break;
2613         }
2614         sp_nodepath_subpath_destroy(a->subpath);
2617     }
2621     //##################################
2622     //# OPEN PATH
2623     //##################################
2624     else {
2626         //We need to get the direction of the list between A and B
2627         //Can we walk from a to b?
2628         start = end = NULL;
2629         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2630             if (curr==b) {
2631                 start = a;  //did it!  we go from a to b
2632                 end   = b;
2633                 //printf("A to B\n");
2634                 break;
2635             }
2636         }
2637         if (!start) {//didn't work?  let's try the other direction
2638             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2639                 if (curr==a) {
2640                     start = b;  //did it!  we go from b to a
2641                     end   = a;
2642                     //printf("B to A\n");
2643                     break;
2644                 }
2645             }
2646         }
2647         if (!start) {
2648             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2649                                                      _("Cannot find path between nodes."));
2650             return;
2651         }
2655         //Copy everything after 'end' to a new subpath
2656        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2657         for (curr=end ; curr ; curr=curr->n.other) {
2658             NRPathcode code = (NRPathcode) curr->code;
2659             if (curr == end)
2660                 code = NR_MOVETO;
2661             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2662                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2663         }
2665         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2666         for (curr = start->n.other ; curr  ; curr=next) {
2667             next = curr->n.other;
2668             sp_nodepath_node_destroy(curr);
2669         }
2671     }
2672     //###########################################
2673     //# END EDITS
2674     //###########################################
2676     //clean up the nodepath (such as for trivial subpaths)
2677     sp_nodepath_cleanup(nodepath);
2679     sp_nodepath_update_handles(nodepath);
2681     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2683     sp_nodepath_update_statusbar(nodepath);
2686 /**
2687  * Call sp_nodepath_set_line() for all selected segments.
2688  */
2689 void
2690 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2692     if (nodepath == NULL) return;
2694     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2695        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2696         g_assert(n->selected);
2697         if (n->p.other && n->p.other->selected) {
2698             sp_nodepath_set_line_type(n, code);
2699         }
2700     }
2702     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2705 /**
2706  * Call sp_nodepath_convert_node_type() for all selected nodes.
2707  */
2708 void
2709 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2711     if (nodepath == NULL) return;
2713     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2715     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2716         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2717     }
2719     sp_nodepath_update_repr(nodepath, _("Change node type"));
2722 /**
2723  * Change select status of node, update its own and neighbour handles.
2724  */
2725 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2727     node->selected = selected;
2729     if (selected) {
2730         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2731         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2732         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2733         sp_knot_update_ctrl(node->knot);
2734     } else {
2735         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2736         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2737         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2738         sp_knot_update_ctrl(node->knot);
2739     }
2741     sp_node_update_handles(node);
2742     if (node->n.other) sp_node_update_handles(node->n.other);
2743     if (node->p.other) sp_node_update_handles(node->p.other);
2746 /**
2747 \brief Select a node
2748 \param node     The node to select
2749 \param incremental   If true, add to selection, otherwise deselect others
2750 \param override   If true, always select this node, otherwise toggle selected status
2751 */
2752 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2754     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2756     if (incremental) {
2757         if (override) {
2758             if (!g_list_find(nodepath->selected, node)) {
2759                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2760             }
2761             sp_node_set_selected(node, TRUE);
2762         } else { // toggle
2763             if (node->selected) {
2764                 g_assert(g_list_find(nodepath->selected, node));
2765                 nodepath->selected = g_list_remove(nodepath->selected, node);
2766             } else {
2767                 g_assert(!g_list_find(nodepath->selected, node));
2768                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2769             }
2770             sp_node_set_selected(node, !node->selected);
2771         }
2772     } else {
2773         sp_nodepath_deselect(nodepath);
2774         nodepath->selected = g_list_prepend(nodepath->selected, node);
2775         sp_node_set_selected(node, TRUE);
2776     }
2778     sp_nodepath_update_statusbar(nodepath);
2782 /**
2783 \brief Deselect all nodes in the nodepath
2784 */
2785 void
2786 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2788     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2790     while (nodepath->selected) {
2791         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2792         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2793     }
2794     sp_nodepath_update_statusbar(nodepath);
2797 /**
2798 \brief Select or invert selection of all nodes in the nodepath
2799 */
2800 void
2801 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2803     if (!nodepath) return;
2805     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2806        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2807         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2808            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2809            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2810         }
2811     }
2814 /**
2815  * If nothing selected, does the same as sp_nodepath_select_all();
2816  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2817  * (i.e., similar to "select all in layer", with the "selected" subpaths
2818  * being treated as "layers" in the path).
2819  */
2820 void
2821 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2823     if (!nodepath) return;
2825     if (g_list_length (nodepath->selected) == 0) {
2826         sp_nodepath_select_all (nodepath, invert);
2827         return;
2828     }
2830     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2831     GSList *subpaths = NULL;
2833     for (GList *l = copy; l != NULL; l = l->next) {
2834         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2835         Inkscape::NodePath::SubPath *subpath = n->subpath;
2836         if (!g_slist_find (subpaths, subpath))
2837             subpaths = g_slist_prepend (subpaths, subpath);
2838     }
2840     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2841         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2842         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2843             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2844             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2845         }
2846     }
2848     g_slist_free (subpaths);
2849     g_list_free (copy);
2852 /**
2853  * \brief Select the node after the last selected; if none is selected,
2854  * select the first within path.
2855  */
2856 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2858     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2860    Inkscape::NodePath::Node *last = NULL;
2861     if (nodepath->selected) {
2862         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2863            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2864             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2865             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2866                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2867                 if (node->selected) {
2868                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2869                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2870                             if (spl->next) { // there's a next subpath
2871                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2872                                 last = subpath_next->first;
2873                             } else if (spl->prev) { // there's a previous subpath
2874                                 last = NULL; // to be set later to the first node of first subpath
2875                             } else {
2876                                 last = node->n.other;
2877                             }
2878                         } else {
2879                             last = node->n.other;
2880                         }
2881                     } else {
2882                         if (node->n.other) {
2883                             last = node->n.other;
2884                         } else {
2885                             if (spl->next) { // there's a next subpath
2886                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2887                                 last = subpath_next->first;
2888                             } else if (spl->prev) { // there's a previous subpath
2889                                 last = NULL; // to be set later to the first node of first subpath
2890                             } else {
2891                                 last = (Inkscape::NodePath::Node *) subpath->first;
2892                             }
2893                         }
2894                     }
2895                 }
2896             }
2897         }
2898         sp_nodepath_deselect(nodepath);
2899     }
2901     if (last) { // there's at least one more node after selected
2902         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2903     } else { // no more nodes, select the first one in first subpath
2904        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2905         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2906     }
2909 /**
2910  * \brief Select the node before the first selected; if none is selected,
2911  * select the last within path
2912  */
2913 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2915     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2917    Inkscape::NodePath::Node *last = NULL;
2918     if (nodepath->selected) {
2919         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2920            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2921             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2922                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2923                 if (node->selected) {
2924                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2925                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2926                             if (spl->prev) { // there's a prev subpath
2927                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2928                                 last = subpath_prev->last;
2929                             } else if (spl->next) { // there's a next subpath
2930                                 last = NULL; // to be set later to the last node of last subpath
2931                             } else {
2932                                 last = node->p.other;
2933                             }
2934                         } else {
2935                             last = node->p.other;
2936                         }
2937                     } else {
2938                         if (node->p.other) {
2939                             last = node->p.other;
2940                         } else {
2941                             if (spl->prev) { // there's a prev subpath
2942                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2943                                 last = subpath_prev->last;
2944                             } else if (spl->next) { // there's a next subpath
2945                                 last = NULL; // to be set later to the last node of last subpath
2946                             } else {
2947                                 last = (Inkscape::NodePath::Node *) subpath->last;
2948                             }
2949                         }
2950                     }
2951                 }
2952             }
2953         }
2954         sp_nodepath_deselect(nodepath);
2955     }
2957     if (last) { // there's at least one more node before selected
2958         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2959     } else { // no more nodes, select the last one in last subpath
2960         GList *spl = g_list_last(nodepath->subpaths);
2961        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2962         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2963     }
2966 /**
2967  * \brief Select all nodes that are within the rectangle.
2968  */
2969 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2971     if (!incremental) {
2972         sp_nodepath_deselect(nodepath);
2973     }
2975     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2976        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2977         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2978            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2980             if (b.contains(node->pos)) {
2981                 sp_nodepath_node_select(node, TRUE, TRUE);
2982             }
2983         }
2984     }
2988 void
2989 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2991     g_assert (n);
2992     g_assert (nodepath);
2993     g_assert (n->subpath->nodepath == nodepath);
2995     if (g_list_length (nodepath->selected) == 0) {
2996         if (grow > 0) {
2997             sp_nodepath_node_select(n, TRUE, TRUE);
2998         }
2999         return;
3000     }
3002     if (g_list_length (nodepath->selected) == 1) {
3003         if (grow < 0) {
3004             sp_nodepath_deselect (nodepath);
3005             return;
3006         }
3007     }
3009         double n_sel_range = 0, p_sel_range = 0;
3010             Inkscape::NodePath::Node *farthest_n_node = n;
3011             Inkscape::NodePath::Node *farthest_p_node = n;
3013         // Calculate ranges
3014         {
3015             double n_range = 0, p_range = 0;
3016             bool n_going = true, p_going = true;
3017             Inkscape::NodePath::Node *n_node = n;
3018             Inkscape::NodePath::Node *p_node = n;
3019             do {
3020                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3021                 if (n_node && n_going)
3022                     n_node = n_node->n.other;
3023                 if (n_node == NULL) {
3024                     n_going = false;
3025                 } else {
3026                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3027                     if (n_node->selected) {
3028                         n_sel_range = n_range;
3029                         farthest_n_node = n_node;
3030                     }
3031                     if (n_node == p_node) {
3032                         n_going = false;
3033                         p_going = false;
3034                     }
3035                 }
3036                 if (p_node && p_going)
3037                     p_node = p_node->p.other;
3038                 if (p_node == NULL) {
3039                     p_going = false;
3040                 } else {
3041                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3042                     if (p_node->selected) {
3043                         p_sel_range = p_range;
3044                         farthest_p_node = p_node;
3045                     }
3046                     if (p_node == n_node) {
3047                         n_going = false;
3048                         p_going = false;
3049                     }
3050                 }
3051             } while (n_going || p_going);
3052         }
3054     if (grow > 0) {
3055         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3056                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3057         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3058                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3059         }
3060     } else {
3061         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3062                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3063         } else if (farthest_p_node && farthest_p_node->selected) {
3064                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3065         }
3066     }
3069 void
3070 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3072     g_assert (n);
3073     g_assert (nodepath);
3074     g_assert (n->subpath->nodepath == nodepath);
3076     if (g_list_length (nodepath->selected) == 0) {
3077         if (grow > 0) {
3078             sp_nodepath_node_select(n, TRUE, TRUE);
3079         }
3080         return;
3081     }
3083     if (g_list_length (nodepath->selected) == 1) {
3084         if (grow < 0) {
3085             sp_nodepath_deselect (nodepath);
3086             return;
3087         }
3088     }
3090     Inkscape::NodePath::Node *farthest_selected = NULL;
3091     double farthest_dist = 0;
3093     Inkscape::NodePath::Node *closest_unselected = NULL;
3094     double closest_dist = NR_HUGE;
3096     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3097        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3098         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3099            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3100            if (node == n)
3101                continue;
3102            if (node->selected) {
3103                if (NR::L2(node->pos - n->pos) > farthest_dist) {
3104                    farthest_dist = NR::L2(node->pos - n->pos);
3105                    farthest_selected = node;
3106                }
3107            } else {
3108                if (NR::L2(node->pos - n->pos) < closest_dist) {
3109                    closest_dist = NR::L2(node->pos - n->pos);
3110                    closest_unselected = node;
3111                }
3112            }
3113         }
3114     }
3116     if (grow > 0) {
3117         if (closest_unselected) {
3118             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3119         }
3120     } else {
3121         if (farthest_selected) {
3122             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3123         }
3124     }
3128 /**
3129 \brief  Saves all nodes' and handles' current positions in their origin members
3130 */
3131 void
3132 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3134     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3135        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3136         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3137            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3138            n->origin = n->pos;
3139            n->p.origin = n->p.pos;
3140            n->n.origin = n->n.pos;
3141         }
3142     }
3145 /**
3146 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3147 */
3148 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3150     if (!nodepath->selected) {
3151         return NULL;
3152     }
3154     GList *r = NULL;
3155     guint i = 0;
3156     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3157        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3158         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3159            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3160             i++;
3161             if (node->selected) {
3162                 r = g_list_append(r, GINT_TO_POINTER(i));
3163             }
3164         }
3165     }
3166     return r;
3169 /**
3170 \brief  Restores selection by selecting nodes whose positions are in the list
3171 */
3172 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3174     sp_nodepath_deselect(nodepath);
3176     guint i = 0;
3177     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3178        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3179         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3180            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3181             i++;
3182             if (g_list_find(r, GINT_TO_POINTER(i))) {
3183                 sp_nodepath_node_select(node, TRUE, TRUE);
3184             }
3185         }
3186     }
3190 /**
3191 \brief Adjusts handle according to node type and line code.
3192 */
3193 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3195     g_assert(node);
3197    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3198    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3200    // nothing to do if we are an end node
3201     if (me->other == NULL) return;
3202     if (other->other == NULL) return;
3204     // nothing to do if we are a cusp node
3205     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3207     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3208     NRPathcode mecode;
3209     if (which_adjust == 1) {
3210         mecode = (NRPathcode)me->other->code;
3211     } else {
3212         mecode = (NRPathcode)node->code;
3213     }
3214     if (mecode == NR_LINETO) return;
3216     if (sp_node_side_is_line(node, other)) {
3217         // other is a line, and we are either smooth or symm
3218        Inkscape::NodePath::Node *othernode = other->other;
3219         double len = NR::L2(me->pos - node->pos);
3220         NR::Point delta = node->pos - othernode->pos;
3221         double linelen = NR::L2(delta);
3222         if (linelen < 1e-18)
3223             return;
3224         me->pos = node->pos + (len / linelen)*delta;
3225         return;
3226     }
3228     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3229         // symmetrize 
3230         me->pos = 2 * node->pos - other->pos;
3231         return;
3232     } else {
3233         // smoothify
3234         double len = NR::L2(me->pos - node->pos);
3235         NR::Point delta = other->pos - node->pos;
3236         double otherlen = NR::L2(delta);
3237         if (otherlen < 1e-18) return;
3238         me->pos = node->pos - (len / otherlen) * delta;
3239     }
3242 /**
3243  \brief Adjusts both handles according to node type and line code
3244  */
3245 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3247     g_assert(node);
3249     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3251     /* we are either smooth or symm */
3253     if (node->p.other == NULL) return;
3254     if (node->n.other == NULL) return;
3256     if (sp_node_side_is_line(node, &node->p)) {
3257         sp_node_adjust_handle(node, 1);
3258         return;
3259     }
3261     if (sp_node_side_is_line(node, &node->n)) {
3262         sp_node_adjust_handle(node, -1);
3263         return;
3264     }
3266     /* both are curves */
3267     NR::Point const delta( node->n.pos - node->p.pos );
3269     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3270         node->p.pos = node->pos - delta / 2;
3271         node->n.pos = node->pos + delta / 2;
3272         return;
3273     }
3275     /* We are smooth */
3276     double plen = NR::L2(node->p.pos - node->pos);
3277     if (plen < 1e-18) return;
3278     double nlen = NR::L2(node->n.pos - node->pos);
3279     if (nlen < 1e-18) return;
3280     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3281     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3284 /**
3285  * Node event callback.
3286  */
3287 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3289     gboolean ret = FALSE;
3290     switch (event->type) {
3291         case GDK_ENTER_NOTIFY:
3292             Inkscape::NodePath::Path::active_node = n;
3293             break;
3294         case GDK_LEAVE_NOTIFY:
3295             Inkscape::NodePath::Path::active_node = NULL;
3296             break;
3297         case GDK_SCROLL:
3298             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3299                 switch (event->scroll.direction) {
3300                     case GDK_SCROLL_UP:
3301                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3302                         break;
3303                     case GDK_SCROLL_DOWN:
3304                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3305                         break;
3306                     default:
3307                         break;
3308                 }
3309                 ret = TRUE;
3310             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3311                 switch (event->scroll.direction) {
3312                     case GDK_SCROLL_UP:
3313                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3314                         break;
3315                     case GDK_SCROLL_DOWN:
3316                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3317                         break;
3318                     default:
3319                         break;
3320                 }
3321                 ret = TRUE;
3322             }
3323             break;
3324         case GDK_KEY_PRESS:
3325             switch (get_group0_keyval (&event->key)) {
3326                 case GDK_space:
3327                     if (event->key.state & GDK_BUTTON1_MASK) {
3328                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3329                         stamp_repr(nodepath);
3330                         ret = TRUE;
3331                     }
3332                     break;
3333                 case GDK_Page_Up:
3334                     if (event->key.state & GDK_CONTROL_MASK) {
3335                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3336                     } else {
3337                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3338                     }
3339                     break;
3340                 case GDK_Page_Down:
3341                     if (event->key.state & GDK_CONTROL_MASK) {
3342                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3343                     } else {
3344                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3345                     }
3346                     break;
3347                 default:
3348                     break;
3349             }
3350             break;
3351         default:
3352             break;
3353     }
3355     return ret;
3358 /**
3359  * Handle keypress on node; directly called.
3360  */
3361 gboolean node_key(GdkEvent *event)
3363     Inkscape::NodePath::Path *np;
3365     // there is no way to verify nodes so set active_node to nil when deleting!!
3366     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3368     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3369         gint ret = FALSE;
3370         switch (get_group0_keyval (&event->key)) {
3371             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3372             case GDK_BackSpace:
3373                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3374                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3375                 sp_nodepath_update_repr(np, _("Delete node"));
3376                 Inkscape::NodePath::Path::active_node = NULL;
3377                 ret = TRUE;
3378                 break;
3379             case GDK_c:
3380                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3381                 ret = TRUE;
3382                 break;
3383             case GDK_s:
3384                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3385                 ret = TRUE;
3386                 break;
3387             case GDK_y:
3388                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3389                 ret = TRUE;
3390                 break;
3391             case GDK_b:
3392                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3393                 ret = TRUE;
3394                 break;
3395         }
3396         return ret;
3397     }
3398     return FALSE;
3401 /**
3402  * Mouseclick on node callback.
3403  */
3404 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3406    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3408     if (state & GDK_CONTROL_MASK) {
3409         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3411         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3412             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3413                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3414             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3415                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3416             } else {
3417                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3418             }
3419             sp_nodepath_update_repr(nodepath, _("Change node type"));
3420             sp_nodepath_update_statusbar(nodepath);
3422         } else { //ctrl+alt+click: delete node
3423             GList *node_to_delete = NULL;
3424             node_to_delete = g_list_append(node_to_delete, n);
3425             sp_node_delete_preserve(node_to_delete);
3426         }
3428     } else {
3429         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3430     }
3433 /**
3434  * Mouse grabbed node callback.
3435  */
3436 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3438    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3440     if (!n->selected) {
3441         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3442     }
3444     n->is_dragging = true;
3445     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3447     sp_nodepath_remember_origins (n->subpath->nodepath);
3450 /**
3451  * Mouse ungrabbed node callback.
3452  */
3453 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3455    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3457    n->dragging_out = NULL;
3458    n->is_dragging = false;
3459    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3461    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3464 /**
3465  * The point on a line, given by its angle, closest to the given point.
3466  * \param p  A point.
3467  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3468  * \param closest  Pointer to the point struct where the result is stored.
3469  * \todo FIXME: use dot product perhaps?
3470  */
3471 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3473     if (a == HUGE_VAL) { // vertical
3474         *closest = NR::Point(0, (*p)[NR::Y]);
3475     } else {
3476         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3477         (*closest)[NR::Y] = a * (*closest)[NR::X];
3478     }
3481 /**
3482  * Distance from the point to a line given by its angle.
3483  * \param p  A point.
3484  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3485  */
3486 static double point_line_distance(NR::Point *p, double a)
3488     NR::Point c;
3489     point_line_closest(p, a, &c);
3490     return sqrt(((*p)[NR::X] - c[NR::X])*((*p)[NR::X] - c[NR::X]) + ((*p)[NR::Y] - c[NR::Y])*((*p)[NR::Y] - c[NR::Y]));
3493 /**
3494  * Callback for node "request" signal.
3495  * \todo fixme: This goes to "moved" event? (lauris)
3496  */
3497 static gboolean
3498 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3500     double yn, xn, yp, xp;
3501     double an, ap, na, pa;
3502     double d_an, d_ap, d_na, d_pa;
3503     gboolean collinear = FALSE;
3504     NR::Point c;
3505     NR::Point pr;
3507     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3509     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3511     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3512     if ( (!n->subpath->nodepath->straight_path) &&
3513          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3514            || n->dragging_out ) )
3515     {
3516        NR::Point mouse = (*p);
3518        if (!n->dragging_out) {
3519            // This is the first drag-out event; find out which handle to drag out
3520            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3521            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3523            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3524                return FALSE;
3526            Inkscape::NodePath::NodeSide *opposite;
3527            if (appr_p > appr_n) { // closer to p
3528                n->dragging_out = &n->p;
3529                opposite = &n->n;
3530                n->code = NR_CURVETO;
3531            } else if (appr_p < appr_n) { // closer to n
3532                n->dragging_out = &n->n;
3533                opposite = &n->p;
3534                n->n.other->code = NR_CURVETO;
3535            } else { // p and n nodes are the same
3536                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3537                    n->dragging_out = &n->p;
3538                    opposite = &n->n;
3539                    n->code = NR_CURVETO;
3540                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3541                    n->dragging_out = &n->n;
3542                    opposite = &n->p;
3543                    n->n.other->code = NR_CURVETO;
3544                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3545                    double appr_other_n = (n->n.other ? NR::L2(n->n.other->n.pos - n->pos) - NR::L2(n->n.other->n.pos - (*p)) : -HUGE_VAL);
3546                    double appr_other_p = (n->n.other ? NR::L2(n->n.other->p.pos - n->pos) - NR::L2(n->n.other->p.pos - (*p)) : -HUGE_VAL);
3547                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3548                        n->dragging_out = &n->n;
3549                        opposite = &n->p;
3550                        n->n.other->code = NR_CURVETO;
3551                    } else { // closer to other's n handle
3552                        n->dragging_out = &n->p;
3553                        opposite = &n->n;
3554                        n->code = NR_CURVETO;
3555                    }
3556                }
3557            }
3559            // if there's another handle, make sure the one we drag out starts parallel to it
3560            if (opposite->pos != n->pos) {
3561                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3562            }
3564            // knots might not be created yet!
3565            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3566            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3567        }
3569        // pass this on to the handle-moved callback
3570        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3571        sp_node_update_handles(n);
3572        return TRUE;
3573    }
3575     if (state & GDK_CONTROL_MASK) { // constrained motion
3577         // calculate relative distances of handles
3578         // n handle:
3579         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3580         xn = n->n.pos[NR::X] - n->pos[NR::X];
3581         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3582         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3583             if (n->n.other) { // if there is the next point
3584                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3585                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3586                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3587             }
3588         }
3589         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3590         if (yn < 0) { xn = -xn; yn = -yn; }
3592         // p handle:
3593         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3594         xp = n->p.pos[NR::X] - n->pos[NR::X];
3595         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3596         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3597             if (n->p.other) {
3598                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3599                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3600                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3601             }
3602         }
3603         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3604         if (yp < 0) { xp = -xp; yp = -yp; }
3606         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3607             // sliding on handles, only if at least one of the handles is non-vertical
3608             // (otherwise it's the same as ctrl+drag anyway)
3610             // calculate angles of the handles
3611             if (xn == 0) {
3612                 if (yn == 0) { // no handle, consider it the continuation of the other one
3613                     an = 0;
3614                     collinear = TRUE;
3615                 }
3616                 else an = 0; // vertical; set the angle to horizontal
3617             } else an = yn/xn;
3619             if (xp == 0) {
3620                 if (yp == 0) { // no handle, consider it the continuation of the other one
3621                     ap = an;
3622                 }
3623                 else ap = 0; // vertical; set the angle to horizontal
3624             } else  ap = yp/xp;
3626             if (collinear) an = ap;
3628             // angles of the perpendiculars; HUGE_VAL means vertical
3629             if (an == 0) na = HUGE_VAL; else na = -1/an;
3630             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3632             // mouse point relative to the node's original pos
3633             pr = (*p) - n->origin;
3635             // distances to the four lines (two handles and two perpendiculars)
3636             d_an = point_line_distance(&pr, an);
3637             d_na = point_line_distance(&pr, na);
3638             d_ap = point_line_distance(&pr, ap);
3639             d_pa = point_line_distance(&pr, pa);
3641             // find out which line is the closest, save its closest point in c
3642             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3643                 point_line_closest(&pr, an, &c);
3644             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3645                 point_line_closest(&pr, ap, &c);
3646             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3647                 point_line_closest(&pr, na, &c);
3648             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3649                 point_line_closest(&pr, pa, &c);
3650             }
3652             // move the node to the closest point
3653             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3654                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3655                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y], 
3656                                             true);
3658         } else {  // constraining to hor/vert
3660             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3661                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3662                                                 (*p)[NR::X] - n->pos[NR::X], 
3663                                                 n->origin[NR::Y] - n->pos[NR::Y],
3664                                                 true, 
3665                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::X]));
3666             } else { // snap to vert
3667                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3668                                                 n->origin[NR::X] - n->pos[NR::X],
3669                                                 (*p)[NR::Y] - n->pos[NR::Y],
3670                                                 true,
3671                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::Y]));
3672             }
3673         }
3674     } else { // move freely
3675         if (n->is_dragging) {
3676             if (state & GDK_MOD1_MASK) { // sculpt
3677                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3678             } else {
3679                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3680                                             (*p)[NR::X] - n->pos[NR::X],
3681                                             (*p)[NR::Y] - n->pos[NR::Y],
3682                                             (state & GDK_SHIFT_MASK) == 0);
3683             }
3684         }
3685     }
3687     n->subpath->nodepath->desktop->scroll_to_point(p);
3689     return TRUE;
3692 /**
3693  * Node handle clicked callback.
3694  */
3695 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3697    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3699     if (state & GDK_CONTROL_MASK) { // "delete" handle
3700         if (n->p.knot == knot) {
3701             n->p.pos = n->pos;
3702         } else if (n->n.knot == knot) {
3703             n->n.pos = n->pos;
3704         }
3705         sp_node_update_handles(n);
3706         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3707         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3708         sp_nodepath_update_statusbar(nodepath);
3710     } else { // just select or add to selection, depending in Shift
3711         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3712     }
3715 /**
3716  * Node handle grabbed callback.
3717  */
3718 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3720    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3722     if (!n->selected) {
3723         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3724     }
3726     // remember the origin point of the handle
3727     if (n->p.knot == knot) {
3728         n->p.origin_radial = n->p.pos - n->pos;
3729     } else if (n->n.knot == knot) {
3730         n->n.origin_radial = n->n.pos - n->pos;
3731     } else {
3732         g_assert_not_reached();
3733     }
3735     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3738 /**
3739  * Node handle ungrabbed callback.
3740  */
3741 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3743    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3745     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3746     if (n->p.knot == knot) {
3747         n->p.origin_radial.a = 0;
3748         sp_knot_set_position(knot, n->p.pos, state);
3749     } else if (n->n.knot == knot) {
3750         n->n.origin_radial.a = 0;
3751         sp_knot_set_position(knot, n->n.pos, state);
3752     } else {
3753         g_assert_not_reached();
3754     }
3756     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3759 /**
3760  * Node handle "request" signal callback.
3761  */
3762 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3764     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3766     Inkscape::NodePath::NodeSide *me, *opposite;
3767     gint which;
3768     if (n->p.knot == knot) {
3769         me = &n->p;
3770         opposite = &n->n;
3771         which = -1;
3772     } else if (n->n.knot == knot) {
3773         me = &n->n;
3774         opposite = &n->p;
3775         which = 1;
3776     } else {
3777         me = opposite = NULL;
3778         which = 0;
3779         g_assert_not_reached();
3780     }
3782     SPDesktop *desktop = n->subpath->nodepath->desktop;
3783     SnapManager &m = desktop->namedview->snap_manager;
3784     m.setup(desktop, n->subpath->nodepath->item);
3785     Inkscape::SnappedPoint s;
3786     
3787     if ((state & GDK_SHIFT_MASK) != 0) {
3788         // We will not try to snap when the shift-key is pressed
3789         // so remove the old snap indicator and don't wait for it to time-out  
3790         desktop->snapindicator->remove_snappoint();     
3791     }
3793     Inkscape::NodePath::Node *othernode = opposite->other;
3794     if (othernode) {
3795         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3796             /* We are smooth node adjacent with line */
3797             NR::Point const delta = *p - n->pos;
3798             NR::Coord const len = NR::L2(delta);
3799             Inkscape::NodePath::Node *othernode = opposite->other;
3800             NR::Point const ndelta = n->pos - othernode->pos;
3801             NR::Coord const linelen = NR::L2(ndelta);
3802             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3803                 NR::Coord const scal = dot(delta, ndelta) / linelen;
3804                 (*p) = n->pos + (scal / linelen) * ndelta;
3805             }
3806             if ((state & GDK_SHIFT_MASK) == 0) {
3807                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(*p), Inkscape::Snapper::ConstraintLine(*p, ndelta));
3808             }
3809         } else {
3810                 if ((state & GDK_SHIFT_MASK) == 0) {
3811                         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(*p));
3812                 }
3813         }
3814     } else {
3815         if ((state & GDK_SHIFT_MASK) == 0) {
3816                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(*p));
3817         }
3818     }
3819     
3820     Geom::Point pt2g = *p;
3821     s.getPoint(pt2g);
3822     *p = pt2g;
3823     
3824     sp_node_adjust_handle(n, -which);
3826     return FALSE;
3829 /**
3830  * Node handle moved callback.
3831  */
3832 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3834    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3836    Inkscape::NodePath::NodeSide *me;
3837    Inkscape::NodePath::NodeSide *other;
3838     if (n->p.knot == knot) {
3839         me = &n->p;
3840         other = &n->n;
3841     } else if (n->n.knot == knot) {
3842         me = &n->n;
3843         other = &n->p;
3844     } else {
3845         me = NULL;
3846         other = NULL;
3847         g_assert_not_reached();
3848     }
3850     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3851     Radial rme(me->pos - n->pos);
3852     Radial rother(other->pos - n->pos);
3853     Radial rnew(*p - n->pos);
3855     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3856         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3857         /* 0 interpreted as "no snapping". */
3859         // 1. Snap to the closest PI/snaps angle, starting from zero.
3860         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3862         // 2. Snap to the original angle, its opposite and perpendiculars
3863         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3864             /* The closest PI/2 angle, starting from original angle */
3865             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3867             // Snap to the closest.
3868             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3869                        ? a_snapped
3870                        : a_ortho );
3871         }
3873         // 3. Snap to the angle of the opposite line, if any
3874         Inkscape::NodePath::Node *othernode = other->other;
3875         if (othernode) {
3876             NR::Point other_to_snap(0,0);
3877             if (sp_node_side_is_line(n, other)) {
3878                 other_to_snap = othernode->pos - n->pos;
3879             } else {
3880                 other_to_snap = other->pos - n->pos;
3881             }
3882             if (NR::L2(other_to_snap) > 1e-3) {
3883                 Radial rother_to_snap(other_to_snap);
3884                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
3885                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
3887                 // Snap to the closest.
3888                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
3889                        ? a_snapped
3890                        : a_oppo );
3891             }
3892         }
3894         rnew.a = a_snapped;
3895     }
3897     if (state & GDK_MOD1_MASK) {
3898         // lock handle length
3899         rnew.r = me->origin_radial.r;
3900     }
3902     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3903         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3904         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3905         rother.a += rnew.a - rme.a;
3906         other->pos = NR::Point(rother) + n->pos;
3907         if (other->knot) {
3908             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3909             sp_knot_moveto(other->knot, other->pos);
3910         }
3911     }
3913     me->pos = NR::Point(rnew) + n->pos;
3914     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3916     // move knot, but without emitting the signal:
3917     // we cannot emit a "moved" signal because we're now processing it
3918     sp_knot_moveto(me->knot, me->pos);
3920     update_object(n->subpath->nodepath);
3922     /* status text */
3923     SPDesktop *desktop = n->subpath->nodepath->desktop;
3924     if (!desktop) return;
3925     SPEventContext *ec = desktop->event_context;
3926     if (!ec) return;
3927     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3928     if (!mc) return;
3930     double degrees = 180 / M_PI * rnew.a;
3931     if (degrees > 180) degrees -= 360;
3932     if (degrees < -180) degrees += 360;
3933     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3934         degrees = angle_to_compass (degrees);
3936     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3938     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
3939          _("<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);
3941     g_string_free(length, TRUE);
3944 /**
3945  * Node handle event callback.
3946  */
3947 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3949     gboolean ret = FALSE;
3950     switch (event->type) {
3951         case GDK_KEY_PRESS:
3952             switch (get_group0_keyval (&event->key)) {
3953                 case GDK_space:
3954                     if (event->key.state & GDK_BUTTON1_MASK) {
3955                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3956                         stamp_repr(nodepath);
3957                         ret = TRUE;
3958                     }
3959                     break;
3960                 default:
3961                     break;
3962             }
3963             break;
3964         case GDK_ENTER_NOTIFY:
3965             // we use an experimentally determined threshold that seems to work fine
3966             if (NR::L2(n->pos - knot->pos) < 0.75)
3967                 Inkscape::NodePath::Path::active_node = n;
3968             break;
3969         case GDK_LEAVE_NOTIFY:
3970             // we use an experimentally determined threshold that seems to work fine
3971             if (NR::L2(n->pos - knot->pos) < 0.75)
3972                 Inkscape::NodePath::Path::active_node = NULL;
3973             break;
3974         default:
3975             break;
3976     }
3978     return ret;
3981 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3982                                  Radial &rme, Radial &rother, gboolean const both)
3984     rme.a += angle;
3985     if ( both
3986          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3987          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3988     {
3989         rother.a += angle;
3990     }
3993 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3994                                         Radial &rme, Radial &rother, gboolean const both)
3996     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3998     gdouble r;
3999     if ( both
4000          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4001          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4002     {
4003         r = MAX(rme.r, rother.r);
4004     } else {
4005         r = rme.r;
4006     }
4008     gdouble const weird_angle = atan2(norm_angle, r);
4009 /* Bulia says norm_angle is just the visible distance that the
4010  * object's end must travel on the screen.  Left as 'angle' for want of
4011  * a better name.*/
4013     rme.a += weird_angle;
4014     if ( both
4015          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4016          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4017     {
4018         rother.a += weird_angle;
4019     }
4022 /**
4023  * Rotate one node.
4024  */
4025 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4027     Inkscape::NodePath::NodeSide *me, *other;
4028     bool both = false;
4030     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4031     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4033     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4034         me = &(n->p);
4035         other = &(n->n);
4036     } else if (!n->p.other) {
4037         me = &(n->n);
4038         other = &(n->p);
4039     } else {
4040         if (which > 0) { // right handle
4041             if (xn > xp) {
4042                 me = &(n->n);
4043                 other = &(n->p);
4044             } else {
4045                 me = &(n->p);
4046                 other = &(n->n);
4047             }
4048         } else if (which < 0){ // left handle
4049             if (xn <= xp) {
4050                 me = &(n->n);
4051                 other = &(n->p);
4052             } else {
4053                 me = &(n->p);
4054                 other = &(n->n);
4055             }
4056         } else { // both handles
4057             me = &(n->n);
4058             other = &(n->p);
4059             both = true;
4060         }
4061     }
4063     Radial rme(me->pos - n->pos);
4064     Radial rother(other->pos - n->pos);
4066     if (screen) {
4067         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4068     } else {
4069         node_rotate_one_internal (*n, angle, rme, rother, both);
4070     }
4072     me->pos = n->pos + NR::Point(rme);
4074     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4075         other->pos =  n->pos + NR::Point(rother);
4076     }
4078     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4079     // so here we just move all the knots without emitting move signals, for speed
4080     sp_node_update_handles(n, false);
4083 /**
4084  * Rotate selected nodes.
4085  */
4086 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4088     if (!nodepath || !nodepath->selected) return;
4090     if (g_list_length(nodepath->selected) == 1) {
4091        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4092         node_rotate_one (n, angle, which, screen);
4093     } else {
4094        // rotate as an object:
4096         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4097         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4098         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4099             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4100             box.expandTo (n->pos); // contain all selected nodes
4101         }
4103         gdouble rot;
4104         if (screen) {
4105             gdouble const zoom = nodepath->desktop->current_zoom();
4106             gdouble const zmove = angle / zoom;
4107             gdouble const r = NR::L2(box.max() - box.midpoint());
4108             rot = atan2(zmove, r);
4109         } else {
4110             rot = angle;
4111         }
4113         NR::Point rot_center;
4114         if (Inkscape::NodePath::Path::active_node == NULL)
4115             rot_center = box.midpoint();
4116         else
4117             rot_center = Inkscape::NodePath::Path::active_node->pos;
4119         NR::Matrix t =
4120             NR::Matrix (NR::translate(-rot_center)) *
4121             NR::Matrix (NR::rotate(rot)) *
4122             NR::Matrix (NR::translate(rot_center));
4124         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4125             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4126             n->pos *= t;
4127             n->n.pos *= t;
4128             n->p.pos *= t;
4129             sp_node_update_handles(n, false);
4130         }
4131     }
4133     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4136 /**
4137  * Scale one node.
4138  */
4139 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4141     bool both = false;
4142     Inkscape::NodePath::NodeSide *me, *other;
4144     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4145     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4147     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4148         me = &(n->p);
4149         other = &(n->n);
4150         n->code = NR_CURVETO;
4151     } else if (!n->p.other) {
4152         me = &(n->n);
4153         other = &(n->p);
4154         if (n->n.other)
4155             n->n.other->code = NR_CURVETO;
4156     } else {
4157         if (which > 0) { // right handle
4158             if (xn > xp) {
4159                 me = &(n->n);
4160                 other = &(n->p);
4161                 if (n->n.other)
4162                     n->n.other->code = NR_CURVETO;
4163             } else {
4164                 me = &(n->p);
4165                 other = &(n->n);
4166                 n->code = NR_CURVETO;
4167             }
4168         } else if (which < 0){ // left handle
4169             if (xn <= xp) {
4170                 me = &(n->n);
4171                 other = &(n->p);
4172                 if (n->n.other)
4173                     n->n.other->code = NR_CURVETO;
4174             } else {
4175                 me = &(n->p);
4176                 other = &(n->n);
4177                 n->code = NR_CURVETO;
4178             }
4179         } else { // both handles
4180             me = &(n->n);
4181             other = &(n->p);
4182             both = true;
4183             n->code = NR_CURVETO;
4184             if (n->n.other)
4185                 n->n.other->code = NR_CURVETO;
4186         }
4187     }
4189     Radial rme(me->pos - n->pos);
4190     Radial rother(other->pos - n->pos);
4192     rme.r += grow;
4193     if (rme.r < 0) rme.r = 0;
4194     if (rme.a == HUGE_VAL) {
4195         if (me->other) { // if direction is unknown, initialize it towards the next node
4196             Radial rme_next(me->other->pos - n->pos);
4197             rme.a = rme_next.a;
4198         } else { // if there's no next, initialize to 0
4199             rme.a = 0;
4200         }
4201     }
4202     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4203         rother.r += grow;
4204         if (rother.r < 0) rother.r = 0;
4205         if (rother.a == HUGE_VAL) {
4206             rother.a = rme.a + M_PI;
4207         }
4208     }
4210     me->pos = n->pos + NR::Point(rme);
4212     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4213         other->pos = n->pos + NR::Point(rother);
4214     }
4216     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4217     // so here we just move all the knots without emitting move signals, for speed
4218     sp_node_update_handles(n, false);
4221 /**
4222  * Scale selected nodes.
4223  */
4224 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4226     if (!nodepath || !nodepath->selected) return;
4228     if (g_list_length(nodepath->selected) == 1) {
4229         // scale handles of the single selected node
4230         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4231         node_scale_one (n, grow, which);
4232     } else {
4233         // scale nodes as an "object":
4235         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4236         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4237         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4238             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4239             box.expandTo (n->pos); // contain all selected nodes
4240         }
4242         double scale = (box.maxExtent() + grow)/box.maxExtent();
4244         NR::Point scale_center;
4245         if (Inkscape::NodePath::Path::active_node == NULL)
4246             scale_center = box.midpoint();
4247         else
4248             scale_center = Inkscape::NodePath::Path::active_node->pos;
4250         NR::Matrix t =
4251             NR::Matrix (NR::translate(-scale_center)) *
4252             NR::Matrix (NR::scale(scale, scale)) *
4253             NR::Matrix (NR::translate(scale_center));
4255         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4256             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4257             n->pos *= t;
4258             n->n.pos *= t;
4259             n->p.pos *= t;
4260             sp_node_update_handles(n, false);
4261         }
4262     }
4264     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4267 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4269     if (!nodepath) return;
4270     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4273 /**
4274  * Flip selected nodes horizontally/vertically.
4275  */
4276 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, boost::optional<NR::Point> center)
4278     if (!nodepath || !nodepath->selected) return;
4280     if (g_list_length(nodepath->selected) == 1 && !center) {
4281         // flip handles of the single selected node
4282         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4283         double temp = n->p.pos[axis];
4284         n->p.pos[axis] = n->n.pos[axis];
4285         n->n.pos[axis] = temp;
4286         sp_node_update_handles(n, false);
4287     } else {
4288         // scale nodes as an "object":
4290         Geom::Rect box = sp_node_selected_bbox (nodepath);
4291         if (!center) {
4292             center = box.midpoint();
4293         }
4294         NR::Matrix t =
4295             NR::Matrix (NR::translate(- *center)) *
4296             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
4297             NR::Matrix (NR::translate(*center));
4299         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4300             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4301             n->pos *= t;
4302             n->n.pos *= t;
4303             n->p.pos *= t;
4304             sp_node_update_handles(n, false);
4305         }
4306     }
4308     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4311 Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4313     g_assert (nodepath->selected);
4315     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4316     Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4317     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4318         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4319         box.expandTo (n->pos); // contain all selected nodes
4320     }
4321     return box;
4324 //-----------------------------------------------
4325 /**
4326  * Return new subpath under given nodepath.
4327  */
4328 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4330     g_assert(nodepath);
4331     g_assert(nodepath->desktop);
4333    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4335     s->nodepath = nodepath;
4336     s->closed = FALSE;
4337     s->nodes = NULL;
4338     s->first = NULL;
4339     s->last = NULL;
4341     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4342     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4343     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4345     return s;
4348 /**
4349  * Destroy nodes in subpath, then subpath itself.
4350  */
4351 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4353     g_assert(subpath);
4354     g_assert(subpath->nodepath);
4355     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4357     while (subpath->nodes) {
4358         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4359     }
4361     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4363     g_free(subpath);
4366 /**
4367  * Link head to tail in subpath.
4368  */
4369 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4371     g_assert(!sp->closed);
4372     g_assert(sp->last != sp->first);
4373     g_assert(sp->first->code == NR_MOVETO);
4375     sp->closed = TRUE;
4377     //Link the head to the tail
4378     sp->first->p.other = sp->last;
4379     sp->last->n.other  = sp->first;
4380     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4381     sp->first          = sp->last;
4383     //Remove the extra end node
4384     sp_nodepath_node_destroy(sp->last->n.other);
4387 /**
4388  * Open closed (loopy) subpath at node.
4389  */
4390 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4392     g_assert(sp->closed);
4393     g_assert(n->subpath == sp);
4394     g_assert(sp->first == sp->last);
4396     /* We create new startpoint, current node will become last one */
4398    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4399                                                 &n->pos, &n->pos, &n->n.pos);
4402     sp->closed        = FALSE;
4404     //Unlink to make a head and tail
4405     sp->first         = new_path;
4406     sp->last          = n;
4407     n->n.other        = NULL;
4408     new_path->p.other = NULL;
4411 /**
4412  * Return new node in subpath with given properties.
4413  * \param pos Position of node.
4414  * \param ppos Handle position in previous direction
4415  * \param npos Handle position in previous direction
4416  */
4417 Inkscape::NodePath::Node *
4418 sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp, Inkscape::NodePath::Node *next, Inkscape::NodePath::NodeType type, NRPathcode code, NR::Point *ppos, NR::Point *pos, NR::Point *npos)
4420     g_assert(sp);
4421     g_assert(sp->nodepath);
4422     g_assert(sp->nodepath->desktop);
4424     if (nodechunk == NULL)
4425         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4427     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4429     n->subpath  = sp;
4431     if (type != Inkscape::NodePath::NODE_NONE) {
4432         // use the type from sodipodi:nodetypes
4433         n->type = type;
4434     } else {
4435         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4436             // points are (almost) collinear
4437             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4438                 // endnode, or a node with a retracted handle
4439                 n->type = Inkscape::NodePath::NODE_CUSP;
4440             } else {
4441                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4442             }
4443         } else {
4444             n->type = Inkscape::NodePath::NODE_CUSP;
4445         }
4446     }
4448     n->code     = code;
4449     n->selected = FALSE;
4450     n->pos      = *pos;
4451     n->p.pos    = *ppos;
4452     n->n.pos    = *npos;
4454     n->dragging_out = NULL;
4456     Inkscape::NodePath::Node *prev;
4457     if (next) {
4458         //g_assert(g_list_find(sp->nodes, next));
4459         prev = next->p.other;
4460     } else {
4461         prev = sp->last;
4462     }
4464     if (prev)
4465         prev->n.other = n;
4466     else
4467         sp->first = n;
4469     if (next)
4470         next->p.other = n;
4471     else
4472         sp->last = n;
4474     n->p.other = prev;
4475     n->n.other = next;
4477     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"));
4478     sp_knot_set_position(n->knot, *pos, 0);
4480     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4481     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4482     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4483     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4484     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4485     sp_knot_update_ctrl(n->knot);
4487     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4488     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4489     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4490     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4491     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4492     sp_knot_show(n->knot);
4494     // We only create handle knots and lines on demand
4495     n->p.knot = NULL;
4496     n->p.line = NULL;
4497     n->n.knot = NULL;
4498     n->n.line = NULL;
4500     sp->nodes = g_list_prepend(sp->nodes, n);
4502     return n;
4505 /**
4506  * Destroy node and its knots, link neighbors in subpath.
4507  */
4508 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4510     g_assert(node);
4511     g_assert(node->subpath);
4512     g_assert(SP_IS_KNOT(node->knot));
4514    Inkscape::NodePath::SubPath *sp = node->subpath;
4516     if (node->selected) { // first, deselect
4517         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4518         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4519     }
4521     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4523     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4524     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4525     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4526     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4527     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4528     g_object_unref(G_OBJECT(node->knot));
4530     if (node->p.knot) {
4531         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4532         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4533         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4534         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4535         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4536         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4537         g_object_unref(G_OBJECT(node->p.knot));
4538         node->p.knot = NULL;
4539     }
4541     if (node->n.knot) {
4542         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4543         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4544         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4545         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4546         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4547         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4548         g_object_unref(G_OBJECT(node->n.knot));
4549         node->n.knot = NULL;
4550     }
4552     if (node->p.line)
4553         gtk_object_destroy(GTK_OBJECT(node->p.line));
4554     if (node->n.line)
4555         gtk_object_destroy(GTK_OBJECT(node->n.line));
4557     if (sp->nodes) { // there are others nodes on the subpath
4558         if (sp->closed) {
4559             if (sp->first == node) {
4560                 g_assert(sp->last == node);
4561                 sp->first = node->n.other;
4562                 sp->last = sp->first;
4563             }
4564             node->p.other->n.other = node->n.other;
4565             node->n.other->p.other = node->p.other;
4566         } else {
4567             if (sp->first == node) {
4568                 sp->first = node->n.other;
4569                 sp->first->code = NR_MOVETO;
4570             }
4571             if (sp->last == node) sp->last = node->p.other;
4572             if (node->p.other) node->p.other->n.other = node->n.other;
4573             if (node->n.other) node->n.other->p.other = node->p.other;
4574         }
4575     } else { // this was the last node on subpath
4576         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4577     }
4579     g_mem_chunk_free(nodechunk, node);
4582 /**
4583  * Returns one of the node's two sides.
4584  * \param which Indicates which side.
4585  * \return Pointer to previous node side if which==-1, next if which==1.
4586  */
4587 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4589     g_assert(node);
4591     switch (which) {
4592         case -1:
4593             return &node->p;
4594         case 1:
4595             return &node->n;
4596         default:
4597             break;
4598     }
4600     g_assert_not_reached();
4602     return NULL;
4605 /**
4606  * Return the other side of the node, given one of its sides.
4607  */
4608 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4610     g_assert(node);
4612     if (me == &node->p) return &node->n;
4613     if (me == &node->n) return &node->p;
4615     g_assert_not_reached();
4617     return NULL;
4620 /**
4621  * Return NRPathcode on the given side of the node.
4622  */
4623 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4625     g_assert(node);
4627     if (me == &node->p) {
4628         if (node->p.other) return (NRPathcode)node->code;
4629         return NR_MOVETO;
4630     }
4632     if (me == &node->n) {
4633         if (node->n.other) return (NRPathcode)node->n.other->code;
4634         return NR_MOVETO;
4635     }
4637     g_assert_not_reached();
4639     return NR_END;
4642 /**
4643  * Return node with the given index
4644  */
4645 Inkscape::NodePath::Node *
4646 sp_nodepath_get_node_by_index(int index)
4648     Inkscape::NodePath::Node *e = NULL;
4650     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4651     if (!nodepath) {
4652         return e;
4653     }
4655     //find segment
4656     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4658         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4659         int n = g_list_length(sp->nodes);
4660         if (sp->closed) {
4661             n++;
4662         }
4664         //if the piece belongs to this subpath grab it
4665         //otherwise move onto the next subpath
4666         if (index < n) {
4667             e = sp->first;
4668             for (int i = 0; i < index; ++i) {
4669                 e = e->n.other;
4670             }
4671             break;
4672         } else {
4673             if (sp->closed) {
4674                 index -= (n+1);
4675             } else {
4676                 index -= n;
4677             }
4678         }
4679     }
4681     return e;
4684 /**
4685  * Returns plain text meaning of node type.
4686  */
4687 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4689     unsigned retracted = 0;
4690     bool endnode = false;
4692     for (int which = -1; which <= 1; which += 2) {
4693         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4694         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4695             retracted ++;
4696         if (!side->other)
4697             endnode = true;
4698     }
4700     if (retracted == 0) {
4701         if (endnode) {
4702                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4703                 return _("end node");
4704         } else {
4705             switch (node->type) {
4706                 case Inkscape::NodePath::NODE_CUSP:
4707                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4708                     return _("cusp");
4709                 case Inkscape::NodePath::NODE_SMOOTH:
4710                     // TRANSLATORS: "smooth" is an adjective here
4711                     return _("smooth");
4712                 case Inkscape::NodePath::NODE_SYMM:
4713                     return _("symmetric");
4714             }
4715         }
4716     } else if (retracted == 1) {
4717         if (endnode) {
4718             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4719             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4720         } else {
4721             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4722         }
4723     } else {
4724         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4725     }
4727     return NULL;
4730 /**
4731  * Handles content of statusbar as long as node tool is active.
4732  */
4733 void
4734 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4736     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");
4737     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4739     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4740     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4741     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4742     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4744     SPDesktop *desktop = NULL;
4745     if (nodepath) {
4746         desktop = nodepath->desktop;
4747     } else {
4748         desktop = SP_ACTIVE_DESKTOP;
4749     }
4751     SPEventContext *ec = desktop->event_context;
4752     if (!ec) return;
4753     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4754     if (!mc) return;
4756     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4758     if (selected_nodes == 0) {
4759         Inkscape::Selection *sel = desktop->selection;
4760         if (!sel || sel->isEmpty()) {
4761             mc->setF(Inkscape::NORMAL_MESSAGE,
4762                      _("Select a single object to edit its nodes or handles."));
4763         } else {
4764             if (nodepath) {
4765             mc->setF(Inkscape::NORMAL_MESSAGE,
4766                      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.",
4767                               "<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.",
4768                               total_nodes),
4769                      total_nodes);
4770             } else {
4771                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4772                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4773                 } else {
4774                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4775                 }
4776             }
4777         }
4778     } else if (nodepath && selected_nodes == 1) {
4779         mc->setF(Inkscape::NORMAL_MESSAGE,
4780                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4781                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4782                           total_nodes),
4783                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4784     } else {
4785         if (selected_subpaths > 1) {
4786             mc->setF(Inkscape::NORMAL_MESSAGE,
4787                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4788                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4789                               total_nodes),
4790                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4791         } else {
4792             mc->setF(Inkscape::NORMAL_MESSAGE,
4793                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4794                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4795                               total_nodes),
4796                      selected_nodes, total_nodes, when_selected);
4797         }
4798     }
4801 /*
4802  * returns a *copy* of the curve of that object.
4803  */
4804 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4805     if (!object)
4806         return NULL;
4808     SPCurve *curve = NULL;
4809     if (SP_IS_PATH(object)) {
4810         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4811         curve = curve_new->copy();
4812     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4813         const gchar *svgd = object->repr->attribute(key);
4814         if (svgd) {
4815             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4816             SPCurve *curve_new = new SPCurve(pv);
4817             if (curve_new) {
4818                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4819             }
4820         }
4821     }
4823     return curve;
4826 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4827     if (!np || !np->object || !curve)
4828         return;
4830     if (SP_IS_PATH(np->object)) {
4831         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4832             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4833         } else {
4834             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4835         }
4836     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4837         Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( LIVEPATHEFFECT(np->object)->lpe->getParameter(np->repr_key) );
4838         if (pathparam) {
4839             pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
4840             np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4841         }
4842     }
4845 /**
4846 SPCanvasItem *
4847 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
4848     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
4850 **/
4852 /**
4853 SPCanvasItem *
4854 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) {
4855     SPCurve *flash_curve = curve->copy();
4856     Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity();
4857     flash_curve->transform(i2d);
4858     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4859     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4860     // unless we also flash the nodes...
4861     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4862     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4863     sp_canvas_item_show(canvasitem);
4864     flash_curve->unref();
4865     return canvasitem;
4868 SPCanvasItem *
4869 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) {
4870     return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path),
4871                                            prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff));
4873 **/
4875 SPCanvasItem *
4876 sp_nodepath_helperpath_from_path(SPDesktop *desktop, SPPath *path) {
4877     SPCurve *flash_curve = sp_path_get_curve_for_edit(path)->copy();
4878     Geom::Matrix i2d = sp_item_i2d_affine(SP_ITEM(path));
4879     flash_curve->transform(i2d);
4880     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4881     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4882     // unless we also flash the nodes...
4883     guint32 color = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
4884     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4885     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4886     sp_canvas_item_show(canvasitem);
4887     flash_curve->unref();
4888     return canvasitem;
4891 // TODO: Merge this with sp_nodepath_make_helper_item()!
4892 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4893     np->show_helperpath = show;
4895     if (show) {
4896         SPCurve *helper_curve = np->curve->copy();
4897         helper_curve->transform(np->i2d);
4898         if (!np->helper_path) {
4899             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
4901             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4902             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);
4903             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4904             sp_canvas_item_move_to_z(np->helper_path, 0);
4905             sp_canvas_item_show(np->helper_path);
4906         } else {
4907             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4908         }
4909         helper_curve->unref();
4910     } else {
4911         if (np->helper_path) {
4912             GtkObject *temp = np->helper_path;
4913             np->helper_path = NULL;
4914             gtk_object_destroy(temp);
4915         }
4916     }
4919 /* sp_nodepath_make_straight_path:
4920  *   Prevents user from curving the path by dragging a segment or activating handles etc.
4921  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
4922  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
4923  */
4924 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4925     np->straight_path = true;
4926     np->show_handles = false;
4927     g_message("add code to make the path straight.");
4928     // do sp_nodepath_convert_node_type on all nodes?
4929     // coding tip: search for this text : "Make selected segments lines"
4932 /*
4933   Local Variables:
4934   mode:c++
4935   c-file-style:"stroustrup"
4936   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4937   indent-tabs-mode:nil
4938   fill-column:99
4939   End:
4940 */
4941 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :