Code

fix error in creating nodepath from pathvector
[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 "knot.h"
30 #include "inkscape.h"
31 #include "document.h"
32 #include "sp-namedview.h"
33 #include "desktop.h"
34 #include "desktop-handles.h"
35 #include "snap.h"
36 #include "message-stack.h"
37 #include "message-context.h"
38 #include "node-context.h"
39 #include "shape-editor.h"
40 #include "selection-chemistry.h"
41 #include "selection.h"
42 #include "xml/repr.h"
43 #include "prefs-utils.h"
44 #include "sp-metrics.h"
45 #include "sp-path.h"
46 #include "libnr/nr-matrix-ops.h"
47 #include "svg/svg.h"
48 #include "verbs.h"
49 #include "display/bezier-utils.h"
50 #include <vector>
51 #include <algorithm>
52 #include <cstring>
53 #include <cmath>
54 #include <string>
55 #include "live_effects/lpeobject.h"
56 #include "live_effects/lpeobject-reference.h"
57 #include "live_effects/effect.h"
58 #include "live_effects/parameter/parameter.h"
59 #include "live_effects/parameter/path.h"
60 #include "util/mathfns.h"
61 #include "display/snap-indicator.h"
62 #include "snapped-point.h"
64 class NR::Matrix;
66 /// \todo
67 /// evil evil evil. FIXME: conflict of two different Path classes!
68 /// There is a conflict in the namespace between two classes named Path.
69 /// #include "sp-flowtext.h"
70 /// #include "sp-flowregion.h"
72 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
73 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
74 GType sp_flowregion_get_type (void);
75 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
76 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
77 GType sp_flowtext_get_type (void);
78 // end evil workaround
80 #include "helper/stlport.h"
83 /// \todo fixme: Implement these via preferences */
85 #define NODE_FILL          0xbfbfbf00
86 #define NODE_STROKE        0x000000ff
87 #define NODE_FILL_HI       0xff000000
88 #define NODE_STROKE_HI     0x000000ff
89 #define NODE_FILL_SEL      0x0000ffff
90 #define NODE_STROKE_SEL    0x000000ff
91 #define NODE_FILL_SEL_HI   0xff000000
92 #define NODE_STROKE_SEL_HI 0x000000ff
93 #define KNOT_FILL          0xffffffff
94 #define KNOT_STROKE        0x000000ff
95 #define KNOT_FILL_HI       0xff000000
96 #define KNOT_STROKE_HI     0x000000ff
98 static GMemChunk *nodechunk = NULL;
100 /* Creation from object */
102 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t);
103 static void add_curve_to_subpath( Inkscape::NodePath::Path *np, Inkscape::NodePath::SubPath *sp, Geom::Curve const & c,
104                                   Inkscape::NodePath::NodeType const *t, guint & i, NR::Point & ppos, NRPathcode & pcode  );
105 static Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length);
107 /* Object updating */
109 static void stamp_repr(Inkscape::NodePath::Path *np);
110 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
111 static gchar *create_typestr(Inkscape::NodePath::Path *np);
113 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
115 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
117 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
119 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
121 /* Adjust handle placement, if the node or the other handle is moved */
122 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
123 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
125 /* Node event callbacks */
126 static void node_clicked(SPKnot *knot, guint state, gpointer data);
127 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
128 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
129 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
131 /* Handle event callbacks */
132 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
133 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
134 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
135 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
136 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
137 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
139 /* Constructors and destructors */
141 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
142 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
143 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
144 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
145 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
146                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
147 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
149 /* Helpers */
151 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
152 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
153 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
155 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
156 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
158 // active_node indicates mouseover node
159 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
161 static SPCanvasItem *
162 sp_nodepath_make_helper_item(Inkscape::NodePath::Path *np, /*SPDesktop *desktop, */const SPCurve *curve, bool show = false) {
163     SPCurve *helper_curve = curve->copy();
164     helper_curve->transform(to_2geom(np->i2d));
165     SPCanvasItem *helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
166     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
167     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helper_path), 0, SP_WIND_RULE_NONZERO);
168     sp_canvas_item_move_to_z(helper_path, 0);
169     if (show) {
170         sp_canvas_item_show(helper_path);
171     }
172     helper_curve->unref();
173     return helper_path;
176 static SPCanvasItem *
177 canvasitem_from_pathvec(Inkscape::NodePath::Path *np, Geom::PathVector const &pathv, bool show) {
178     SPCurve *helper_curve = new SPCurve(pathv);
179     return sp_nodepath_make_helper_item(np, helper_curve, show);
182 static void
183 sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) {
184     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> >* helper_path_vec;
185     if (!SP_IS_LPE_ITEM(np->item)) {
186         g_print ("Only LPEItems can have helperpaths!\n");
187         return;
188     }
190     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
191     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
192     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
193         Inkscape::LivePathEffect::LPEObjectReference *lperef = (*i);
194         Inkscape::LivePathEffect::Effect *lpe = lperef->lpeobject->lpe;
195         // create new canvas items from the effect's helper paths
196         std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
197         for (std::vector<Geom::PathVector>::iterator j = hpaths.begin(); j != hpaths.end(); ++j) {
198             (*np->helper_path_vec)[lpe].push_back(canvasitem_from_pathvec(np, *j, true));
199         }
200     }
203 void
204 sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np) {
205     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> >* helper_path_vec;
206     if (!SP_IS_LPE_ITEM(np->item)) {
207         g_print ("Only LPEItems can have helperpaths!\n");
208         return;
209     }
211     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
212     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
213     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
214         Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->lpe;
215         /* update canvas items from the effect's helper paths; note that this code relies on the
216          * fact that getHelperPaths() will always return the same number of helperpaths in the same
217          * order as during their creation in sp_nodepath_create_helperpaths
218          */
219         std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
220         for (unsigned int j = 0; j < hpaths.size(); ++j) {
221             SPCurve *curve = new SPCurve(hpaths[j]);
222             curve->transform(to_2geom(np->i2d));
223             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(((*np->helper_path_vec)[lpe])[j]), curve);
224             curve = curve->unref();
225         }
226     }
229 static void
230 sp_nodepath_destroy_helperpaths(Inkscape::NodePath::Path *np) {
231     for (HelperPathList::iterator i = np->helper_path_vec->begin(); i != np->helper_path_vec->end(); ++i) {
232         for (std::vector<SPCanvasItem *>::iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) {
233             GtkObject *temp = *j;
234             *j = NULL;
235             gtk_object_destroy(temp);
236         }
237     }
241 /**
242  * \brief Creates new nodepath from item
243  */
244 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
246     Inkscape::XML::Node *repr = object->repr;
248     /** \todo
249      * FIXME: remove this. We don't want to edit paths inside flowtext.
250      * Instead we will build our flowtext with cloned paths, so that the
251      * real paths are outside the flowtext and thus editable as usual.
252      */
253     if (SP_IS_FLOWTEXT(object)) {
254         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
255             if SP_IS_FLOWREGION(child) {
256                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
257                 if (grandchild && SP_IS_PATH(grandchild)) {
258                     object = SP_ITEM(grandchild);
259                     break;
260                 }
261             }
262         }
263     }
265     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
267     if (curve == NULL)
268         return NULL;
270     if (curve->get_segment_count() < 1) {
271         curve->unref();
272         return NULL; // prevent crash for one-node paths
273     }
275     //Create new nodepath
276     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
277     if (!np) {
278         curve->unref();
279         return NULL;
280     }
282     // Set defaults
283     np->desktop     = desktop;
284     np->object      = object;
285     np->subpaths    = NULL;
286     np->selected    = NULL;
287     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
288     np->local_change = 0;
289     np->show_handles = show_handles;
290     np->helper_path = NULL;
291     np->helper_path_vec = new HelperPathList;
292     np->helperpath_rgba = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
293     np->helperpath_width = 1.0;
294     np->curve = curve->copy();
295     np->show_helperpath = (prefs_get_int_attribute ("tools.nodes", "show_helperpath",  0) == 1);
296     if (SP_IS_LPE_ITEM(object)) {
297         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
298         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
299             np->show_helperpath = true;
300         }            
301     }
302     np->straight_path = false;
303     if (IS_LIVEPATHEFFECT(object) && item) {
304         np->item = item;
305     } else {
306         np->item = SP_ITEM(object);
307     }
309     // we need to update item's transform from the repr here,
310     // because they may be out of sync when we respond
311     // to a change in repr by regenerating nodepath     --bb
312     sp_object_read_attr(SP_OBJECT(np->item), "transform");
314     np->i2d  = from_2geom(sp_item_i2d_affine(np->item));
315     np->d2i  = np->i2d.inverse();
317     np->repr = repr;
318     if (repr_key_in) { // apparantly the object is an LPEObject
319         np->repr_key = g_strdup(repr_key_in);
320         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
321         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
322         if (lpeparam) {
323             lpeparam->param_setup_nodepath(np);
324         }
325     } else {
326         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
327         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
328             np->repr_key = g_strdup("inkscape:original-d");
330             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
331             if (lpe) {
332                 lpe->setup_nodepath(np);
333             }
334         } else {
335             np->repr_key = g_strdup("d");
336         }
337     }
339     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
340      * So for example a closed rectangle has a nodetypestring of length 5.
341      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
342     Geom::PathVector const &pathv = curve->get_pathvector();
343     guint length = curve->get_segment_count();
344     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.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, 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 = from_2geom(pit->initialPoint()) * np->i2d;
527         NRPathcode pcode = NR_MOVETO;
529         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
530             add_curve_to_subpath(np, sp, *cit, t, i, ppos, pcode);
531         }
533         if (pit->closed()) {
534             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
535             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
536              * If the length is zero, don't add it to the nodepath. */
537             Geom::Curve const &closing_seg = pit->back_closed();
538             if ( ! closing_seg.isDegenerate() ) {
539                 NR::Point pos = from_2geom(closing_seg.finalPoint()) * np->i2d;
540                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
541             }
543             sp_nodepath_subpath_close(sp);
544         }
545     }
547 // should add initial point of curve with type of previous curve:
548 static void add_curve_to_subpath(Inkscape::NodePath::Path *np, Inkscape::NodePath::SubPath *sp, Geom::Curve const & c, Inkscape::NodePath::NodeType const *t, guint & i,
549                                  NR::Point & ppos, NRPathcode & pcode)
551     if( dynamic_cast<Geom::LineSegment const*>(&c) ||
552         dynamic_cast<Geom::HLineSegment const*>(&c) ||
553         dynamic_cast<Geom::VLineSegment const*>(&c) )
554     {
555         NR::Point pos = from_2geom(c.initialPoint()) * np->i2d;
556         sp_nodepath_node_new(sp, NULL, t[i++], pcode, &pos, &pos, &pos);
557         ppos = from_2geom(c.finalPoint());
558         pcode = NR_LINETO;
559     }
560     else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&c)) {
561         std::vector<Geom::Point> points = cubic_bezier->points();
562         NR::Point pos = from_2geom(points[0]) * np->i2d;
563         NR::Point npos = from_2geom(points[1]) * np->i2d;
564         sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
565         ppos = from_2geom(points[2]) * np->i2d;
566         pcode = NR_CURVETO;
567     }
568     else {
569         //this case handles sbasis as well as all other curve types
570         Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(c.toSBasis(), 0.1);
572         for(Geom::Path::iterator iter = sbasis_path.begin(); iter != sbasis_path.end(); ++iter) {
573             add_curve_to_subpath(np, sp, *iter, t, i, ppos, pcode);
574         }
575     }
579 /**
580  * Convert from sodipodi:nodetypes to new style type array.
581  */
582 static
583 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
585     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
587     guint pos = 0;
589     if (types) {
590         for (guint i = 0; types[i] && ( i < length ); i++) {
591             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
592             if (types[i] != '\0') {
593                 switch (types[i]) {
594                     case 's':
595                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
596                         break;
597                     case 'z':
598                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
599                         break;
600                     case 'c':
601                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
602                         break;
603                     default:
604                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
605                         break;
606                 }
607             }
608         }
609     }
611     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
613     return typestr;
616 /**
617  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
618  * updated but repr is not (for speed). Used during curve and node drag.
619  */
620 static void update_object(Inkscape::NodePath::Path *np)
622     g_assert(np);
624     np->curve->unref();
625     np->curve = create_curve(np);
627     sp_nodepath_set_curve(np, np->curve);
629     if (np->show_helperpath) {
630         SPCurve * helper_curve = np->curve->copy();
631         helper_curve->transform(to_2geom(np->i2d));
632         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
633         helper_curve->unref();
634     }
636     // updating helperpaths of LPEItems is now done in sp_lpe_item_update();
637     //sp_nodepath_update_helperpaths(np);
639     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
640     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
641     np->shape_editor->update_knotholder();
644 /**
645  * Update XML path node with data from path object.
646  */
647 static void update_repr_internal(Inkscape::NodePath::Path *np)
649     g_assert(np);
651     Inkscape::XML::Node *repr = np->object->repr;
653     np->curve->unref();
654     np->curve = create_curve(np);
656     gchar *typestr = create_typestr(np);
657     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
659     // determine if path has an effect applied and write to correct "d" attribute.
660     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
661         np->local_change++;
662         repr->setAttribute(np->repr_key, svgpath);
663     }
665     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
666         np->local_change++;
667         repr->setAttribute(np->repr_nodetypes_key, typestr);
668     }
670     g_free(svgpath);
671     g_free(typestr);
673     if (np->show_helperpath) {
674         SPCurve * helper_curve = np->curve->copy();
675         helper_curve->transform(to_2geom(np->i2d));
676         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
677         helper_curve->unref();
678     }
680     // TODO: do we need this call here? after all, update_object() should have been called just before
681     //sp_nodepath_update_helperpaths(np);
684 /**
685  * Update XML path node with data from path object, commit changes forever.
686  */
687 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
689     //fixme: np can be NULL, so check before proceeding
690     g_return_if_fail(np != NULL);
692     update_repr_internal(np);
693     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
695     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
696                      annotation);
699 /**
700  * Update XML path node with data from path object, commit changes with undo.
701  */
702 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
704     update_repr_internal(np);
705     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
706                            annotation);
709 /**
710  * Make duplicate of path, replace corresponding XML node in tree, commit.
711  */
712 static void stamp_repr(Inkscape::NodePath::Path *np)
714     g_assert(np);
716     Inkscape::XML::Node *old_repr = np->object->repr;
717     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
719     // remember the position of the item
720     gint pos = old_repr->position();
721     // remember parent
722     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
724     SPCurve *curve = create_curve(np);
725     gchar *typestr = create_typestr(np);
727     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
729     new_repr->setAttribute(np->repr_key, svgpath);
730     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
732     // add the new repr to the parent
733     parent->appendChild(new_repr);
734     // move to the saved position
735     new_repr->setPosition(pos > 0 ? pos : 0);
737     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
738                      _("Stamp"));
740     Inkscape::GC::release(new_repr);
741     g_free(svgpath);
742     g_free(typestr);
743     curve->unref();
746 /**
747  * Create curve from path.
748  */
749 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
751     SPCurve *curve = new SPCurve();
753     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
754        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
755         curve->moveto(sp->first->pos * np->d2i);
756        Inkscape::NodePath::Node *n = sp->first->n.other;
757         while (n) {
758             NR::Point const end_pt = n->pos * np->d2i;
759             switch (n->code) {
760                 case NR_LINETO:
761                     curve->lineto(end_pt);
762                     break;
763                 case NR_CURVETO:
764                     curve->curveto(n->p.other->n.pos * np->d2i,
765                                      n->p.pos * np->d2i,
766                                      end_pt);
767                     break;
768                 default:
769                     g_assert_not_reached();
770                     break;
771             }
772             if (n != sp->last) {
773                 n = n->n.other;
774             } else {
775                 n = NULL;
776             }
777         }
778         if (sp->closed) {
779             curve->closepath();
780         }
781     }
783     return curve;
786 /**
787  * Convert path type string to sodipodi:nodetypes style.
788  */
789 static gchar *create_typestr(Inkscape::NodePath::Path *np)
791     gchar *typestr = g_new(gchar, 32);
792     gint len = 32;
793     gint pos = 0;
795     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
796        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
798         if (pos >= len) {
799             typestr = g_renew(gchar, typestr, len + 32);
800             len += 32;
801         }
803         typestr[pos++] = 'c';
805        Inkscape::NodePath::Node *n;
806         n = sp->first->n.other;
807         while (n) {
808             gchar code;
810             switch (n->type) {
811                 case Inkscape::NodePath::NODE_CUSP:
812                     code = 'c';
813                     break;
814                 case Inkscape::NodePath::NODE_SMOOTH:
815                     code = 's';
816                     break;
817                 case Inkscape::NodePath::NODE_SYMM:
818                     code = 'z';
819                     break;
820                 default:
821                     g_assert_not_reached();
822                     code = '\0';
823                     break;
824             }
826             if (pos >= len) {
827                 typestr = g_renew(gchar, typestr, len + 32);
828                 len += 32;
829             }
831             typestr[pos++] = code;
833             if (n != sp->last) {
834                 n = n->n.other;
835             } else {
836                 n = NULL;
837             }
838         }
839     }
841     if (pos >= len) {
842         typestr = g_renew(gchar, typestr, len + 1);
843         len += 1;
844     }
846     typestr[pos++] = '\0';
848     return typestr;
851 /**
852  * Returns current path in context. // later eliminate this function at all!
853  */
854 static Inkscape::NodePath::Path *sp_nodepath_current()
856     if (!SP_ACTIVE_DESKTOP) {
857         return NULL;
858     }
860     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
862     if (!SP_IS_NODE_CONTEXT(event_context)) {
863         return NULL;
864     }
866     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
871 /**
872  \brief Fills node and handle positions for three nodes, splitting line
873   marked by end at distance t.
874  */
875 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
877     g_assert(new_path != NULL);
878     g_assert(end      != NULL);
880     g_assert(end->p.other == new_path);
881    Inkscape::NodePath::Node *start = new_path->p.other;
882     g_assert(start);
884     if (end->code == NR_LINETO) {
885         new_path->type =Inkscape::NodePath::NODE_CUSP;
886         new_path->code = NR_LINETO;
887         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
888     } else {
889         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
890         new_path->code = NR_CURVETO;
891         gdouble s      = 1 - t;
892         for (int dim = 0; dim < 2; dim++) {
893             NR::Coord const f000 = start->pos[dim];
894             NR::Coord const f001 = start->n.pos[dim];
895             NR::Coord const f011 = end->p.pos[dim];
896             NR::Coord const f111 = end->pos[dim];
897             NR::Coord const f00t = s * f000 + t * f001;
898             NR::Coord const f01t = s * f001 + t * f011;
899             NR::Coord const f11t = s * f011 + t * f111;
900             NR::Coord const f0tt = s * f00t + t * f01t;
901             NR::Coord const f1tt = s * f01t + t * f11t;
902             NR::Coord const fttt = s * f0tt + t * f1tt;
903             start->n.pos[dim]    = f00t;
904             new_path->p.pos[dim] = f0tt;
905             new_path->pos[dim]   = fttt;
906             new_path->n.pos[dim] = f1tt;
907             end->p.pos[dim]      = f11t;
908         }
909     }
912 /**
913  * Adds new node on direct line between two nodes, activates handles of all
914  * three nodes.
915  */
916 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
918     g_assert(end);
919     g_assert(end->subpath);
920     g_assert(g_list_find(end->subpath->nodes, end));
922    Inkscape::NodePath::Node *start = end->p.other;
923     g_assert( start->n.other == end );
924    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
925                                                end,
926                                                (NRPathcode)end->code == NR_LINETO?
927                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
928                                                (NRPathcode)end->code,
929                                                &start->pos, &start->pos, &start->n.pos);
930     sp_nodepath_line_midpoint(newnode, end, t);
932     sp_node_adjust_handles(start);
933     sp_node_update_handles(start);
934     sp_node_update_handles(newnode);
935     sp_node_adjust_handles(end);
936     sp_node_update_handles(end);
938     return newnode;
941 /**
942 \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
943 */
944 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
946     g_assert(node);
947     g_assert(node->subpath);
948     g_assert(g_list_find(node->subpath->nodes, node));
950    Inkscape::NodePath::SubPath *sp = node->subpath;
951     Inkscape::NodePath::Path *np    = sp->nodepath;
953     if (sp->closed) {
954         sp_nodepath_subpath_open(sp, node);
955         return sp->first;
956     } else {
957         // no break for end nodes
958         if (node == sp->first) return NULL;
959         if (node == sp->last ) return NULL;
961         // create a new subpath
962        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
964         // duplicate the break node as start of the new subpath
965         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
967         // attach rest of curve to new node
968         g_assert(node->n.other);
969         newnode->n.other = node->n.other; node->n.other = NULL;
970         newnode->n.other->p.other = newnode;
971         newsubpath->last = sp->last;
972         sp->last = node;
973         node = newnode;
974         while (node->n.other) {
975             node = node->n.other;
976             node->subpath = newsubpath;
977             sp->nodes = g_list_remove(sp->nodes, node);
978             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
979         }
982         return newnode;
983     }
986 /**
987  * Duplicate node and connect to neighbours.
988  */
989 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
991     g_assert(node);
992     g_assert(node->subpath);
993     g_assert(g_list_find(node->subpath->nodes, node));
995    Inkscape::NodePath::SubPath *sp = node->subpath;
997     NRPathcode code = (NRPathcode) node->code;
998     if (code == NR_MOVETO) { // if node is the endnode,
999         node->code = NR_LINETO; // new one is inserted before it, so change that to line
1000     }
1002     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
1004     if (!node->n.other || !node->p.other) // if node is an endnode, select it
1005         return node;
1006     else
1007         return newnode; // otherwise select the newly created node
1010 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
1012     node->p.pos = (node->pos + (node->pos - node->n.pos));
1015 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
1017     node->n.pos = (node->pos + (node->pos - node->p.pos));
1020 /**
1021  * Change line type at node, with side effects on neighbours.
1022  */
1023 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
1025     g_assert(end);
1026     g_assert(end->subpath);
1027     g_assert(end->p.other);
1029     if (end->code == static_cast< guint > ( code ) )
1030         return;
1032    Inkscape::NodePath::Node *start = end->p.other;
1034     end->code = code;
1036     if (code == NR_LINETO) {
1037         if (start->code == NR_LINETO) {
1038             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
1039         }
1040         if (end->n.other) {
1041             if (end->n.other->code == NR_LINETO) {
1042                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
1043             }
1044         }
1045     } else {
1046         NR::Point delta = end->pos - start->pos;
1047         start->n.pos = start->pos + delta / 3;
1048         end->p.pos = end->pos - delta / 3;
1049         sp_node_adjust_handle(start, 1);
1050         sp_node_adjust_handle(end, -1);
1051     }
1053     sp_node_update_handles(start);
1054     sp_node_update_handles(end);
1057 /**
1058  * Change node type, and its handles accordingly.
1059  */
1060 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1062     g_assert(node);
1063     g_assert(node->subpath);
1065     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1066         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1067             type =Inkscape::NodePath::NODE_CUSP;
1068         }
1069     }
1071     node->type = type;
1073     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1074         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1075         node->knot->setSize (node->selected? 11 : 9);
1076         sp_knot_update_ctrl(node->knot);
1077     } else {
1078         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1079         node->knot->setSize (node->selected? 9 : 7);
1080         sp_knot_update_ctrl(node->knot);
1081     }
1083     // if one of handles is mouseovered, preserve its position
1084     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1085         sp_node_adjust_handle(node, 1);
1086     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1087         sp_node_adjust_handle(node, -1);
1088     } else {
1089         sp_node_adjust_handles(node);
1090     }
1092     sp_node_update_handles(node);
1094     sp_nodepath_update_statusbar(node->subpath->nodepath);
1096     return node;
1099 bool
1100 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1102         Inkscape::NodePath::Node *othernode = side->other;
1103         if (!othernode)
1104             return false;
1105         NRPathcode const code = sp_node_path_code_from_side(node, side);
1106         if (code == NR_LINETO)
1107             return true;
1108         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1109         if (&node->p == side) {
1110             other_to_me = &othernode->n;
1111         } else if (&node->n == side) {
1112             other_to_me = &othernode->p;
1113         } 
1114         if (!other_to_me)
1115             return false;
1116         bool is_line = 
1117              (NR::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1118               NR::L2(node->pos - side->pos) < 1e-6);
1119         return is_line;
1122 /**
1123  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1124  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1125  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1126  * If already cusp and set to cusp, retracts handles.
1127 */
1128 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1130     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1132 /* 
1133   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1134  
1135         if (two_handles) {
1136             // do nothing, adjust_handles called via set_node_type will line them up
1137         } else if (one_handle) {
1138             if (opposite_to_handle_is_line) {
1139                 if (lined_up) {
1140                     // already half-smooth; pull opposite handle too making it fully smooth
1141                 } else {
1142                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1143                 }
1144             } else {
1145                 // pull opposite handle in line with the existing one
1146             }
1147         } else if (no_handles) {
1148             if (both_segments_are_lines OR both_segments_are_curves) {
1149                 //pull both handles
1150             } else {
1151                 // pull the handle opposite to line segment, making node half-smooth
1152             }
1153         }
1154 */
1155         bool p_has_handle = (NR::L2(node->pos  - node->p.pos) > 1e-6);
1156         bool n_has_handle = (NR::L2(node->pos  - node->n.pos) > 1e-6);
1157         bool p_is_line = sp_node_side_is_line(node, &node->p);
1158         bool n_is_line = sp_node_side_is_line(node, &node->n);
1160         if (p_has_handle && n_has_handle) {
1161             // do nothing, adjust_handles will line them up
1162         } else if (p_has_handle || n_has_handle) {
1163             if (p_has_handle && n_is_line) {
1164                 Radial line (node->n.other->pos - node->pos);
1165                 Radial handle (node->pos - node->p.pos);
1166                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1167                     // already half-smooth; pull opposite handle too making it fully smooth
1168                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1169                 } else {
1170                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1171                 }
1172             } else if (n_has_handle && p_is_line) {
1173                 Radial line (node->p.other->pos - node->pos);
1174                 Radial handle (node->pos - node->n.pos);
1175                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1176                     // already half-smooth; pull opposite handle too making it fully smooth
1177                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1178                 } else {
1179                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1180                 }
1181             } else if (p_has_handle && node->n.other) {
1182                 // pull n handle
1183                 node->n.other->code = NR_CURVETO;
1184                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1185                     NR::L2(node->p.pos - node->pos) :
1186                     NR::L2(node->n.other->pos - node->pos) / 3;
1187                 node->n.pos = node->pos - (len / NR::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1188             } else if (n_has_handle && node->p.other) {
1189                 // pull p handle
1190                 node->code = NR_CURVETO;
1191                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1192                     NR::L2(node->n.pos - node->pos) :
1193                     NR::L2(node->p.other->pos - node->pos) / 3;
1194                 node->p.pos = node->pos - (len / NR::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1195             }
1196         } else if (!p_has_handle && !n_has_handle) {
1197             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1198                 // no handles, but both segments are either lnes or curves:
1199                 //pull both handles
1201                 // convert both to curves:
1202                 node->code = NR_CURVETO;
1203                 node->n.other->code = NR_CURVETO;
1205                 NR::Point leg_prev = node->pos - node->p.other->pos;
1206                 NR::Point leg_next = node->pos - node->n.other->pos;
1208                 double norm_leg_prev = L2(leg_prev);
1209                 double norm_leg_next = L2(leg_next);
1211                 NR::Point delta;
1212                 if (norm_leg_next > 0.0) {
1213                     delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1214                     (&delta)->normalize();
1215                 }
1217                 if (type == Inkscape::NodePath::NODE_SYMM) {
1218                     double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1219                     node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1220                     node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1221                 } else {
1222                     // length of handle is proportional to distance to adjacent node
1223                     node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1224                     node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1225                 }
1227             } else {
1228                 // pull the handle opposite to line segment, making it half-smooth
1229                 if (p_is_line && node->n.other) {
1230                     if (type != Inkscape::NodePath::NODE_SYMM) {
1231                         // pull n handle
1232                         node->n.other->code = NR_CURVETO;
1233                         double len =  NR::L2(node->n.other->pos - node->pos) / 3;
1234                         node->n.pos = node->pos + (len / NR::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1235                     }
1236                 } else if (n_is_line && node->p.other) {
1237                     if (type != Inkscape::NodePath::NODE_SYMM) {
1238                         // pull p handle
1239                         node->code = NR_CURVETO;
1240                         double len =  NR::L2(node->p.other->pos - node->pos) / 3;
1241                         node->p.pos = node->pos + (len / NR::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1242                     }
1243                 }
1244             }
1245         }
1246     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1247         // cusping a cusp: retract nodes
1248         node->p.pos = node->pos;
1249         node->n.pos = node->pos;
1250     }
1252     sp_nodepath_set_node_type (node, type);
1255 /**
1256  * Move node to point, and adjust its and neighbouring handles.
1257  */
1258 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1260     NR::Point delta = p - node->pos;
1261     node->pos = p;
1263     node->p.pos += delta;
1264     node->n.pos += delta;
1266     Inkscape::NodePath::Node *node_p = NULL;
1267     Inkscape::NodePath::Node *node_n = NULL;
1269     if (node->p.other) {
1270         if (node->code == NR_LINETO) {
1271             sp_node_adjust_handle(node, 1);
1272             sp_node_adjust_handle(node->p.other, -1);
1273             node_p = node->p.other;
1274         }
1275     }
1276     if (node->n.other) {
1277         if (node->n.other->code == NR_LINETO) {
1278             sp_node_adjust_handle(node, -1);
1279             sp_node_adjust_handle(node->n.other, 1);
1280             node_n = node->n.other;
1281         }
1282     }
1284     // this function is only called from batch movers that will update display at the end
1285     // themselves, so here we just move all the knots without emitting move signals, for speed
1286     sp_node_update_handles(node, false);
1287     if (node_n) {
1288         sp_node_update_handles(node_n, false);
1289     }
1290     if (node_p) {
1291         sp_node_update_handles(node_p, false);
1292     }
1295 /**
1296  * Call sp_node_moveto() for node selection and handle possible snapping.
1297  */
1298 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1299                                             bool const snap, bool constrained = false, 
1300                                             Inkscape::Snapper::ConstraintLine const &constraint = NR::Point())
1302     NR::Coord best = NR_HUGE;
1303     NR::Point delta(dx, dy);
1304     NR::Point best_pt = delta;
1305     Inkscape::SnappedPoint best_abs;
1306     
1307     if (snap) {    
1308         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1309          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1310          * must provide that information. */
1311           
1312         // Build a list of the unselected nodes to which the snapper should snap 
1313         std::vector<NR::Point> unselected_nodes;
1314         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1315             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1316             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1317                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1318                 if (!node->selected) {
1319                     unselected_nodes.push_back(node->pos);
1320                 }    
1321             }
1322         }        
1323         
1324         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1325         
1326         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1327             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1328             m.setup(NULL, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1329             Inkscape::SnappedPoint s;
1330             if (constrained) {
1331                 Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1332                 dedicated_constraint.setPoint(n->pos);
1333                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, dedicated_constraint);
1334             } else {
1335                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta);
1336             }            
1337             if (s.getSnapped() && (s.getDistance() < best)) {
1338                 best = s.getDistance();
1339                 best_abs = s;
1340                 best_pt = s.getPoint() - n->pos;
1341             }
1342         }
1343                         
1344         if (best_abs.getSnapped()) {
1345             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1346         } else {
1347             nodepath->desktop->snapindicator->remove_snappoint();    
1348         }
1349     }
1351     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1352         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1353         sp_node_moveto(n, n->pos + best_pt);
1354     }
1356     // do not update repr here so that node dragging is acceptably fast
1357     update_object(nodepath);
1360 /**
1361 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1362 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1363 near x = 0.
1364  */
1365 double
1366 sculpt_profile (double x, double alpha, guint profile)
1368     if (x >= 1)
1369         return 0;
1370     if (x <= 0)
1371         return 1;
1373     switch (profile) {
1374         case SCULPT_PROFILE_LINEAR:
1375         return 1 - x;
1376         case SCULPT_PROFILE_BELL:
1377         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1378         case SCULPT_PROFILE_ELLIPTIC:
1379         return sqrt(1 - x*x);
1380     }
1382     return 1;
1385 double
1386 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1388     // extremely primitive for now, don't have time to look for the real one
1389     double lower = NR::L2(b - a);
1390     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1391     return (lower + upper)/2;
1394 void
1395 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1397     n->pos = n->origin + delta;
1398     n->n.pos = n->n.origin + delta_n;
1399     n->p.pos = n->p.origin + delta_p;
1400     sp_node_adjust_handles(n);
1401     sp_node_update_handles(n, false);
1404 /**
1405  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1406  * on how far they are from the dragged node n.
1407  */
1408 static void
1409 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1411     g_assert (n);
1412     g_assert (nodepath);
1413     g_assert (n->subpath->nodepath == nodepath);
1415     double pressure = n->knot->pressure;
1416     if (pressure == 0)
1417         pressure = 0.5; // default
1418     pressure = CLAMP (pressure, 0.2, 0.8);
1420     // map pressure to alpha = 1/5 ... 5
1421     double alpha = 1 - 2 * fabs(pressure - 0.5);
1422     if (pressure > 0.5)
1423         alpha = 1/alpha;
1425     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1427     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1428         // Only one subpath has selected nodes:
1429         // use linear mode, where the distance from n to node being dragged is calculated along the path
1431         double n_sel_range = 0, p_sel_range = 0;
1432         guint n_nodes = 0, p_nodes = 0;
1433         guint n_sel_nodes = 0, p_sel_nodes = 0;
1435         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1436         {
1437             double n_range = 0, p_range = 0;
1438             bool n_going = true, p_going = true;
1439             Inkscape::NodePath::Node *n_node = n;
1440             Inkscape::NodePath::Node *p_node = n;
1441             do {
1442                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1443                 if (n_node && n_going)
1444                     n_node = n_node->n.other;
1445                 if (n_node == NULL) {
1446                     n_going = false;
1447                 } else {
1448                     n_nodes ++;
1449                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1450                     if (n_node->selected) {
1451                         n_sel_nodes ++;
1452                         n_sel_range = n_range;
1453                     }
1454                     if (n_node == p_node) {
1455                         n_going = false;
1456                         p_going = false;
1457                     }
1458                 }
1459                 if (p_node && p_going)
1460                     p_node = p_node->p.other;
1461                 if (p_node == NULL) {
1462                     p_going = false;
1463                 } else {
1464                     p_nodes ++;
1465                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1466                     if (p_node->selected) {
1467                         p_sel_nodes ++;
1468                         p_sel_range = p_range;
1469                     }
1470                     if (p_node == n_node) {
1471                         n_going = false;
1472                         p_going = false;
1473                     }
1474                 }
1475             } while (n_going || p_going);
1476         }
1478         // Second pass: actually move nodes in this subpath
1479         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1480         {
1481             double n_range = 0, p_range = 0;
1482             bool n_going = true, p_going = true;
1483             Inkscape::NodePath::Node *n_node = n;
1484             Inkscape::NodePath::Node *p_node = n;
1485             do {
1486                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1487                 if (n_node && n_going)
1488                     n_node = n_node->n.other;
1489                 if (n_node == NULL) {
1490                     n_going = false;
1491                 } else {
1492                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1493                     if (n_node->selected) {
1494                         sp_nodepath_move_node_and_handles (n_node,
1495                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1496                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1497                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1498                     }
1499                     if (n_node == p_node) {
1500                         n_going = false;
1501                         p_going = false;
1502                     }
1503                 }
1504                 if (p_node && p_going)
1505                     p_node = p_node->p.other;
1506                 if (p_node == NULL) {
1507                     p_going = false;
1508                 } else {
1509                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1510                     if (p_node->selected) {
1511                         sp_nodepath_move_node_and_handles (p_node,
1512                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1513                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1514                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1515                     }
1516                     if (p_node == n_node) {
1517                         n_going = false;
1518                         p_going = false;
1519                     }
1520                 }
1521             } while (n_going || p_going);
1522         }
1524     } else {
1525         // Multiple subpaths have selected nodes:
1526         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1527         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1528         // fix the pear-like shape when sculpting e.g. a ring
1530         // First pass: calculate range
1531         gdouble direct_range = 0;
1532         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1533             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1534             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1535                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1536                 if (node->selected) {
1537                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1538                 }
1539             }
1540         }
1542         // Second pass: actually move nodes
1543         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1544             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1545             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1546                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1547                 if (node->selected) {
1548                     if (direct_range > 1e-6) {
1549                         sp_nodepath_move_node_and_handles (node,
1550                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1551                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1552                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1553                     } else {
1554                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1555                     }
1557                 }
1558             }
1559         }
1560     }
1562     // do not update repr here so that node dragging is acceptably fast
1563     update_object(nodepath);
1567 /**
1568  * Move node selection to point, adjust its and neighbouring handles,
1569  * handle possible snapping, and commit the change with possible undo.
1570  */
1571 void
1572 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1574     if (!nodepath) return;
1576     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1578     if (dx == 0) {
1579         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1580     } else if (dy == 0) {
1581         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1582     } else {
1583         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1584     }
1587 /**
1588  * Move node selection off screen and commit the change.
1589  */
1590 void
1591 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1593     // borrowed from sp_selection_move_screen in selection-chemistry.c
1594     // we find out the current zoom factor and divide deltas by it
1595     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1597     gdouble zoom = desktop->current_zoom();
1598     gdouble zdx = dx / zoom;
1599     gdouble zdy = dy / zoom;
1601     if (!nodepath) return;
1603     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1605     if (dx == 0) {
1606         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1607     } else if (dy == 0) {
1608         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1609     } else {
1610         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1611     }
1614 /**
1615  * Move selected nodes to the absolute position given
1616  */
1617 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis)
1619     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1620         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1621         NR::Point npos(axis == NR::X ? val : n->pos[NR::X], axis == NR::Y ? val : n->pos[NR::Y]);
1622         sp_node_moveto(n, npos);
1623     }
1625     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1628 /**
1629  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1630  */
1631 NR::Maybe<NR::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1633     NR::Maybe<NR::Coord> no_coord = NR::Nothing();
1634     g_return_val_if_fail(nodepath->selected, no_coord);
1636     // determine coordinate of first selected node
1637     GList *nsel = nodepath->selected;
1638     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1639     NR::Coord coord = n->pos[axis];
1640     bool coincide = true;
1642     // compare it to the coordinates of all the other selected nodes
1643     for (GList *l = nsel->next; l != NULL; l = l->next) {
1644         n = (Inkscape::NodePath::Node *) l->data;
1645         if (n->pos[axis] != coord) {
1646             coincide = false;
1647         }
1648     }
1649     if (coincide) {
1650         return coord;
1651     } else {
1652         NR::Rect bbox = sp_node_selected_bbox(nodepath);
1653         // currently we return the coordinate of the bounding box midpoint because I don't know how
1654         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1655         return bbox.midpoint()[axis];
1656     }
1659 /** If they don't yet exist, creates knot and line for the given side of the node */
1660 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1662     if (!side->knot) {
1663         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"));
1665         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1666         side->knot->setSize (7);
1667         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1668         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1669         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1670         sp_knot_update_ctrl(side->knot);
1672         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1673         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1674         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1675         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1676         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1677         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1678     }
1680     if (!side->line) {
1681         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1682                                         SP_TYPE_CTRLLINE, NULL);
1683     }
1686 /**
1687  * Ensure the given handle of the node is visible/invisible, update its screen position
1688  */
1689 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1691     g_assert(node != NULL);
1693    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1694     NRPathcode code = sp_node_path_code_from_side(node, side);
1696     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1698     if (show_handle) {
1699         if (!side->knot) { // No handle knot at all
1700             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1701             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1702             side->knot->pos = side->pos;
1703             if (side->knot->item)
1704                 SP_CTRL(side->knot->item)->moveto(side->pos);
1705             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1706             sp_knot_show(side->knot);
1707         } else {
1708             if (side->knot->pos != side->pos) { // only if it's really moved
1709                 if (fire_move_signals) {
1710                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1711                 } else {
1712                     sp_knot_moveto(side->knot, &side->pos);
1713                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1714                 }
1715             }
1716             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1717                 sp_knot_show(side->knot);
1718             }
1719         }
1720         sp_canvas_item_show(side->line);
1721     } else {
1722         if (side->knot) {
1723             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1724                 sp_knot_hide(side->knot);
1725             }
1726         }
1727         if (side->line) {
1728             sp_canvas_item_hide(side->line);
1729         }
1730     }
1733 /**
1734  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1735  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1736  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1737  * updated; otherwise, just move the knots silently (used in batch moves).
1738  */
1739 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1741     g_assert(node != NULL);
1743     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1744         sp_knot_show(node->knot);
1745     }
1747     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1748         if (fire_move_signals)
1749             sp_knot_set_position(node->knot, &node->pos, 0);
1750         else
1751             sp_knot_moveto(node->knot, &node->pos);
1752     }
1754     gboolean show_handles = node->selected;
1755     if (node->p.other != NULL) {
1756         if (node->p.other->selected) show_handles = TRUE;
1757     }
1758     if (node->n.other != NULL) {
1759         if (node->n.other->selected) show_handles = TRUE;
1760     }
1762     if (node->subpath->nodepath->show_handles == false)
1763         show_handles = FALSE;
1765     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1766     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1769 /**
1770  * Call sp_node_update_handles() for all nodes on subpath.
1771  */
1772 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1774     g_assert(subpath != NULL);
1776     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1777         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1778     }
1781 /**
1782  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1783  */
1784 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1786     g_assert(nodepath != NULL);
1788     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1789         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1790     }
1793 void
1794 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1796     if (nodepath == NULL) return;
1798     nodepath->show_handles = show;
1799     sp_nodepath_update_handles(nodepath);
1802 /**
1803  * Adds all selected nodes in nodepath to list.
1804  */
1805 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1807     StlConv<Node *>::list(l, selected);
1808 /// \todo this adds a copying, rework when the selection becomes a stl list
1811 /**
1812  * Align selected nodes on the specified axis.
1813  */
1814 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1816     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1817         return;
1818     }
1820     if ( !nodepath->selected->next ) { // only one node selected
1821         return;
1822     }
1823    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1824     NR::Point dest(pNode->pos);
1825     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1826         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1827         if (pNode) {
1828             dest[axis] = pNode->pos[axis];
1829             sp_node_moveto(pNode, dest);
1830         }
1831     }
1833     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1836 /// Helper struct.
1837 struct NodeSort
1839    Inkscape::NodePath::Node *_node;
1840     NR::Coord _coord;
1841     /// \todo use vectorof pointers instead of calling copy ctor
1842     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1843         _node(node), _coord(node->pos[axis])
1844     {}
1846 };
1848 static bool operator<(NodeSort const &a, NodeSort const &b)
1850     return (a._coord < b._coord);
1853 /**
1854  * Distribute selected nodes on the specified axis.
1855  */
1856 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1858     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1859         return;
1860     }
1862     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1863         return;
1864     }
1866    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1867     std::vector<NodeSort> sorted;
1868     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1869         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1870         if (pNode) {
1871             NodeSort n(pNode, axis);
1872             sorted.push_back(n);
1873             //dest[axis] = pNode->pos[axis];
1874             //sp_node_moveto(pNode, dest);
1875         }
1876     }
1877     std::sort(sorted.begin(), sorted.end());
1878     unsigned int len = sorted.size();
1879     //overall bboxes span
1880     float dist = (sorted.back()._coord -
1881                   sorted.front()._coord);
1882     //new distance between each bbox
1883     float step = (dist) / (len - 1);
1884     float pos = sorted.front()._coord;
1885     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1886           it < sorted.end();
1887           it ++ )
1888     {
1889         NR::Point dest((*it)._node->pos);
1890         dest[axis] = pos;
1891         sp_node_moveto((*it)._node, dest);
1892         pos += step;
1893     }
1895     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1899 /**
1900  * Call sp_nodepath_line_add_node() for all selected segments.
1901  */
1902 void
1903 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1905     if (!nodepath) {
1906         return;
1907     }
1909     GList *nl = NULL;
1911     int n_added = 0;
1913     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1914        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1915         g_assert(t->selected);
1916         if (t->p.other && t->p.other->selected) {
1917             nl = g_list_prepend(nl, t);
1918         }
1919     }
1921     while (nl) {
1922        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1923        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1924        sp_nodepath_node_select(n, TRUE, FALSE);
1925        n_added ++;
1926        nl = g_list_remove(nl, t);
1927     }
1929     /** \todo fixme: adjust ? */
1930     sp_nodepath_update_handles(nodepath);
1932     if (n_added > 1) {
1933         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1934     } else if (n_added > 0) {
1935         sp_nodepath_update_repr(nodepath, _("Add node"));
1936     }
1938     sp_nodepath_update_statusbar(nodepath);
1941 /**
1942  * Select segment nearest to point
1943  */
1944 void
1945 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1947     if (!nodepath) {
1948         return;
1949     }
1951     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
1952     Geom::PathVector const &pathv = curve->get_pathvector();
1953     Geom::PathVectorPosition pvpos = Geom::nearestPoint(pathv, p);
1955     // calculate index for nodepath's representation.
1956     unsigned int segment_index = floor(pvpos.t) + 1;
1957     for (unsigned int i = 0; i < pvpos.path_nr; ++i) {
1958         segment_index += pathv[i].size() + 1;
1959         if (pathv[i].closed()) {
1960             segment_index += 1;
1961         }
1962     }
1964     curve->unref();
1966     //find segment to segment
1967     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(segment_index);
1969     //fixme: this can return NULL, so check before proceeding.
1970     g_return_if_fail(e != NULL);
1972     gboolean force = FALSE;
1973     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1974         force = TRUE;
1975     }
1976     sp_nodepath_node_select(e, (gboolean) toggle, force);
1977     if (e->p.other)
1978         sp_nodepath_node_select(e->p.other, TRUE, force);
1980     sp_nodepath_update_handles(nodepath);
1982     sp_nodepath_update_statusbar(nodepath);
1985 /**
1986  * Add a node nearest to point
1987  */
1988 void
1989 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1991     if (!nodepath) {
1992         return;
1993     }
1995     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
1996     Geom::PathVector const &pathv = curve->get_pathvector();
1997     Geom::PathVectorPosition pvpos = Geom::nearestPoint(pathv, p);
1999     // calculate index for nodepath's representation.
2000     double int_part;
2001     double t = std::modf(pvpos.t, &int_part);
2002     unsigned int segment_index = (unsigned int)int_part + 1;
2003     for (unsigned int i = 0; i < pvpos.path_nr; ++i) {
2004         segment_index += pathv[i].size() + 1;
2005         if (pathv[i].closed()) {
2006             segment_index += 1;
2007         }
2008     }
2010     curve->unref();
2012     //find segment to split
2013     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(segment_index);
2015     //don't know why but t seems to flip for lines
2016     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2017         t = 1.0 - t;
2018     }
2020     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2021     sp_nodepath_node_select(n, FALSE, TRUE);
2023     /* fixme: adjust ? */
2024     sp_nodepath_update_handles(nodepath);
2026     sp_nodepath_update_repr(nodepath, _("Add node"));
2028     sp_nodepath_update_statusbar(nodepath);
2031 /*
2032  * Adjusts a segment so that t moves by a certain delta for dragging
2033  * converts lines to curves
2034  *
2035  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2036  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2037  */
2038 void
2039 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
2041     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
2043     //fixme: e and e->p can be NULL, so check for those before proceeding
2044     g_return_if_fail(e != NULL);
2045     g_return_if_fail(&e->p != NULL);
2047     /* feel good is an arbitrary parameter that distributes the delta between handles
2048      * if t of the drag point is less than 1/6 distance form the endpoint only
2049      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2050      */
2051     double feel_good;
2052     if (t <= 1.0 / 6.0)
2053         feel_good = 0;
2054     else if (t <= 0.5)
2055         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2056     else if (t <= 5.0 / 6.0)
2057         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2058     else
2059         feel_good = 1;
2061     //if we're dragging a line convert it to a curve
2062     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2063         sp_nodepath_set_line_type(e, NR_CURVETO);
2064     }
2066     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2067     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2068     e->p.other->n.pos += offsetcoord0;
2069     e->p.pos += offsetcoord1;
2071     // adjust handles of adjacent nodes where necessary
2072     sp_node_adjust_handle(e,1);
2073     sp_node_adjust_handle(e->p.other,-1);
2075     sp_nodepath_update_handles(e->subpath->nodepath);
2077     update_object(e->subpath->nodepath);
2079     sp_nodepath_update_statusbar(e->subpath->nodepath);
2083 /**
2084  * Call sp_nodepath_break() for all selected segments.
2085  */
2086 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2088     if (!nodepath) return;
2090     GList *tempin = g_list_copy(nodepath->selected);
2091     GList *temp = NULL;
2092     for (GList *l = tempin; l != NULL; l = l->next) {
2093        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2094        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2095         if (nn == NULL) continue; // no break, no new node
2096         temp = g_list_prepend(temp, nn);
2097     }
2098     g_list_free(tempin);
2100     if (temp) {
2101         sp_nodepath_deselect(nodepath);
2102     }
2103     for (GList *l = temp; l != NULL; l = l->next) {
2104         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2105     }
2107     sp_nodepath_update_handles(nodepath);
2109     sp_nodepath_update_repr(nodepath, _("Break path"));
2112 /**
2113  * Duplicate the selected node(s).
2114  */
2115 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2117     if (!nodepath) {
2118         return;
2119     }
2121     GList *temp = NULL;
2122     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2123        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2124        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2125         if (nn == NULL) continue; // could not duplicate
2126         temp = g_list_prepend(temp, nn);
2127     }
2129     if (temp) {
2130         sp_nodepath_deselect(nodepath);
2131     }
2132     for (GList *l = temp; l != NULL; l = l->next) {
2133         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2134     }
2136     sp_nodepath_update_handles(nodepath);
2138     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2141 /**
2142  *  Internal function to join two nodes by merging them into one.
2143  */
2144 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2146     /* a and b are endpoints */
2148     // if one of the two nodes is mouseovered, fix its position
2149     NR::Point c;
2150     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2151         c = a->pos;
2152     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2153         c = b->pos;
2154     } else {
2155         // otherwise, move joined node to the midpoint
2156         c = (a->pos + b->pos) / 2;
2157     }
2159     if (a->subpath == b->subpath) {
2160        Inkscape::NodePath::SubPath *sp = a->subpath;
2161         sp_nodepath_subpath_close(sp);
2162         sp_node_moveto (sp->first, c);
2164         sp_nodepath_update_handles(sp->nodepath);
2165         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2166         return;
2167     }
2169     /* a and b are separate subpaths */
2170     Inkscape::NodePath::SubPath *sa = a->subpath;
2171     Inkscape::NodePath::SubPath *sb = b->subpath;
2172     NR::Point p;
2173     Inkscape::NodePath::Node *n;
2174     NRPathcode code;
2175     if (a == sa->first) {
2176         // we will now reverse sa, so that a is its last node, not first, and drop that node
2177         p = sa->first->n.pos;
2178         code = (NRPathcode)sa->first->n.other->code;
2179         // create new subpath
2180        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2181        // create a first moveto node on it
2182         n = sa->last;
2183         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2184         n = n->p.other;
2185         if (n == sa->first) n = NULL;
2186         while (n) {
2187             // copy the rest of the nodes from sa to t, going backwards
2188             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2189             n = n->p.other;
2190             if (n == sa->first) n = NULL;
2191         }
2192         // replace sa with t
2193         sp_nodepath_subpath_destroy(sa);
2194         sa = t;
2195     } else if (a == sa->last) {
2196         // a is already last, just drop it
2197         p = sa->last->p.pos;
2198         code = (NRPathcode)sa->last->code;
2199         sp_nodepath_node_destroy(sa->last);
2200     } else {
2201         code = NR_END;
2202         g_assert_not_reached();
2203     }
2205     if (b == sb->first) {
2206         // copy all nodes from b to a, forward 
2207         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2208         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2209             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2210         }
2211     } else if (b == sb->last) {
2212         // copy all nodes from b to a, backward 
2213         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2214         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2215             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2216         }
2217     } else {
2218         g_assert_not_reached();
2219     }
2220     /* and now destroy sb */
2222     sp_nodepath_subpath_destroy(sb);
2224     sp_nodepath_update_handles(sa->nodepath);
2226     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2228     sp_nodepath_update_statusbar(nodepath);
2231 /**
2232  *  Internal function to join two nodes by adding a segment between them.
2233  */
2234 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2236     if (a->subpath == b->subpath) {
2237        Inkscape::NodePath::SubPath *sp = a->subpath;
2239         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2240         sp->closed = TRUE;
2242         sp->first->p.other = sp->last;
2243         sp->last->n.other  = sp->first;
2245         sp_node_handle_mirror_p_to_n(sp->last);
2246         sp_node_handle_mirror_n_to_p(sp->first);
2248         sp->first->code = sp->last->code;
2249         sp->first       = sp->last;
2251         sp_nodepath_update_handles(sp->nodepath);
2253         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2255         return;
2256     }
2258     /* a and b are separate subpaths */
2259     Inkscape::NodePath::SubPath *sa = a->subpath;
2260     Inkscape::NodePath::SubPath *sb = b->subpath;
2262     Inkscape::NodePath::Node *n;
2263     NR::Point p;
2264     NRPathcode code;
2265     if (a == sa->first) {
2266         code = (NRPathcode) sa->first->n.other->code;
2267        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2268         n = sa->last;
2269         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2270         for (n = n->p.other; n != NULL; n = n->p.other) {
2271             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2272         }
2273         sp_nodepath_subpath_destroy(sa);
2274         sa = t;
2275     } else if (a == sa->last) {
2276         code = (NRPathcode)sa->last->code;
2277     } else {
2278         code = NR_END;
2279         g_assert_not_reached();
2280     }
2282     if (b == sb->first) {
2283         n = sb->first;
2284         sp_node_handle_mirror_p_to_n(sa->last);
2285         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2286         sp_node_handle_mirror_n_to_p(sa->last);
2287         for (n = n->n.other; n != NULL; n = n->n.other) {
2288             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2289         }
2290     } else if (b == sb->last) {
2291         n = sb->last;
2292         sp_node_handle_mirror_p_to_n(sa->last);
2293         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2294         sp_node_handle_mirror_n_to_p(sa->last);
2295         for (n = n->p.other; n != NULL; n = n->p.other) {
2296             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2297         }
2298     } else {
2299         g_assert_not_reached();
2300     }
2301     /* and now destroy sb */
2303     sp_nodepath_subpath_destroy(sb);
2305     sp_nodepath_update_handles(sa->nodepath);
2307     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2310 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2312 /**
2313  * Internal function to handle joining two nodes.
2314  */
2315 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2317     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2319     if (g_list_length(nodepath->selected) != 2) {
2320         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2321         return;
2322     }
2324     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2325     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2327     g_assert(a != b);
2328     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2329         // someone tried to join an orphan node (i.e. a single-node subpath).
2330         // this is not worth an error message, just fail silently.
2331         return;
2332     }
2334     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2335         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2336         return;
2337     }
2339     switch(mode) {
2340         case NODE_JOIN_ENDPOINTS:
2341             do_node_selected_join(nodepath, a, b);
2342             break;
2343         case NODE_JOIN_SEGMENT:
2344             do_node_selected_join_segment(nodepath, a, b);
2345             break;
2346     }
2349 /**
2350  *  Join two nodes by merging them into one.
2351  */
2352 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2354     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2357 /**
2358  *  Join two nodes by adding a segment between them.
2359  */
2360 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2362     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2365 /**
2366  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2367  */
2368 void sp_node_delete_preserve(GList *nodes_to_delete)
2370     GSList *nodepaths = NULL;
2372     while (nodes_to_delete) {
2373         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2374         Inkscape::NodePath::SubPath *sp = node->subpath;
2375         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2376         Inkscape::NodePath::Node *sample_cursor = NULL;
2377         Inkscape::NodePath::Node *sample_end = NULL;
2378         Inkscape::NodePath::Node *delete_cursor = node;
2379         bool just_delete = false;
2381         //find the start of this contiguous selection
2382         //move left to the first node that is not selected
2383         //or the start of the non-closed path
2384         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2385             delete_cursor = curr;
2386         }
2388         //just delete at the beginning of an open path
2389         if (!delete_cursor->p.other) {
2390             sample_cursor = delete_cursor;
2391             just_delete = true;
2392         } else {
2393             sample_cursor = delete_cursor->p.other;
2394         }
2396         //calculate points for each segment
2397         int rate = 5;
2398         float period = 1.0 / rate;
2399         std::vector<NR::Point> data;
2400         if (!just_delete) {
2401             data.push_back(sample_cursor->pos);
2402             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2403                 //just delete at the end of an open path
2404                 if (!sp->closed && curr == sp->last) {
2405                     just_delete = true;
2406                     break;
2407                 }
2409                 //sample points on the contiguous selected segment
2410                 NR::Point *bez;
2411                 bez = new NR::Point [4];
2412                 bez[0] = curr->pos;
2413                 bez[1] = curr->n.pos;
2414                 bez[2] = curr->n.other->p.pos;
2415                 bez[3] = curr->n.other->pos;
2416                 for (int i=1; i<rate; i++) {
2417                     gdouble t = i * period;
2418                     NR::Point p = bezier_pt(3, bez, t);
2419                     data.push_back(p);
2420                 }
2421                 data.push_back(curr->n.other->pos);
2423                 sample_end = curr->n.other;
2424                 //break if we've come full circle or hit the end of the selection
2425                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2426                     break;
2427                 }
2428             }
2429         }
2431         if (!just_delete) {
2432             //calculate the best fitting single segment and adjust the endpoints
2433             NR::Point *adata;
2434             adata = new NR::Point [data.size()];
2435             copy(data.begin(), data.end(), adata);
2437             NR::Point *bez;
2438             bez = new NR::Point [4];
2439             //would decreasing error create a better fitting approximation?
2440             gdouble error = 1.0;
2441             gint ret;
2442             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2444             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2445             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2446             //the resulting nodes behave as expected.
2447             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2448                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2449             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2450                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2452             //adjust endpoints
2453             sample_cursor->n.pos = bez[1];
2454             sample_end->p.pos = bez[2];
2455         }
2457         //destroy this contiguous selection
2458         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2459             Inkscape::NodePath::Node *temp = delete_cursor;
2460             if (delete_cursor->n.other == delete_cursor) {
2461                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2462                 delete_cursor = NULL;
2463             } else {
2464                 delete_cursor = delete_cursor->n.other;
2465             }
2466             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2467             sp_nodepath_node_destroy(temp);
2468         }
2470         sp_nodepath_update_handles(nodepath);
2472         if (!g_slist_find(nodepaths, nodepath))
2473             nodepaths = g_slist_prepend (nodepaths, nodepath);
2474     }
2476     for (GSList *i = nodepaths; i; i = i->next) {
2477         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2478         // different nodepaths will give us one undo event per nodepath
2479         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2481         // if the entire nodepath is removed, delete the selected object.
2482         if (nodepath->subpaths == NULL ||
2483             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2484             //at least 2
2485             sp_nodepath_get_node_count(nodepath) < 2) {
2486             SPDocument *document = sp_desktop_document (nodepath->desktop);
2487             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2488             //delete this nodepath's object, not the entire selection! (though at this time, this
2489             //does not matter)
2490             sp_selection_delete();
2491             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2492                               _("Delete nodes"));
2493         } else {
2494             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2495             sp_nodepath_update_statusbar(nodepath);
2496         }
2497     }
2499     g_slist_free (nodepaths);
2502 /**
2503  * Delete one or more selected nodes.
2504  */
2505 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2507     if (!nodepath) return;
2508     if (!nodepath->selected) return;
2510     /** \todo fixme: do it the right way */
2511     while (nodepath->selected) {
2512        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2513         sp_nodepath_node_destroy(node);
2514     }
2517     //clean up the nodepath (such as for trivial subpaths)
2518     sp_nodepath_cleanup(nodepath);
2520     sp_nodepath_update_handles(nodepath);
2522     // if the entire nodepath is removed, delete the selected object.
2523     if (nodepath->subpaths == NULL ||
2524         sp_nodepath_get_node_count(nodepath) < 2) {
2525         SPDocument *document = sp_desktop_document (nodepath->desktop);
2526         sp_selection_delete();
2527         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2528                           _("Delete nodes"));
2529         return;
2530     }
2532     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2534     sp_nodepath_update_statusbar(nodepath);
2537 /**
2538  * Delete one or more segments between two selected nodes.
2539  * This is the code for 'split'.
2540  */
2541 void
2542 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2544    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2545    Inkscape::NodePath::Node *curr, *next;     //Iterators
2547     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2549     if (g_list_length(nodepath->selected) != 2) {
2550         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2551                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2552         return;
2553     }
2555     //Selected nodes, not inclusive
2556    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2557    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2559     if ( ( a==b)                       ||  //same node
2560          (a->subpath  != b->subpath )  ||  //not the same path
2561          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2562          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2563     {
2564         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2565                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2566         return;
2567     }
2569     //###########################################
2570     //# BEGIN EDITS
2571     //###########################################
2572     //##################################
2573     //# CLOSED PATH
2574     //##################################
2575     if (a->subpath->closed) {
2578         gboolean reversed = FALSE;
2580         //Since we can go in a circle, we need to find the shorter distance.
2581         //  a->b or b->a
2582         start = end = NULL;
2583         int distance    = 0;
2584         int minDistance = 0;
2585         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2586             if (curr==b) {
2587                 //printf("a to b:%d\n", distance);
2588                 start = a;//go from a to b
2589                 end   = b;
2590                 minDistance = distance;
2591                 //printf("A to B :\n");
2592                 break;
2593             }
2594             distance++;
2595         }
2597         //try again, the other direction
2598         distance = 0;
2599         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2600             if (curr==a) {
2601                 //printf("b to a:%d\n", distance);
2602                 if (distance < minDistance) {
2603                     start    = b;  //we go from b to a
2604                     end      = a;
2605                     reversed = TRUE;
2606                     //printf("B to A\n");
2607                 }
2608                 break;
2609             }
2610             distance++;
2611         }
2614         //Copy everything from 'end' to 'start' to a new subpath
2615        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2616         for (curr=end ; curr ; curr=curr->n.other) {
2617             NRPathcode code = (NRPathcode) curr->code;
2618             if (curr == end)
2619                 code = NR_MOVETO;
2620             sp_nodepath_node_new(t, NULL,
2621                                  (Inkscape::NodePath::NodeType)curr->type, code,
2622                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2623             if (curr == start)
2624                 break;
2625         }
2626         sp_nodepath_subpath_destroy(a->subpath);
2629     }
2633     //##################################
2634     //# OPEN PATH
2635     //##################################
2636     else {
2638         //We need to get the direction of the list between A and B
2639         //Can we walk from a to b?
2640         start = end = NULL;
2641         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2642             if (curr==b) {
2643                 start = a;  //did it!  we go from a to b
2644                 end   = b;
2645                 //printf("A to B\n");
2646                 break;
2647             }
2648         }
2649         if (!start) {//didn't work?  let's try the other direction
2650             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2651                 if (curr==a) {
2652                     start = b;  //did it!  we go from b to a
2653                     end   = a;
2654                     //printf("B to A\n");
2655                     break;
2656                 }
2657             }
2658         }
2659         if (!start) {
2660             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2661                                                      _("Cannot find path between nodes."));
2662             return;
2663         }
2667         //Copy everything after 'end' to a new subpath
2668        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2669         for (curr=end ; curr ; curr=curr->n.other) {
2670             NRPathcode code = (NRPathcode) curr->code;
2671             if (curr == end)
2672                 code = NR_MOVETO;
2673             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2674                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2675         }
2677         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2678         for (curr = start->n.other ; curr  ; curr=next) {
2679             next = curr->n.other;
2680             sp_nodepath_node_destroy(curr);
2681         }
2683     }
2684     //###########################################
2685     //# END EDITS
2686     //###########################################
2688     //clean up the nodepath (such as for trivial subpaths)
2689     sp_nodepath_cleanup(nodepath);
2691     sp_nodepath_update_handles(nodepath);
2693     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2695     sp_nodepath_update_statusbar(nodepath);
2698 /**
2699  * Call sp_nodepath_set_line() for all selected segments.
2700  */
2701 void
2702 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2704     if (nodepath == NULL) return;
2706     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2707        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2708         g_assert(n->selected);
2709         if (n->p.other && n->p.other->selected) {
2710             sp_nodepath_set_line_type(n, code);
2711         }
2712     }
2714     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2717 /**
2718  * Call sp_nodepath_convert_node_type() for all selected nodes.
2719  */
2720 void
2721 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2723     if (nodepath == NULL) return;
2725     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2727     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2728         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2729     }
2731     sp_nodepath_update_repr(nodepath, _("Change node type"));
2734 /**
2735  * Change select status of node, update its own and neighbour handles.
2736  */
2737 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2739     node->selected = selected;
2741     if (selected) {
2742         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2743         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2744         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2745         sp_knot_update_ctrl(node->knot);
2746     } else {
2747         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2748         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2749         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2750         sp_knot_update_ctrl(node->knot);
2751     }
2753     sp_node_update_handles(node);
2754     if (node->n.other) sp_node_update_handles(node->n.other);
2755     if (node->p.other) sp_node_update_handles(node->p.other);
2758 /**
2759 \brief Select a node
2760 \param node     The node to select
2761 \param incremental   If true, add to selection, otherwise deselect others
2762 \param override   If true, always select this node, otherwise toggle selected status
2763 */
2764 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2766     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2768     if (incremental) {
2769         if (override) {
2770             if (!g_list_find(nodepath->selected, node)) {
2771                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2772             }
2773             sp_node_set_selected(node, TRUE);
2774         } else { // toggle
2775             if (node->selected) {
2776                 g_assert(g_list_find(nodepath->selected, node));
2777                 nodepath->selected = g_list_remove(nodepath->selected, node);
2778             } else {
2779                 g_assert(!g_list_find(nodepath->selected, node));
2780                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2781             }
2782             sp_node_set_selected(node, !node->selected);
2783         }
2784     } else {
2785         sp_nodepath_deselect(nodepath);
2786         nodepath->selected = g_list_prepend(nodepath->selected, node);
2787         sp_node_set_selected(node, TRUE);
2788     }
2790     sp_nodepath_update_statusbar(nodepath);
2794 /**
2795 \brief Deselect all nodes in the nodepath
2796 */
2797 void
2798 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2800     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2802     while (nodepath->selected) {
2803         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2804         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2805     }
2806     sp_nodepath_update_statusbar(nodepath);
2809 /**
2810 \brief Select or invert selection of all nodes in the nodepath
2811 */
2812 void
2813 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2815     if (!nodepath) return;
2817     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2818        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2819         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2820            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2821            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2822         }
2823     }
2826 /**
2827  * If nothing selected, does the same as sp_nodepath_select_all();
2828  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2829  * (i.e., similar to "select all in layer", with the "selected" subpaths
2830  * being treated as "layers" in the path).
2831  */
2832 void
2833 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2835     if (!nodepath) return;
2837     if (g_list_length (nodepath->selected) == 0) {
2838         sp_nodepath_select_all (nodepath, invert);
2839         return;
2840     }
2842     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2843     GSList *subpaths = NULL;
2845     for (GList *l = copy; l != NULL; l = l->next) {
2846         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2847         Inkscape::NodePath::SubPath *subpath = n->subpath;
2848         if (!g_slist_find (subpaths, subpath))
2849             subpaths = g_slist_prepend (subpaths, subpath);
2850     }
2852     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2853         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2854         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2855             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2856             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2857         }
2858     }
2860     g_slist_free (subpaths);
2861     g_list_free (copy);
2864 /**
2865  * \brief Select the node after the last selected; if none is selected,
2866  * select the first within path.
2867  */
2868 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2870     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2872    Inkscape::NodePath::Node *last = NULL;
2873     if (nodepath->selected) {
2874         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2875            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2876             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2877             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2878                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2879                 if (node->selected) {
2880                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2881                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2882                             if (spl->next) { // there's a next subpath
2883                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2884                                 last = subpath_next->first;
2885                             } else if (spl->prev) { // there's a previous subpath
2886                                 last = NULL; // to be set later to the first node of first subpath
2887                             } else {
2888                                 last = node->n.other;
2889                             }
2890                         } else {
2891                             last = node->n.other;
2892                         }
2893                     } else {
2894                         if (node->n.other) {
2895                             last = node->n.other;
2896                         } else {
2897                             if (spl->next) { // there's a next subpath
2898                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2899                                 last = subpath_next->first;
2900                             } else if (spl->prev) { // there's a previous subpath
2901                                 last = NULL; // to be set later to the first node of first subpath
2902                             } else {
2903                                 last = (Inkscape::NodePath::Node *) subpath->first;
2904                             }
2905                         }
2906                     }
2907                 }
2908             }
2909         }
2910         sp_nodepath_deselect(nodepath);
2911     }
2913     if (last) { // there's at least one more node after selected
2914         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2915     } else { // no more nodes, select the first one in first subpath
2916        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2917         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2918     }
2921 /**
2922  * \brief Select the node before the first selected; if none is selected,
2923  * select the last within path
2924  */
2925 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2927     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2929    Inkscape::NodePath::Node *last = NULL;
2930     if (nodepath->selected) {
2931         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2932            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2933             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2934                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2935                 if (node->selected) {
2936                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2937                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2938                             if (spl->prev) { // there's a prev subpath
2939                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2940                                 last = subpath_prev->last;
2941                             } else if (spl->next) { // there's a next subpath
2942                                 last = NULL; // to be set later to the last node of last subpath
2943                             } else {
2944                                 last = node->p.other;
2945                             }
2946                         } else {
2947                             last = node->p.other;
2948                         }
2949                     } else {
2950                         if (node->p.other) {
2951                             last = node->p.other;
2952                         } else {
2953                             if (spl->prev) { // there's a prev subpath
2954                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2955                                 last = subpath_prev->last;
2956                             } else if (spl->next) { // there's a next subpath
2957                                 last = NULL; // to be set later to the last node of last subpath
2958                             } else {
2959                                 last = (Inkscape::NodePath::Node *) subpath->last;
2960                             }
2961                         }
2962                     }
2963                 }
2964             }
2965         }
2966         sp_nodepath_deselect(nodepath);
2967     }
2969     if (last) { // there's at least one more node before selected
2970         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2971     } else { // no more nodes, select the last one in last subpath
2972         GList *spl = g_list_last(nodepath->subpaths);
2973        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2974         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2975     }
2978 /**
2979  * \brief Select all nodes that are within the rectangle.
2980  */
2981 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2983     if (!incremental) {
2984         sp_nodepath_deselect(nodepath);
2985     }
2987     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2988        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2989         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2990            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2992             if (b.contains(node->pos)) {
2993                 sp_nodepath_node_select(node, TRUE, TRUE);
2994             }
2995         }
2996     }
3000 void
3001 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3003     g_assert (n);
3004     g_assert (nodepath);
3005     g_assert (n->subpath->nodepath == nodepath);
3007     if (g_list_length (nodepath->selected) == 0) {
3008         if (grow > 0) {
3009             sp_nodepath_node_select(n, TRUE, TRUE);
3010         }
3011         return;
3012     }
3014     if (g_list_length (nodepath->selected) == 1) {
3015         if (grow < 0) {
3016             sp_nodepath_deselect (nodepath);
3017             return;
3018         }
3019     }
3021         double n_sel_range = 0, p_sel_range = 0;
3022             Inkscape::NodePath::Node *farthest_n_node = n;
3023             Inkscape::NodePath::Node *farthest_p_node = n;
3025         // Calculate ranges
3026         {
3027             double n_range = 0, p_range = 0;
3028             bool n_going = true, p_going = true;
3029             Inkscape::NodePath::Node *n_node = n;
3030             Inkscape::NodePath::Node *p_node = n;
3031             do {
3032                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3033                 if (n_node && n_going)
3034                     n_node = n_node->n.other;
3035                 if (n_node == NULL) {
3036                     n_going = false;
3037                 } else {
3038                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3039                     if (n_node->selected) {
3040                         n_sel_range = n_range;
3041                         farthest_n_node = n_node;
3042                     }
3043                     if (n_node == p_node) {
3044                         n_going = false;
3045                         p_going = false;
3046                     }
3047                 }
3048                 if (p_node && p_going)
3049                     p_node = p_node->p.other;
3050                 if (p_node == NULL) {
3051                     p_going = false;
3052                 } else {
3053                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3054                     if (p_node->selected) {
3055                         p_sel_range = p_range;
3056                         farthest_p_node = p_node;
3057                     }
3058                     if (p_node == n_node) {
3059                         n_going = false;
3060                         p_going = false;
3061                     }
3062                 }
3063             } while (n_going || p_going);
3064         }
3066     if (grow > 0) {
3067         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3068                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3069         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3070                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3071         }
3072     } else {
3073         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3074                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3075         } else if (farthest_p_node && farthest_p_node->selected) {
3076                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3077         }
3078     }
3081 void
3082 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3084     g_assert (n);
3085     g_assert (nodepath);
3086     g_assert (n->subpath->nodepath == nodepath);
3088     if (g_list_length (nodepath->selected) == 0) {
3089         if (grow > 0) {
3090             sp_nodepath_node_select(n, TRUE, TRUE);
3091         }
3092         return;
3093     }
3095     if (g_list_length (nodepath->selected) == 1) {
3096         if (grow < 0) {
3097             sp_nodepath_deselect (nodepath);
3098             return;
3099         }
3100     }
3102     Inkscape::NodePath::Node *farthest_selected = NULL;
3103     double farthest_dist = 0;
3105     Inkscape::NodePath::Node *closest_unselected = NULL;
3106     double closest_dist = NR_HUGE;
3108     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3109        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3110         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3111            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3112            if (node == n)
3113                continue;
3114            if (node->selected) {
3115                if (NR::L2(node->pos - n->pos) > farthest_dist) {
3116                    farthest_dist = NR::L2(node->pos - n->pos);
3117                    farthest_selected = node;
3118                }
3119            } else {
3120                if (NR::L2(node->pos - n->pos) < closest_dist) {
3121                    closest_dist = NR::L2(node->pos - n->pos);
3122                    closest_unselected = node;
3123                }
3124            }
3125         }
3126     }
3128     if (grow > 0) {
3129         if (closest_unselected) {
3130             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3131         }
3132     } else {
3133         if (farthest_selected) {
3134             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3135         }
3136     }
3140 /**
3141 \brief  Saves all nodes' and handles' current positions in their origin members
3142 */
3143 void
3144 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3146     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3147        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3148         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3149            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3150            n->origin = n->pos;
3151            n->p.origin = n->p.pos;
3152            n->n.origin = n->n.pos;
3153         }
3154     }
3157 /**
3158 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3159 */
3160 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3162     if (!nodepath->selected) {
3163         return NULL;
3164     }
3166     GList *r = NULL;
3167     guint i = 0;
3168     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3169        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3170         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3171            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3172             i++;
3173             if (node->selected) {
3174                 r = g_list_append(r, GINT_TO_POINTER(i));
3175             }
3176         }
3177     }
3178     return r;
3181 /**
3182 \brief  Restores selection by selecting nodes whose positions are in the list
3183 */
3184 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3186     sp_nodepath_deselect(nodepath);
3188     guint i = 0;
3189     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3190        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3191         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3192            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3193             i++;
3194             if (g_list_find(r, GINT_TO_POINTER(i))) {
3195                 sp_nodepath_node_select(node, TRUE, TRUE);
3196             }
3197         }
3198     }
3202 /**
3203 \brief Adjusts handle according to node type and line code.
3204 */
3205 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3207     g_assert(node);
3209    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3210    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3212    // nothing to do if we are an end node
3213     if (me->other == NULL) return;
3214     if (other->other == NULL) return;
3216     // nothing to do if we are a cusp node
3217     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3219     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3220     NRPathcode mecode;
3221     if (which_adjust == 1) {
3222         mecode = (NRPathcode)me->other->code;
3223     } else {
3224         mecode = (NRPathcode)node->code;
3225     }
3226     if (mecode == NR_LINETO) return;
3228     if (sp_node_side_is_line(node, other)) {
3229         // other is a line, and we are either smooth or symm
3230        Inkscape::NodePath::Node *othernode = other->other;
3231         double len = NR::L2(me->pos - node->pos);
3232         NR::Point delta = node->pos - othernode->pos;
3233         double linelen = NR::L2(delta);
3234         if (linelen < 1e-18)
3235             return;
3236         me->pos = node->pos + (len / linelen)*delta;
3237         return;
3238     }
3240     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3241         // symmetrize 
3242         me->pos = 2 * node->pos - other->pos;
3243         return;
3244     } else {
3245         // smoothify
3246         double len = NR::L2(me->pos - node->pos);
3247         NR::Point delta = other->pos - node->pos;
3248         double otherlen = NR::L2(delta);
3249         if (otherlen < 1e-18) return;
3250         me->pos = node->pos - (len / otherlen) * delta;
3251     }
3254 /**
3255  \brief Adjusts both handles according to node type and line code
3256  */
3257 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3259     g_assert(node);
3261     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3263     /* we are either smooth or symm */
3265     if (node->p.other == NULL) return;
3266     if (node->n.other == NULL) return;
3268     if (sp_node_side_is_line(node, &node->p)) {
3269         sp_node_adjust_handle(node, 1);
3270         return;
3271     }
3273     if (sp_node_side_is_line(node, &node->n)) {
3274         sp_node_adjust_handle(node, -1);
3275         return;
3276     }
3278     /* both are curves */
3279     NR::Point const delta( node->n.pos - node->p.pos );
3281     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3282         node->p.pos = node->pos - delta / 2;
3283         node->n.pos = node->pos + delta / 2;
3284         return;
3285     }
3287     /* We are smooth */
3288     double plen = NR::L2(node->p.pos - node->pos);
3289     if (plen < 1e-18) return;
3290     double nlen = NR::L2(node->n.pos - node->pos);
3291     if (nlen < 1e-18) return;
3292     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3293     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3296 /**
3297  * Node event callback.
3298  */
3299 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3301     gboolean ret = FALSE;
3302     switch (event->type) {
3303         case GDK_ENTER_NOTIFY:
3304             Inkscape::NodePath::Path::active_node = n;
3305             break;
3306         case GDK_LEAVE_NOTIFY:
3307             Inkscape::NodePath::Path::active_node = NULL;
3308             break;
3309         case GDK_SCROLL:
3310             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3311                 switch (event->scroll.direction) {
3312                     case GDK_SCROLL_UP:
3313                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3314                         break;
3315                     case GDK_SCROLL_DOWN:
3316                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3317                         break;
3318                     default:
3319                         break;
3320                 }
3321                 ret = TRUE;
3322             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3323                 switch (event->scroll.direction) {
3324                     case GDK_SCROLL_UP:
3325                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3326                         break;
3327                     case GDK_SCROLL_DOWN:
3328                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3329                         break;
3330                     default:
3331                         break;
3332                 }
3333                 ret = TRUE;
3334             }
3335             break;
3336         case GDK_KEY_PRESS:
3337             switch (get_group0_keyval (&event->key)) {
3338                 case GDK_space:
3339                     if (event->key.state & GDK_BUTTON1_MASK) {
3340                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3341                         stamp_repr(nodepath);
3342                         ret = TRUE;
3343                     }
3344                     break;
3345                 case GDK_Page_Up:
3346                     if (event->key.state & GDK_CONTROL_MASK) {
3347                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3348                     } else {
3349                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3350                     }
3351                     break;
3352                 case GDK_Page_Down:
3353                     if (event->key.state & GDK_CONTROL_MASK) {
3354                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3355                     } else {
3356                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3357                     }
3358                     break;
3359                 default:
3360                     break;
3361             }
3362             break;
3363         default:
3364             break;
3365     }
3367     return ret;
3370 /**
3371  * Handle keypress on node; directly called.
3372  */
3373 gboolean node_key(GdkEvent *event)
3375     Inkscape::NodePath::Path *np;
3377     // there is no way to verify nodes so set active_node to nil when deleting!!
3378     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3380     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3381         gint ret = FALSE;
3382         switch (get_group0_keyval (&event->key)) {
3383             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3384             case GDK_BackSpace:
3385                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3386                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3387                 sp_nodepath_update_repr(np, _("Delete node"));
3388                 Inkscape::NodePath::Path::active_node = NULL;
3389                 ret = TRUE;
3390                 break;
3391             case GDK_c:
3392                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3393                 ret = TRUE;
3394                 break;
3395             case GDK_s:
3396                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3397                 ret = TRUE;
3398                 break;
3399             case GDK_y:
3400                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3401                 ret = TRUE;
3402                 break;
3403             case GDK_b:
3404                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3405                 ret = TRUE;
3406                 break;
3407         }
3408         return ret;
3409     }
3410     return FALSE;
3413 /**
3414  * Mouseclick on node callback.
3415  */
3416 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3418    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3420     if (state & GDK_CONTROL_MASK) {
3421         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3423         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3424             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3425                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3426             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3427                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3428             } else {
3429                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3430             }
3431             sp_nodepath_update_repr(nodepath, _("Change node type"));
3432             sp_nodepath_update_statusbar(nodepath);
3434         } else { //ctrl+alt+click: delete node
3435             GList *node_to_delete = NULL;
3436             node_to_delete = g_list_append(node_to_delete, n);
3437             sp_node_delete_preserve(node_to_delete);
3438         }
3440     } else {
3441         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3442     }
3445 /**
3446  * Mouse grabbed node callback.
3447  */
3448 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3450    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3452     if (!n->selected) {
3453         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3454     }
3456     n->is_dragging = true;
3457     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3459     sp_nodepath_remember_origins (n->subpath->nodepath);
3462 /**
3463  * Mouse ungrabbed node callback.
3464  */
3465 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3467    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3469    n->dragging_out = NULL;
3470    n->is_dragging = false;
3471    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3473    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3476 /**
3477  * The point on a line, given by its angle, closest to the given point.
3478  * \param p  A point.
3479  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3480  * \param closest  Pointer to the point struct where the result is stored.
3481  * \todo FIXME: use dot product perhaps?
3482  */
3483 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3485     if (a == HUGE_VAL) { // vertical
3486         *closest = NR::Point(0, (*p)[NR::Y]);
3487     } else {
3488         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3489         (*closest)[NR::Y] = a * (*closest)[NR::X];
3490     }
3493 /**
3494  * Distance from the point to a line given by its angle.
3495  * \param p  A point.
3496  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3497  */
3498 static double point_line_distance(NR::Point *p, double a)
3500     NR::Point c;
3501     point_line_closest(p, a, &c);
3502     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]));
3505 /**
3506  * Callback for node "request" signal.
3507  * \todo fixme: This goes to "moved" event? (lauris)
3508  */
3509 static gboolean
3510 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3512     double yn, xn, yp, xp;
3513     double an, ap, na, pa;
3514     double d_an, d_ap, d_na, d_pa;
3515     gboolean collinear = FALSE;
3516     NR::Point c;
3517     NR::Point pr;
3519     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3521     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3523     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3524     if ( (!n->subpath->nodepath->straight_path) &&
3525          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3526            || n->dragging_out ) )
3527     {
3528        NR::Point mouse = (*p);
3530        if (!n->dragging_out) {
3531            // This is the first drag-out event; find out which handle to drag out
3532            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3533            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3535            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3536                return FALSE;
3538            Inkscape::NodePath::NodeSide *opposite;
3539            if (appr_p > appr_n) { // closer to p
3540                n->dragging_out = &n->p;
3541                opposite = &n->n;
3542                n->code = NR_CURVETO;
3543            } else if (appr_p < appr_n) { // closer to n
3544                n->dragging_out = &n->n;
3545                opposite = &n->p;
3546                n->n.other->code = NR_CURVETO;
3547            } else { // p and n nodes are the same
3548                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3549                    n->dragging_out = &n->p;
3550                    opposite = &n->n;
3551                    n->code = NR_CURVETO;
3552                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3553                    n->dragging_out = &n->n;
3554                    opposite = &n->p;
3555                    n->n.other->code = NR_CURVETO;
3556                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3557                    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);
3558                    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);
3559                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3560                        n->dragging_out = &n->n;
3561                        opposite = &n->p;
3562                        n->n.other->code = NR_CURVETO;
3563                    } else { // closer to other's n handle
3564                        n->dragging_out = &n->p;
3565                        opposite = &n->n;
3566                        n->code = NR_CURVETO;
3567                    }
3568                }
3569            }
3571            // if there's another handle, make sure the one we drag out starts parallel to it
3572            if (opposite->pos != n->pos) {
3573                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3574            }
3576            // knots might not be created yet!
3577            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3578            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3579        }
3581        // pass this on to the handle-moved callback
3582        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3583        sp_node_update_handles(n);
3584        return TRUE;
3585    }
3587     if (state & GDK_CONTROL_MASK) { // constrained motion
3589         // calculate relative distances of handles
3590         // n handle:
3591         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3592         xn = n->n.pos[NR::X] - n->pos[NR::X];
3593         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3594         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3595             if (n->n.other) { // if there is the next point
3596                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3597                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3598                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3599             }
3600         }
3601         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3602         if (yn < 0) { xn = -xn; yn = -yn; }
3604         // p handle:
3605         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3606         xp = n->p.pos[NR::X] - n->pos[NR::X];
3607         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3608         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3609             if (n->p.other) {
3610                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3611                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3612                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3613             }
3614         }
3615         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3616         if (yp < 0) { xp = -xp; yp = -yp; }
3618         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3619             // sliding on handles, only if at least one of the handles is non-vertical
3620             // (otherwise it's the same as ctrl+drag anyway)
3622             // calculate angles of the handles
3623             if (xn == 0) {
3624                 if (yn == 0) { // no handle, consider it the continuation of the other one
3625                     an = 0;
3626                     collinear = TRUE;
3627                 }
3628                 else an = 0; // vertical; set the angle to horizontal
3629             } else an = yn/xn;
3631             if (xp == 0) {
3632                 if (yp == 0) { // no handle, consider it the continuation of the other one
3633                     ap = an;
3634                 }
3635                 else ap = 0; // vertical; set the angle to horizontal
3636             } else  ap = yp/xp;
3638             if (collinear) an = ap;
3640             // angles of the perpendiculars; HUGE_VAL means vertical
3641             if (an == 0) na = HUGE_VAL; else na = -1/an;
3642             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3644             // mouse point relative to the node's original pos
3645             pr = (*p) - n->origin;
3647             // distances to the four lines (two handles and two perpendiculars)
3648             d_an = point_line_distance(&pr, an);
3649             d_na = point_line_distance(&pr, na);
3650             d_ap = point_line_distance(&pr, ap);
3651             d_pa = point_line_distance(&pr, pa);
3653             // find out which line is the closest, save its closest point in c
3654             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3655                 point_line_closest(&pr, an, &c);
3656             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3657                 point_line_closest(&pr, ap, &c);
3658             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3659                 point_line_closest(&pr, na, &c);
3660             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3661                 point_line_closest(&pr, pa, &c);
3662             }
3664             // move the node to the closest point
3665             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3666                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3667                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y], 
3668                                             true);
3670         } else {  // constraining to hor/vert
3672             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3673                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3674                                                 (*p)[NR::X] - n->pos[NR::X], 
3675                                                 n->origin[NR::Y] - n->pos[NR::Y],
3676                                                 true, 
3677                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::X]));
3678             } else { // snap to vert
3679                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3680                                                 n->origin[NR::X] - n->pos[NR::X],
3681                                                 (*p)[NR::Y] - n->pos[NR::Y],
3682                                                 true,
3683                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::Y]));
3684             }
3685         }
3686     } else { // move freely
3687         if (n->is_dragging) {
3688             if (state & GDK_MOD1_MASK) { // sculpt
3689                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3690             } else {
3691                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3692                                             (*p)[NR::X] - n->pos[NR::X],
3693                                             (*p)[NR::Y] - n->pos[NR::Y],
3694                                             (state & GDK_SHIFT_MASK) == 0);
3695             }
3696         }
3697     }
3699     n->subpath->nodepath->desktop->scroll_to_point(p);
3701     return TRUE;
3704 /**
3705  * Node handle clicked callback.
3706  */
3707 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3709    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3711     if (state & GDK_CONTROL_MASK) { // "delete" handle
3712         if (n->p.knot == knot) {
3713             n->p.pos = n->pos;
3714         } else if (n->n.knot == knot) {
3715             n->n.pos = n->pos;
3716         }
3717         sp_node_update_handles(n);
3718         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3719         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3720         sp_nodepath_update_statusbar(nodepath);
3722     } else { // just select or add to selection, depending in Shift
3723         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3724     }
3727 /**
3728  * Node handle grabbed callback.
3729  */
3730 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3732    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3734     if (!n->selected) {
3735         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3736     }
3738     // remember the origin point of the handle
3739     if (n->p.knot == knot) {
3740         n->p.origin_radial = n->p.pos - n->pos;
3741     } else if (n->n.knot == knot) {
3742         n->n.origin_radial = n->n.pos - n->pos;
3743     } else {
3744         g_assert_not_reached();
3745     }
3747     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3750 /**
3751  * Node handle ungrabbed callback.
3752  */
3753 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3755    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3757     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3758     if (n->p.knot == knot) {
3759         n->p.origin_radial.a = 0;
3760         sp_knot_set_position(knot, &n->p.pos, state);
3761     } else if (n->n.knot == knot) {
3762         n->n.origin_radial.a = 0;
3763         sp_knot_set_position(knot, &n->n.pos, state);
3764     } else {
3765         g_assert_not_reached();
3766     }
3768     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3771 /**
3772  * Node handle "request" signal callback.
3773  */
3774 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3776     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3778     Inkscape::NodePath::NodeSide *me, *opposite;
3779     gint which;
3780     if (n->p.knot == knot) {
3781         me = &n->p;
3782         opposite = &n->n;
3783         which = -1;
3784     } else if (n->n.knot == knot) {
3785         me = &n->n;
3786         opposite = &n->p;
3787         which = 1;
3788     } else {
3789         me = opposite = NULL;
3790         which = 0;
3791         g_assert_not_reached();
3792     }
3794     SPDesktop *desktop = n->subpath->nodepath->desktop;
3795     SnapManager &m = desktop->namedview->snap_manager;
3796     m.setup(desktop, n->subpath->nodepath->item);
3797     Inkscape::SnappedPoint s;
3798     
3799     if ((state & GDK_SHIFT_MASK) != 0) {
3800         // We will not try to snap when the shift-key is pressed
3801         // so remove the old snap indicator and don't wait for it to time-out  
3802         desktop->snapindicator->remove_snappoint();     
3803     }
3805     Inkscape::NodePath::Node *othernode = opposite->other;
3806     if (othernode) {
3807         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3808             /* We are smooth node adjacent with line */
3809             NR::Point const delta = *p - n->pos;
3810             NR::Coord const len = NR::L2(delta);
3811             Inkscape::NodePath::Node *othernode = opposite->other;
3812             NR::Point const ndelta = n->pos - othernode->pos;
3813             NR::Coord const linelen = NR::L2(ndelta);
3814             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3815                 NR::Coord const scal = dot(delta, ndelta) / linelen;
3816                 (*p) = n->pos + (scal / linelen) * ndelta;
3817             }
3818             if ((state & GDK_SHIFT_MASK) == 0) {
3819                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta));
3820             }
3821         } else {
3822                 if ((state & GDK_SHIFT_MASK) == 0) {
3823                         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3824                 }
3825         }
3826     } else {
3827         if ((state & GDK_SHIFT_MASK) == 0) {
3828                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3829         }
3830     }
3831     
3832     s.getPoint(*p);
3833     
3834     sp_node_adjust_handle(n, -which);
3836     return FALSE;
3839 /**
3840  * Node handle moved callback.
3841  */
3842 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3844    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3846    Inkscape::NodePath::NodeSide *me;
3847    Inkscape::NodePath::NodeSide *other;
3848     if (n->p.knot == knot) {
3849         me = &n->p;
3850         other = &n->n;
3851     } else if (n->n.knot == knot) {
3852         me = &n->n;
3853         other = &n->p;
3854     } else {
3855         me = NULL;
3856         other = NULL;
3857         g_assert_not_reached();
3858     }
3860     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3861     Radial rme(me->pos - n->pos);
3862     Radial rother(other->pos - n->pos);
3863     Radial rnew(*p - n->pos);
3865     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3866         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3867         /* 0 interpreted as "no snapping". */
3869         // 1. Snap to the closest PI/snaps angle, starting from zero.
3870         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3872         // 2. Snap to the original angle, its opposite and perpendiculars
3873         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3874             /* The closest PI/2 angle, starting from original angle */
3875             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3877             // Snap to the closest.
3878             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3879                        ? a_snapped
3880                        : a_ortho );
3881         }
3883         // 3. Snap to the angle of the opposite line, if any
3884         Inkscape::NodePath::Node *othernode = other->other;
3885         if (othernode) {
3886             NR::Point other_to_snap(0,0);
3887             if (sp_node_side_is_line(n, other)) {
3888                 other_to_snap = othernode->pos - n->pos;
3889             } else {
3890                 other_to_snap = other->pos - n->pos;
3891             }
3892             if (NR::L2(other_to_snap) > 1e-3) {
3893                 Radial rother_to_snap(other_to_snap);
3894                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
3895                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
3897                 // Snap to the closest.
3898                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
3899                        ? a_snapped
3900                        : a_oppo );
3901             }
3902         }
3904         rnew.a = a_snapped;
3905     }
3907     if (state & GDK_MOD1_MASK) {
3908         // lock handle length
3909         rnew.r = me->origin_radial.r;
3910     }
3912     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3913         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3914         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3915         rother.a += rnew.a - rme.a;
3916         other->pos = NR::Point(rother) + n->pos;
3917         if (other->knot) {
3918             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3919             sp_knot_moveto(other->knot, &other->pos);
3920         }
3921     }
3923     me->pos = NR::Point(rnew) + n->pos;
3924     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3926     // move knot, but without emitting the signal:
3927     // we cannot emit a "moved" signal because we're now processing it
3928     sp_knot_moveto(me->knot, &(me->pos));
3930     update_object(n->subpath->nodepath);
3932     /* status text */
3933     SPDesktop *desktop = n->subpath->nodepath->desktop;
3934     if (!desktop) return;
3935     SPEventContext *ec = desktop->event_context;
3936     if (!ec) return;
3937     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3938     if (!mc) return;
3940     double degrees = 180 / M_PI * rnew.a;
3941     if (degrees > 180) degrees -= 360;
3942     if (degrees < -180) degrees += 360;
3943     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3944         degrees = angle_to_compass (degrees);
3946     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3948     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
3949          _("<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);
3951     g_string_free(length, TRUE);
3954 /**
3955  * Node handle event callback.
3956  */
3957 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3959     gboolean ret = FALSE;
3960     switch (event->type) {
3961         case GDK_KEY_PRESS:
3962             switch (get_group0_keyval (&event->key)) {
3963                 case GDK_space:
3964                     if (event->key.state & GDK_BUTTON1_MASK) {
3965                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3966                         stamp_repr(nodepath);
3967                         ret = TRUE;
3968                     }
3969                     break;
3970                 default:
3971                     break;
3972             }
3973             break;
3974         case GDK_ENTER_NOTIFY:
3975             // we use an experimentally determined threshold that seems to work fine
3976             if (NR::L2(n->pos - knot->pos) < 0.75)
3977                 Inkscape::NodePath::Path::active_node = n;
3978             break;
3979         case GDK_LEAVE_NOTIFY:
3980             // we use an experimentally determined threshold that seems to work fine
3981             if (NR::L2(n->pos - knot->pos) < 0.75)
3982                 Inkscape::NodePath::Path::active_node = NULL;
3983             break;
3984         default:
3985             break;
3986     }
3988     return ret;
3991 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3992                                  Radial &rme, Radial &rother, gboolean const both)
3994     rme.a += angle;
3995     if ( both
3996          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3997          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3998     {
3999         rother.a += angle;
4000     }
4003 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
4004                                         Radial &rme, Radial &rother, gboolean const both)
4006     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
4008     gdouble r;
4009     if ( both
4010          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4011          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4012     {
4013         r = MAX(rme.r, rother.r);
4014     } else {
4015         r = rme.r;
4016     }
4018     gdouble const weird_angle = atan2(norm_angle, r);
4019 /* Bulia says norm_angle is just the visible distance that the
4020  * object's end must travel on the screen.  Left as 'angle' for want of
4021  * a better name.*/
4023     rme.a += weird_angle;
4024     if ( both
4025          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4026          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4027     {
4028         rother.a += weird_angle;
4029     }
4032 /**
4033  * Rotate one node.
4034  */
4035 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4037     Inkscape::NodePath::NodeSide *me, *other;
4038     bool both = false;
4040     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4041     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4043     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4044         me = &(n->p);
4045         other = &(n->n);
4046     } else if (!n->p.other) {
4047         me = &(n->n);
4048         other = &(n->p);
4049     } else {
4050         if (which > 0) { // right handle
4051             if (xn > xp) {
4052                 me = &(n->n);
4053                 other = &(n->p);
4054             } else {
4055                 me = &(n->p);
4056                 other = &(n->n);
4057             }
4058         } else if (which < 0){ // left handle
4059             if (xn <= xp) {
4060                 me = &(n->n);
4061                 other = &(n->p);
4062             } else {
4063                 me = &(n->p);
4064                 other = &(n->n);
4065             }
4066         } else { // both handles
4067             me = &(n->n);
4068             other = &(n->p);
4069             both = true;
4070         }
4071     }
4073     Radial rme(me->pos - n->pos);
4074     Radial rother(other->pos - n->pos);
4076     if (screen) {
4077         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4078     } else {
4079         node_rotate_one_internal (*n, angle, rme, rother, both);
4080     }
4082     me->pos = n->pos + NR::Point(rme);
4084     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4085         other->pos =  n->pos + NR::Point(rother);
4086     }
4088     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4089     // so here we just move all the knots without emitting move signals, for speed
4090     sp_node_update_handles(n, false);
4093 /**
4094  * Rotate selected nodes.
4095  */
4096 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4098     if (!nodepath || !nodepath->selected) return;
4100     if (g_list_length(nodepath->selected) == 1) {
4101        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4102         node_rotate_one (n, angle, which, screen);
4103     } else {
4104        // rotate as an object:
4106         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4107         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4108         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4109             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4110             box.expandTo (n->pos); // contain all selected nodes
4111         }
4113         gdouble rot;
4114         if (screen) {
4115             gdouble const zoom = nodepath->desktop->current_zoom();
4116             gdouble const zmove = angle / zoom;
4117             gdouble const r = NR::L2(box.max() - box.midpoint());
4118             rot = atan2(zmove, r);
4119         } else {
4120             rot = angle;
4121         }
4123         NR::Point rot_center;
4124         if (Inkscape::NodePath::Path::active_node == NULL)
4125             rot_center = box.midpoint();
4126         else
4127             rot_center = Inkscape::NodePath::Path::active_node->pos;
4129         NR::Matrix t =
4130             NR::Matrix (NR::translate(-rot_center)) *
4131             NR::Matrix (NR::rotate(rot)) *
4132             NR::Matrix (NR::translate(rot_center));
4134         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4135             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4136             n->pos *= t;
4137             n->n.pos *= t;
4138             n->p.pos *= t;
4139             sp_node_update_handles(n, false);
4140         }
4141     }
4143     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4146 /**
4147  * Scale one node.
4148  */
4149 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4151     bool both = false;
4152     Inkscape::NodePath::NodeSide *me, *other;
4154     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4155     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4157     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4158         me = &(n->p);
4159         other = &(n->n);
4160         n->code = NR_CURVETO;
4161     } else if (!n->p.other) {
4162         me = &(n->n);
4163         other = &(n->p);
4164         if (n->n.other)
4165             n->n.other->code = NR_CURVETO;
4166     } else {
4167         if (which > 0) { // right handle
4168             if (xn > xp) {
4169                 me = &(n->n);
4170                 other = &(n->p);
4171                 if (n->n.other)
4172                     n->n.other->code = NR_CURVETO;
4173             } else {
4174                 me = &(n->p);
4175                 other = &(n->n);
4176                 n->code = NR_CURVETO;
4177             }
4178         } else if (which < 0){ // left handle
4179             if (xn <= xp) {
4180                 me = &(n->n);
4181                 other = &(n->p);
4182                 if (n->n.other)
4183                     n->n.other->code = NR_CURVETO;
4184             } else {
4185                 me = &(n->p);
4186                 other = &(n->n);
4187                 n->code = NR_CURVETO;
4188             }
4189         } else { // both handles
4190             me = &(n->n);
4191             other = &(n->p);
4192             both = true;
4193             n->code = NR_CURVETO;
4194             if (n->n.other)
4195                 n->n.other->code = NR_CURVETO;
4196         }
4197     }
4199     Radial rme(me->pos - n->pos);
4200     Radial rother(other->pos - n->pos);
4202     rme.r += grow;
4203     if (rme.r < 0) rme.r = 0;
4204     if (rme.a == HUGE_VAL) {
4205         if (me->other) { // if direction is unknown, initialize it towards the next node
4206             Radial rme_next(me->other->pos - n->pos);
4207             rme.a = rme_next.a;
4208         } else { // if there's no next, initialize to 0
4209             rme.a = 0;
4210         }
4211     }
4212     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4213         rother.r += grow;
4214         if (rother.r < 0) rother.r = 0;
4215         if (rother.a == HUGE_VAL) {
4216             rother.a = rme.a + M_PI;
4217         }
4218     }
4220     me->pos = n->pos + NR::Point(rme);
4222     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4223         other->pos = n->pos + NR::Point(rother);
4224     }
4226     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4227     // so here we just move all the knots without emitting move signals, for speed
4228     sp_node_update_handles(n, false);
4231 /**
4232  * Scale selected nodes.
4233  */
4234 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4236     if (!nodepath || !nodepath->selected) return;
4238     if (g_list_length(nodepath->selected) == 1) {
4239         // scale handles of the single selected node
4240         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4241         node_scale_one (n, grow, which);
4242     } else {
4243         // scale nodes as an "object":
4245         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4246         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4247         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4248             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4249             box.expandTo (n->pos); // contain all selected nodes
4250         }
4252         double scale = (box.maxExtent() + grow)/box.maxExtent();
4254         NR::Point scale_center;
4255         if (Inkscape::NodePath::Path::active_node == NULL)
4256             scale_center = box.midpoint();
4257         else
4258             scale_center = Inkscape::NodePath::Path::active_node->pos;
4260         NR::Matrix t =
4261             NR::Matrix (NR::translate(-scale_center)) *
4262             NR::Matrix (NR::scale(scale, scale)) *
4263             NR::Matrix (NR::translate(scale_center));
4265         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4266             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4267             n->pos *= t;
4268             n->n.pos *= t;
4269             n->p.pos *= t;
4270             sp_node_update_handles(n, false);
4271         }
4272     }
4274     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4277 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4279     if (!nodepath) return;
4280     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4283 /**
4284  * Flip selected nodes horizontally/vertically.
4285  */
4286 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
4288     if (!nodepath || !nodepath->selected) return;
4290     if (g_list_length(nodepath->selected) == 1 && !center) {
4291         // flip handles of the single selected node
4292         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4293         double temp = n->p.pos[axis];
4294         n->p.pos[axis] = n->n.pos[axis];
4295         n->n.pos[axis] = temp;
4296         sp_node_update_handles(n, false);
4297     } else {
4298         // scale nodes as an "object":
4300         NR::Rect box = sp_node_selected_bbox (nodepath);
4301         if (!center) {
4302             center = box.midpoint();
4303         }
4304         NR::Matrix t =
4305             NR::Matrix (NR::translate(- *center)) *
4306             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
4307             NR::Matrix (NR::translate(*center));
4309         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4310             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4311             n->pos *= t;
4312             n->n.pos *= t;
4313             n->p.pos *= t;
4314             sp_node_update_handles(n, false);
4315         }
4316     }
4318     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4321 NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4323     g_assert (nodepath->selected);
4325     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4326     NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4327     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4328         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4329         box.expandTo (n->pos); // contain all selected nodes
4330     }
4331     return box;
4334 //-----------------------------------------------
4335 /**
4336  * Return new subpath under given nodepath.
4337  */
4338 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4340     g_assert(nodepath);
4341     g_assert(nodepath->desktop);
4343    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4345     s->nodepath = nodepath;
4346     s->closed = FALSE;
4347     s->nodes = NULL;
4348     s->first = NULL;
4349     s->last = NULL;
4351     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4352     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4353     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4355     return s;
4358 /**
4359  * Destroy nodes in subpath, then subpath itself.
4360  */
4361 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4363     g_assert(subpath);
4364     g_assert(subpath->nodepath);
4365     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4367     while (subpath->nodes) {
4368         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4369     }
4371     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4373     g_free(subpath);
4376 /**
4377  * Link head to tail in subpath.
4378  */
4379 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4381     g_assert(!sp->closed);
4382     g_assert(sp->last != sp->first);
4383     g_assert(sp->first->code == NR_MOVETO);
4385     sp->closed = TRUE;
4387     //Link the head to the tail
4388     sp->first->p.other = sp->last;
4389     sp->last->n.other  = sp->first;
4390     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4391     sp->first          = sp->last;
4393     //Remove the extra end node
4394     sp_nodepath_node_destroy(sp->last->n.other);
4397 /**
4398  * Open closed (loopy) subpath at node.
4399  */
4400 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4402     g_assert(sp->closed);
4403     g_assert(n->subpath == sp);
4404     g_assert(sp->first == sp->last);
4406     /* We create new startpoint, current node will become last one */
4408    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4409                                                 &n->pos, &n->pos, &n->n.pos);
4412     sp->closed        = FALSE;
4414     //Unlink to make a head and tail
4415     sp->first         = new_path;
4416     sp->last          = n;
4417     n->n.other        = NULL;
4418     new_path->p.other = NULL;
4421 /**
4422  * Return new node in subpath with given properties.
4423  * \param pos Position of node.
4424  * \param ppos Handle position in previous direction
4425  * \param npos Handle position in previous direction
4426  */
4427 Inkscape::NodePath::Node *
4428 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)
4430     g_assert(sp);
4431     g_assert(sp->nodepath);
4432     g_assert(sp->nodepath->desktop);
4434     if (nodechunk == NULL)
4435         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4437     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4439     n->subpath  = sp;
4441     if (type != Inkscape::NodePath::NODE_NONE) {
4442         // use the type from sodipodi:nodetypes
4443         n->type = type;
4444     } else {
4445         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4446             // points are (almost) collinear
4447             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4448                 // endnode, or a node with a retracted handle
4449                 n->type = Inkscape::NodePath::NODE_CUSP;
4450             } else {
4451                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4452             }
4453         } else {
4454             n->type = Inkscape::NodePath::NODE_CUSP;
4455         }
4456     }
4458     n->code     = code;
4459     n->selected = FALSE;
4460     n->pos      = *pos;
4461     n->p.pos    = *ppos;
4462     n->n.pos    = *npos;
4464     n->dragging_out = NULL;
4466     Inkscape::NodePath::Node *prev;
4467     if (next) {
4468         //g_assert(g_list_find(sp->nodes, next));
4469         prev = next->p.other;
4470     } else {
4471         prev = sp->last;
4472     }
4474     if (prev)
4475         prev->n.other = n;
4476     else
4477         sp->first = n;
4479     if (next)
4480         next->p.other = n;
4481     else
4482         sp->last = n;
4484     n->p.other = prev;
4485     n->n.other = next;
4487     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"));
4488     sp_knot_set_position(n->knot, pos, 0);
4490     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4491     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4492     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4493     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4494     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4495     sp_knot_update_ctrl(n->knot);
4497     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4498     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4499     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4500     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4501     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4502     sp_knot_show(n->knot);
4504     // We only create handle knots and lines on demand
4505     n->p.knot = NULL;
4506     n->p.line = NULL;
4507     n->n.knot = NULL;
4508     n->n.line = NULL;
4510     sp->nodes = g_list_prepend(sp->nodes, n);
4512     return n;
4515 /**
4516  * Destroy node and its knots, link neighbors in subpath.
4517  */
4518 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4520     g_assert(node);
4521     g_assert(node->subpath);
4522     g_assert(SP_IS_KNOT(node->knot));
4524    Inkscape::NodePath::SubPath *sp = node->subpath;
4526     if (node->selected) { // first, deselect
4527         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4528         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4529     }
4531     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4533     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4534     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4535     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4536     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4537     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4538     g_object_unref(G_OBJECT(node->knot));
4540     if (node->p.knot) {
4541         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4542         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4543         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4544         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4545         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4546         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4547         g_object_unref(G_OBJECT(node->p.knot));
4548         node->p.knot = NULL;
4549     }
4551     if (node->n.knot) {
4552         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4553         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4554         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4555         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4556         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4557         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4558         g_object_unref(G_OBJECT(node->n.knot));
4559         node->n.knot = NULL;
4560     }
4562     if (node->p.line)
4563         gtk_object_destroy(GTK_OBJECT(node->p.line));
4564     if (node->n.line)
4565         gtk_object_destroy(GTK_OBJECT(node->n.line));
4567     if (sp->nodes) { // there are others nodes on the subpath
4568         if (sp->closed) {
4569             if (sp->first == node) {
4570                 g_assert(sp->last == node);
4571                 sp->first = node->n.other;
4572                 sp->last = sp->first;
4573             }
4574             node->p.other->n.other = node->n.other;
4575             node->n.other->p.other = node->p.other;
4576         } else {
4577             if (sp->first == node) {
4578                 sp->first = node->n.other;
4579                 sp->first->code = NR_MOVETO;
4580             }
4581             if (sp->last == node) sp->last = node->p.other;
4582             if (node->p.other) node->p.other->n.other = node->n.other;
4583             if (node->n.other) node->n.other->p.other = node->p.other;
4584         }
4585     } else { // this was the last node on subpath
4586         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4587     }
4589     g_mem_chunk_free(nodechunk, node);
4592 /**
4593  * Returns one of the node's two sides.
4594  * \param which Indicates which side.
4595  * \return Pointer to previous node side if which==-1, next if which==1.
4596  */
4597 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4599     g_assert(node);
4601     switch (which) {
4602         case -1:
4603             return &node->p;
4604         case 1:
4605             return &node->n;
4606         default:
4607             break;
4608     }
4610     g_assert_not_reached();
4612     return NULL;
4615 /**
4616  * Return the other side of the node, given one of its sides.
4617  */
4618 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4620     g_assert(node);
4622     if (me == &node->p) return &node->n;
4623     if (me == &node->n) return &node->p;
4625     g_assert_not_reached();
4627     return NULL;
4630 /**
4631  * Return NRPathcode on the given side of the node.
4632  */
4633 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4635     g_assert(node);
4637     if (me == &node->p) {
4638         if (node->p.other) return (NRPathcode)node->code;
4639         return NR_MOVETO;
4640     }
4642     if (me == &node->n) {
4643         if (node->n.other) return (NRPathcode)node->n.other->code;
4644         return NR_MOVETO;
4645     }
4647     g_assert_not_reached();
4649     return NR_END;
4652 /**
4653  * Return node with the given index
4654  */
4655 Inkscape::NodePath::Node *
4656 sp_nodepath_get_node_by_index(int index)
4658     Inkscape::NodePath::Node *e = NULL;
4660     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4661     if (!nodepath) {
4662         return e;
4663     }
4665     //find segment
4666     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4668         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4669         int n = g_list_length(sp->nodes);
4670         if (sp->closed) {
4671             n++;
4672         }
4674         //if the piece belongs to this subpath grab it
4675         //otherwise move onto the next subpath
4676         if (index < n) {
4677             e = sp->first;
4678             for (int i = 0; i < index; ++i) {
4679                 e = e->n.other;
4680             }
4681             break;
4682         } else {
4683             if (sp->closed) {
4684                 index -= (n+1);
4685             } else {
4686                 index -= n;
4687             }
4688         }
4689     }
4691     return e;
4694 /**
4695  * Returns plain text meaning of node type.
4696  */
4697 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4699     unsigned retracted = 0;
4700     bool endnode = false;
4702     for (int which = -1; which <= 1; which += 2) {
4703         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4704         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4705             retracted ++;
4706         if (!side->other)
4707             endnode = true;
4708     }
4710     if (retracted == 0) {
4711         if (endnode) {
4712                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4713                 return _("end node");
4714         } else {
4715             switch (node->type) {
4716                 case Inkscape::NodePath::NODE_CUSP:
4717                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4718                     return _("cusp");
4719                 case Inkscape::NodePath::NODE_SMOOTH:
4720                     // TRANSLATORS: "smooth" is an adjective here
4721                     return _("smooth");
4722                 case Inkscape::NodePath::NODE_SYMM:
4723                     return _("symmetric");
4724             }
4725         }
4726     } else if (retracted == 1) {
4727         if (endnode) {
4728             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4729             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4730         } else {
4731             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4732         }
4733     } else {
4734         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4735     }
4737     return NULL;
4740 /**
4741  * Handles content of statusbar as long as node tool is active.
4742  */
4743 void
4744 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4746     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");
4747     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4749     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4750     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4751     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4752     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4754     SPDesktop *desktop = NULL;
4755     if (nodepath) {
4756         desktop = nodepath->desktop;
4757     } else {
4758         desktop = SP_ACTIVE_DESKTOP;
4759     }
4761     SPEventContext *ec = desktop->event_context;
4762     if (!ec) return;
4763     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4764     if (!mc) return;
4766     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4768     if (selected_nodes == 0) {
4769         Inkscape::Selection *sel = desktop->selection;
4770         if (!sel || sel->isEmpty()) {
4771             mc->setF(Inkscape::NORMAL_MESSAGE,
4772                      _("Select a single object to edit its nodes or handles."));
4773         } else {
4774             if (nodepath) {
4775             mc->setF(Inkscape::NORMAL_MESSAGE,
4776                      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.",
4777                               "<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.",
4778                               total_nodes),
4779                      total_nodes);
4780             } else {
4781                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4782                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4783                 } else {
4784                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4785                 }
4786             }
4787         }
4788     } else if (nodepath && selected_nodes == 1) {
4789         mc->setF(Inkscape::NORMAL_MESSAGE,
4790                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4791                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4792                           total_nodes),
4793                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4794     } else {
4795         if (selected_subpaths > 1) {
4796             mc->setF(Inkscape::NORMAL_MESSAGE,
4797                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4798                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4799                               total_nodes),
4800                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4801         } else {
4802             mc->setF(Inkscape::NORMAL_MESSAGE,
4803                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4804                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4805                               total_nodes),
4806                      selected_nodes, total_nodes, when_selected);
4807         }
4808     }
4811 /*
4812  * returns a *copy* of the curve of that object.
4813  */
4814 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4815     if (!object)
4816         return NULL;
4818     SPCurve *curve = NULL;
4819     if (SP_IS_PATH(object)) {
4820         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4821         curve = curve_new->copy();
4822     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4823         const gchar *svgd = object->repr->attribute(key);
4824         if (svgd) {
4825             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4826             SPCurve *curve_new = new SPCurve(pv);
4827             if (curve_new) {
4828                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4829             }
4830         }
4831     }
4833     return curve;
4836 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4837     if (!np || !np->object || !curve)
4838         return;
4840     if (SP_IS_PATH(np->object)) {
4841         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4842             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4843         } else {
4844             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4845         }
4846     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4847         Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( LIVEPATHEFFECT(np->object)->lpe->getParameter(np->repr_key) );
4848         if (pathparam) {
4849             pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
4850             np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4851         }
4852     }
4855 /**
4856 SPCanvasItem *
4857 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
4858     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
4860 **/
4862 /**
4863 SPCanvasItem *
4864 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) {
4865     SPCurve *flash_curve = curve->copy();
4866     Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity();
4867     flash_curve->transform(i2d);
4868     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4869     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4870     // unless we also flash the nodes...
4871     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4872     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4873     sp_canvas_item_show(canvasitem);
4874     flash_curve->unref();
4875     return canvasitem;
4878 SPCanvasItem *
4879 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) {
4880     return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path),
4881                                            prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff));
4883 **/
4885 SPCanvasItem *
4886 sp_nodepath_helperpath_from_path(SPDesktop *desktop, SPPath *path) {
4887     SPCurve *flash_curve = sp_path_get_curve_for_edit(path)->copy();
4888     Geom::Matrix i2d = sp_item_i2d_affine(SP_ITEM(path));
4889     flash_curve->transform(i2d);
4890     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4891     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4892     // unless we also flash the nodes...
4893     guint32 color = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
4894     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4895     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4896     sp_canvas_item_show(canvasitem);
4897     flash_curve->unref();
4898     return canvasitem;
4901 // TODO: Merge this with sp_nodepath_make_helper_item()!
4902 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4903     np->show_helperpath = show;
4905     if (show) {
4906         SPCurve *helper_curve = np->curve->copy();
4907         helper_curve->transform(to_2geom(np->i2d));
4908         if (!np->helper_path) {
4909             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
4911             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4912             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);
4913             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4914             sp_canvas_item_move_to_z(np->helper_path, 0);
4915             sp_canvas_item_show(np->helper_path);
4916         } else {
4917             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4918         }
4919         helper_curve->unref();
4920     } else {
4921         if (np->helper_path) {
4922             GtkObject *temp = np->helper_path;
4923             np->helper_path = NULL;
4924             gtk_object_destroy(temp);
4925         }
4926     }
4929 /* sp_nodepath_make_straight_path:
4930  *   Prevents user from curving the path by dragging a segment or activating handles etc.
4931  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
4932  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
4933  */
4934 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4935     np->straight_path = true;
4936     np->show_handles = false;
4937     g_message("add code to make the path straight.");
4938     // do sp_nodepath_convert_node_type on all nodes?
4939     // coding tip: search for this text : "Make selected segments lines"
4943 /*
4944   Local Variables:
4945   mode:c++
4946   c-file-style:"stroustrup"
4947   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4948   indent-tabs-mode:nil
4949   fill-column:99
4950   End:
4951 */
4952 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :