Code

convert all SPCurve's points and matrices arguments and return types to 2Geom
[inkscape.git] / src / nodepath.cpp
1 #define __SP_NODEPATH_C__
3 /** \file
4  * Path handler in node edit mode
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *
10  * Portions of this code are in public domain; node sculpting functions written by bulia byak are under GNU GPL
11  */
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
17 #include <gdk/gdkkeysyms.h>
18 #include "display/canvas-bpath.h"
19 #include "display/curve.h"
20 #include "display/sp-ctrlline.h"
21 #include "display/sodipodi-ctrl.h"
22 #include "display/sp-canvas-util.h"
23 #include <glibmm/i18n.h>
24 #include <2geom/pathvector.h>
25 #include <2geom/sbasis-to-bezier.h>
26 #include "helper/units.h"
27 #include "knot.h"
28 #include "inkscape.h"
29 #include "document.h"
30 #include "sp-namedview.h"
31 #include "desktop.h"
32 #include "desktop-handles.h"
33 #include "snap.h"
34 #include "message-stack.h"
35 #include "message-context.h"
36 #include "node-context.h"
37 #include "shape-editor.h"
38 #include "selection-chemistry.h"
39 #include "selection.h"
40 #include "xml/repr.h"
41 #include "prefs-utils.h"
42 #include "sp-metrics.h"
43 #include "sp-path.h"
44 #include "libnr/nr-matrix-ops.h"
45 #include "splivarot.h"
46 #include "svg/svg.h"
47 #include "verbs.h"
48 #include "display/bezier-utils.h"
49 #include <vector>
50 #include <algorithm>
51 #include <cstring>
52 #include <string>
53 #include "live_effects/lpeobject.h"
54 #include "live_effects/effect.h"
55 #include "live_effects/parameter/parameter.h"
56 #include "util/mathfns.h"
57 #include "display/snap-indicator.h"
58 #include "snapped-point.h"
60 class NR::Matrix;
62 /// \todo
63 /// evil evil evil. FIXME: conflict of two different Path classes!
64 /// There is a conflict in the namespace between two classes named Path.
65 /// #include "sp-flowtext.h"
66 /// #include "sp-flowregion.h"
68 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
69 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
70 GType sp_flowregion_get_type (void);
71 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
72 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
73 GType sp_flowtext_get_type (void);
74 // end evil workaround
76 #include "helper/stlport.h"
79 /// \todo fixme: Implement these via preferences */
81 #define NODE_FILL          0xbfbfbf00
82 #define NODE_STROKE        0x000000ff
83 #define NODE_FILL_HI       0xff000000
84 #define NODE_STROKE_HI     0x000000ff
85 #define NODE_FILL_SEL      0x0000ffff
86 #define NODE_STROKE_SEL    0x000000ff
87 #define NODE_FILL_SEL_HI   0xff000000
88 #define NODE_STROKE_SEL_HI 0x000000ff
89 #define KNOT_FILL          0xffffffff
90 #define KNOT_STROKE        0x000000ff
91 #define KNOT_FILL_HI       0xff000000
92 #define KNOT_STROKE_HI     0x000000ff
94 static GMemChunk *nodechunk = NULL;
96 /* Creation from object */
98 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t);
99 static void add_curve_to_subpath( Inkscape::NodePath::Path *np, Inkscape::NodePath::SubPath *sp, Geom::Curve const & c,
100                                   Inkscape::NodePath::NodeType const *t, guint & i, NR::Point & ppos, NRPathcode & pcode  );
101 static Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length);
103 /* Object updating */
105 static void stamp_repr(Inkscape::NodePath::Path *np);
106 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
107 static gchar *create_typestr(Inkscape::NodePath::Path *np);
109 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
111 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
113 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
115 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
117 /* Adjust handle placement, if the node or the other handle is moved */
118 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
119 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
121 /* Node event callbacks */
122 static void node_clicked(SPKnot *knot, guint state, gpointer data);
123 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
124 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
125 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
127 /* Handle event callbacks */
128 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
129 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
130 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
131 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
132 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
133 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
135 /* Constructors and destructors */
137 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
138 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
139 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
140 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
141 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
142                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
143 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
145 /* Helpers */
147 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
148 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
149 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
151 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
152 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
154 // active_node indicates mouseover node
155 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
157 static void sp_nodepath_draw_helper_curve(Inkscape::NodePath::Path *np, SPDesktop *desktop) {
158     // Draw helper curve
159     if (np->show_helperpath) {
160         SPCurve *helper_curve = np->curve->copy();
161         helper_curve->transform(to_2geom(np->i2d));
162         np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(desktop), helper_curve);
163         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);
164         sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
165         sp_canvas_item_move_to_z(np->helper_path, 0);
166         sp_canvas_item_show(np->helper_path);
167         helper_curve->unref();
168     }
171 /**
172  * \brief Creates new nodepath from item
173  */
174 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
176     Inkscape::XML::Node *repr = object->repr;
178     /** \todo
179      * FIXME: remove this. We don't want to edit paths inside flowtext.
180      * Instead we will build our flowtext with cloned paths, so that the
181      * real paths are outside the flowtext and thus editable as usual.
182      */
183     if (SP_IS_FLOWTEXT(object)) {
184         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
185             if SP_IS_FLOWREGION(child) {
186                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
187                 if (grandchild && SP_IS_PATH(grandchild)) {
188                     object = SP_ITEM(grandchild);
189                     break;
190                 }
191             }
192         }
193     }
195     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
197     if (curve == NULL)
198         return NULL;
200     if (curve->get_segment_count() < 1) {
201         curve->unref();
202         return NULL; // prevent crash for one-node paths
203     }
205     //Create new nodepath
206     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
207     if (!np) {
208         curve->unref();
209         return NULL;
210     }
212     // Set defaults
213     np->desktop     = desktop;
214     np->object      = object;
215     np->subpaths    = NULL;
216     np->selected    = NULL;
217     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
218     np->livarot_path = NULL;
219     np->local_change = 0;
220     np->show_handles = show_handles;
221     np->helper_path = NULL;
222     np->helperpath_rgba = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
223     np->helperpath_width = 1.0;
224     np->curve = curve->copy();
225     np->show_helperpath = (prefs_get_int_attribute ("tools.nodes", "show_helperpath",  0) == 1);
226     if (SP_IS_LPE_ITEM(object)) {
227         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
228         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
229             np->show_helperpath = true;
230         }            
231     }
232     np->straight_path = false;
233     if (IS_LIVEPATHEFFECT(object) && item) {
234         np->item = item;
235     } else {
236         np->item = SP_ITEM(object);
237     }
239     // we need to update item's transform from the repr here,
240     // because they may be out of sync when we respond
241     // to a change in repr by regenerating nodepath     --bb
242     sp_object_read_attr(SP_OBJECT(np->item), "transform");
244     np->i2d  = from_2geom(sp_item_i2d_affine(np->item));
245     np->d2i  = np->i2d.inverse();
247     np->repr = repr;
248     if (repr_key_in) { // apparantly the object is an LPEObject
249         np->repr_key = g_strdup(repr_key_in);
250         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
251         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
252         if (lpeparam) {
253             lpeparam->param_setup_nodepath(np);
254         }
255     } else {
256         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
257         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
258             np->repr_key = g_strdup("inkscape:original-d");
260             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
261             if (lpe) {
262                 lpe->setup_nodepath(np);
263             }
264         } else {
265             np->repr_key = g_strdup("d");
266         }
267     }
269     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
270      * So for example a closed rectangle has a nodetypestring of length 5.
271      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
272     Geom::PathVector const &pathv = curve->get_pathvector();
273     guint length = curve->get_segment_count();
274     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
275         length += pit->empty() ? 0 : 1;
276     }
278     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
279     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
281     // create the subpath(s) from the bpath
282     subpaths_from_pathvector(np, pathv, typestr);
284     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
285     np->subpaths = g_list_reverse(np->subpaths);
287     delete[] typestr;
288     curve->unref();
290     // create the livarot representation from the same item
291     sp_nodepath_ensure_livarot_path(np);
293     sp_nodepath_draw_helper_curve(np, desktop);
295     return np;
298 /**
299  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
300  */
301 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
303     if (!np)  //soft fail, like delete
304         return;
306     while (np->subpaths) {
307         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
308     }
310     //Inform the ShapeEditor that made me, if any, that I am gone.
311     if (np->shape_editor)
312         np->shape_editor->nodepath_destroyed();
314     g_assert(!np->selected);
316     if (np->livarot_path) {
317         delete np->livarot_path;
318         np->livarot_path = NULL;
319     }
321     if (np->helper_path) {
322         GtkObject *temp = np->helper_path;
323         np->helper_path = NULL;
324         gtk_object_destroy(temp);
325     }
326     if (np->curve) {
327         np->curve->unref();
328         np->curve = NULL;
329     }
331     if (np->repr_key) {
332         g_free(np->repr_key);
333         np->repr_key = NULL;
334     }
335     if (np->repr_nodetypes_key) {
336         g_free(np->repr_nodetypes_key);
337         np->repr_nodetypes_key = NULL;
338     }
340     np->desktop = NULL;
342     g_free(np);
346 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
348     if (np && np->livarot_path == NULL) {
349         SPCurve *curve = create_curve(np);
350         np->livarot_path = new Path;
351         np->livarot_path->LoadPathVector(curve->get_pathvector());
353         if (np->livarot_path)
354             np->livarot_path->ConvertWithBackData(0.01);
356         curve->unref();
357     }
361 /**
362  *  Return the node count of a given NodeSubPath.
363  */
364 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
366     if (!subpath)
367         return 0;
368     gint nodeCount = g_list_length(subpath->nodes);
369     return nodeCount;
372 /**
373  *  Return the node count of a given NodePath.
374  */
375 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
377     if (!np)
378         return 0;
379     gint nodeCount = 0;
380     for (GList *item = np->subpaths ; item ; item=item->next) {
381        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
382         nodeCount += g_list_length(subpath->nodes);
383     }
384     return nodeCount;
387 /**
388  *  Return the subpath count of a given NodePath.
389  */
390 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
392     if (!np)
393         return 0;
394     return g_list_length (np->subpaths);
397 /**
398  *  Return the selected node count of a given NodePath.
399  */
400 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
402     if (!np)
403         return 0;
404     return g_list_length (np->selected);
407 /**
408  *  Return the number of subpaths where nodes are selected in a given NodePath.
409  */
410 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
412     if (!np)
413         return 0;
414     if (!np->selected)
415         return 0;
416     if (!np->selected->next)
417         return 1;
418     gint count = 0;
419     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
420         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
421         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
422             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
423             if (node->selected) {
424                 count ++;
425                 break;
426             }
427         }
428     }
429     return count;
432 /**
433  * Clean up a nodepath after editing.
434  *
435  * Currently we are deleting trivial subpaths.
436  */
437 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
439     GList *badSubPaths = NULL;
441     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
442     for (GList *l = nodepath->subpaths; l ; l=l->next) {
443        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
444        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
445             badSubPaths = g_list_append(badSubPaths, sp);
446     }
448     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
449     //also removes the subpath from nodepath->subpaths
450     for (GList *l = badSubPaths; l ; l=l->next) {
451        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
452         sp_nodepath_subpath_destroy(sp);
453     }
455     g_list_free(badSubPaths);
458 /**
459  * Create new nodepaths from pathvector, make it subpaths of np.
460  * \param t The node type array.
461  */
462 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
464     guint i = 0;  // index into node type array
465     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
466         if (pit->empty())
467             continue;  // don't add single knot paths
469         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
471         NR::Point ppos = from_2geom(pit->initialPoint()) * np->i2d;
472         NRPathcode pcode = NR_MOVETO;
474         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
475             add_curve_to_subpath(np, sp, *cit, t, i, ppos, pcode);
476         }
478         if (pit->closed()) {
479             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
480             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
481              * If the length is zero, don't add it to the nodepath. */
482             Geom::Curve const &closing_seg = pit->back_closed();
483             if ( ! closing_seg.isDegenerate() ) {
484                 NR::Point pos = from_2geom(closing_seg.finalPoint()) * np->i2d;
485                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
486             }
488             sp_nodepath_subpath_close(sp);
489         }
490     }
492 // should add initial point of curve with type of previous curve:
493 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,
494                                  NR::Point & ppos, NRPathcode & pcode)
496     if( dynamic_cast<Geom::LineSegment const*>(&c) ||
497         dynamic_cast<Geom::HLineSegment const*>(&c) ||
498         dynamic_cast<Geom::VLineSegment const*>(&c) )
499     {
500         NR::Point pos = from_2geom(c.initialPoint()) * np->i2d;
501         sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
502         ppos = from_2geom(c.finalPoint());
503         pcode = NR_LINETO;
504     }
505     else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&c)) {
506         std::vector<Geom::Point> points = cubic_bezier->points();
507         NR::Point pos = from_2geom(points[0]) * np->i2d;
508         NR::Point npos = from_2geom(points[1]) * np->i2d;
509         sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
510         ppos = from_2geom(points[2]) * np->i2d;
511         pcode = NR_CURVETO;
512     }
513     else {
514         //this case handles sbasis as well as all other curve types
515         Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(c.toSBasis(), 0.1);
517         for(Geom::Path::iterator iter = sbasis_path.begin(); iter != sbasis_path.end(); ++iter) {
518             add_curve_to_subpath(np, sp, *iter, t, i, ppos, pcode);
519         }
520     }
524 /**
525  * Convert from sodipodi:nodetypes to new style type array.
526  */
527 static
528 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
530     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
532     guint pos = 0;
534     if (types) {
535         for (guint i = 0; types[i] && ( i < length ); i++) {
536             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
537             if (types[i] != '\0') {
538                 switch (types[i]) {
539                     case 's':
540                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
541                         break;
542                     case 'z':
543                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
544                         break;
545                     case 'c':
546                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
547                         break;
548                     default:
549                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
550                         break;
551                 }
552             }
553         }
554     }
556     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
558     return typestr;
561 /**
562  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
563  * updated but repr is not (for speed). Used during curve and node drag.
564  */
565 static void update_object(Inkscape::NodePath::Path *np)
567     g_assert(np);
569     np->curve->unref();
570     np->curve = create_curve(np);
572     sp_nodepath_set_curve(np, np->curve);
574     if (np->show_helperpath) {
575         SPCurve * helper_curve = np->curve->copy();
576         helper_curve->transform(to_2geom(np->i2d));
577         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
578         helper_curve->unref();
579     }
581     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
582     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
583     np->shape_editor->update_knotholder();
586 /**
587  * Update XML path node with data from path object.
588  */
589 static void update_repr_internal(Inkscape::NodePath::Path *np)
591     g_assert(np);
593     Inkscape::XML::Node *repr = np->object->repr;
595     np->curve->unref();
596     np->curve = create_curve(np);
598     gchar *typestr = create_typestr(np);
599     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
601     // determine if path has an effect applied and write to correct "d" attribute.
602     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
603         np->local_change++;
604         repr->setAttribute(np->repr_key, svgpath);
605     }
607     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
608         np->local_change++;
609         repr->setAttribute(np->repr_nodetypes_key, typestr);
610     }
612     g_free(svgpath);
613     g_free(typestr);
615     if (np->show_helperpath) {
616         SPCurve * helper_curve = np->curve->copy();
617         helper_curve->transform(to_2geom(np->i2d));
618         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
619         helper_curve->unref();
620     }
621  }
623 /**
624  * Update XML path node with data from path object, commit changes forever.
625  */
626 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
628     //fixme: np can be NULL, so check before proceeding
629     g_return_if_fail(np != NULL);
631     if (np->livarot_path) {
632         delete np->livarot_path;
633         np->livarot_path = NULL;
634     }
636     update_repr_internal(np);
637     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
639     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
640                      annotation);
643 /**
644  * Update XML path node with data from path object, commit changes with undo.
645  */
646 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
648     if (np->livarot_path) {
649         delete np->livarot_path;
650         np->livarot_path = NULL;
651     }
653     update_repr_internal(np);
654     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
655                            annotation);
658 /**
659  * Make duplicate of path, replace corresponding XML node in tree, commit.
660  */
661 static void stamp_repr(Inkscape::NodePath::Path *np)
663     g_assert(np);
665     Inkscape::XML::Node *old_repr = np->object->repr;
666     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
668     // remember the position of the item
669     gint pos = old_repr->position();
670     // remember parent
671     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
673     SPCurve *curve = create_curve(np);
674     gchar *typestr = create_typestr(np);
676     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
678     new_repr->setAttribute(np->repr_key, svgpath);
679     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
681     // add the new repr to the parent
682     parent->appendChild(new_repr);
683     // move to the saved position
684     new_repr->setPosition(pos > 0 ? pos : 0);
686     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
687                      _("Stamp"));
689     Inkscape::GC::release(new_repr);
690     g_free(svgpath);
691     g_free(typestr);
692     curve->unref();
695 /**
696  * Create curve from path.
697  */
698 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
700     SPCurve *curve = new SPCurve();
702     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
703        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
704         curve->moveto(sp->first->pos * np->d2i);
705        Inkscape::NodePath::Node *n = sp->first->n.other;
706         while (n) {
707             NR::Point const end_pt = n->pos * np->d2i;
708             switch (n->code) {
709                 case NR_LINETO:
710                     curve->lineto(end_pt);
711                     break;
712                 case NR_CURVETO:
713                     curve->curveto(n->p.other->n.pos * np->d2i,
714                                      n->p.pos * np->d2i,
715                                      end_pt);
716                     break;
717                 default:
718                     g_assert_not_reached();
719                     break;
720             }
721             if (n != sp->last) {
722                 n = n->n.other;
723             } else {
724                 n = NULL;
725             }
726         }
727         if (sp->closed) {
728             curve->closepath();
729         }
730     }
732     return curve;
735 /**
736  * Convert path type string to sodipodi:nodetypes style.
737  */
738 static gchar *create_typestr(Inkscape::NodePath::Path *np)
740     gchar *typestr = g_new(gchar, 32);
741     gint len = 32;
742     gint pos = 0;
744     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
745        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
747         if (pos >= len) {
748             typestr = g_renew(gchar, typestr, len + 32);
749             len += 32;
750         }
752         typestr[pos++] = 'c';
754        Inkscape::NodePath::Node *n;
755         n = sp->first->n.other;
756         while (n) {
757             gchar code;
759             switch (n->type) {
760                 case Inkscape::NodePath::NODE_CUSP:
761                     code = 'c';
762                     break;
763                 case Inkscape::NodePath::NODE_SMOOTH:
764                     code = 's';
765                     break;
766                 case Inkscape::NodePath::NODE_SYMM:
767                     code = 'z';
768                     break;
769                 default:
770                     g_assert_not_reached();
771                     code = '\0';
772                     break;
773             }
775             if (pos >= len) {
776                 typestr = g_renew(gchar, typestr, len + 32);
777                 len += 32;
778             }
780             typestr[pos++] = code;
782             if (n != sp->last) {
783                 n = n->n.other;
784             } else {
785                 n = NULL;
786             }
787         }
788     }
790     if (pos >= len) {
791         typestr = g_renew(gchar, typestr, len + 1);
792         len += 1;
793     }
795     typestr[pos++] = '\0';
797     return typestr;
800 /**
801  * Returns current path in context. // later eliminate this function at all!
802  */
803 static Inkscape::NodePath::Path *sp_nodepath_current()
805     if (!SP_ACTIVE_DESKTOP) {
806         return NULL;
807     }
809     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
811     if (!SP_IS_NODE_CONTEXT(event_context)) {
812         return NULL;
813     }
815     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
820 /**
821  \brief Fills node and handle positions for three nodes, splitting line
822   marked by end at distance t.
823  */
824 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
826     g_assert(new_path != NULL);
827     g_assert(end      != NULL);
829     g_assert(end->p.other == new_path);
830    Inkscape::NodePath::Node *start = new_path->p.other;
831     g_assert(start);
833     if (end->code == NR_LINETO) {
834         new_path->type =Inkscape::NodePath::NODE_CUSP;
835         new_path->code = NR_LINETO;
836         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
837     } else {
838         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
839         new_path->code = NR_CURVETO;
840         gdouble s      = 1 - t;
841         for (int dim = 0; dim < 2; dim++) {
842             NR::Coord const f000 = start->pos[dim];
843             NR::Coord const f001 = start->n.pos[dim];
844             NR::Coord const f011 = end->p.pos[dim];
845             NR::Coord const f111 = end->pos[dim];
846             NR::Coord const f00t = s * f000 + t * f001;
847             NR::Coord const f01t = s * f001 + t * f011;
848             NR::Coord const f11t = s * f011 + t * f111;
849             NR::Coord const f0tt = s * f00t + t * f01t;
850             NR::Coord const f1tt = s * f01t + t * f11t;
851             NR::Coord const fttt = s * f0tt + t * f1tt;
852             start->n.pos[dim]    = f00t;
853             new_path->p.pos[dim] = f0tt;
854             new_path->pos[dim]   = fttt;
855             new_path->n.pos[dim] = f1tt;
856             end->p.pos[dim]      = f11t;
857         }
858     }
861 /**
862  * Adds new node on direct line between two nodes, activates handles of all
863  * three nodes.
864  */
865 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
867     g_assert(end);
868     g_assert(end->subpath);
869     g_assert(g_list_find(end->subpath->nodes, end));
871    Inkscape::NodePath::Node *start = end->p.other;
872     g_assert( start->n.other == end );
873    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
874                                                end,
875                                                (NRPathcode)end->code == NR_LINETO?
876                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
877                                                (NRPathcode)end->code,
878                                                &start->pos, &start->pos, &start->n.pos);
879     sp_nodepath_line_midpoint(newnode, end, t);
881     sp_node_adjust_handles(start);
882     sp_node_update_handles(start);
883     sp_node_update_handles(newnode);
884     sp_node_adjust_handles(end);
885     sp_node_update_handles(end);
887     return newnode;
890 /**
891 \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
892 */
893 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
895     g_assert(node);
896     g_assert(node->subpath);
897     g_assert(g_list_find(node->subpath->nodes, node));
899    Inkscape::NodePath::SubPath *sp = node->subpath;
900     Inkscape::NodePath::Path *np    = sp->nodepath;
902     if (sp->closed) {
903         sp_nodepath_subpath_open(sp, node);
904         return sp->first;
905     } else {
906         // no break for end nodes
907         if (node == sp->first) return NULL;
908         if (node == sp->last ) return NULL;
910         // create a new subpath
911        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
913         // duplicate the break node as start of the new subpath
914         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
916         // attach rest of curve to new node
917         g_assert(node->n.other);
918         newnode->n.other = node->n.other; node->n.other = NULL;
919         newnode->n.other->p.other = newnode;
920         newsubpath->last = sp->last;
921         sp->last = node;
922         node = newnode;
923         while (node->n.other) {
924             node = node->n.other;
925             node->subpath = newsubpath;
926             sp->nodes = g_list_remove(sp->nodes, node);
927             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
928         }
931         return newnode;
932     }
935 /**
936  * Duplicate node and connect to neighbours.
937  */
938 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
940     g_assert(node);
941     g_assert(node->subpath);
942     g_assert(g_list_find(node->subpath->nodes, node));
944    Inkscape::NodePath::SubPath *sp = node->subpath;
946     NRPathcode code = (NRPathcode) node->code;
947     if (code == NR_MOVETO) { // if node is the endnode,
948         node->code = NR_LINETO; // new one is inserted before it, so change that to line
949     }
951     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
953     if (!node->n.other || !node->p.other) // if node is an endnode, select it
954         return node;
955     else
956         return newnode; // otherwise select the newly created node
959 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
961     node->p.pos = (node->pos + (node->pos - node->n.pos));
964 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
966     node->n.pos = (node->pos + (node->pos - node->p.pos));
969 /**
970  * Change line type at node, with side effects on neighbours.
971  */
972 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
974     g_assert(end);
975     g_assert(end->subpath);
976     g_assert(end->p.other);
978     if (end->code == static_cast< guint > ( code ) )
979         return;
981    Inkscape::NodePath::Node *start = end->p.other;
983     end->code = code;
985     if (code == NR_LINETO) {
986         if (start->code == NR_LINETO) {
987             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
988         }
989         if (end->n.other) {
990             if (end->n.other->code == NR_LINETO) {
991                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
992             }
993         }
994     } else {
995         NR::Point delta = end->pos - start->pos;
996         start->n.pos = start->pos + delta / 3;
997         end->p.pos = end->pos - delta / 3;
998         sp_node_adjust_handle(start, 1);
999         sp_node_adjust_handle(end, -1);
1000     }
1002     sp_node_update_handles(start);
1003     sp_node_update_handles(end);
1006 /**
1007  * Change node type, and its handles accordingly.
1008  */
1009 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1011     g_assert(node);
1012     g_assert(node->subpath);
1014     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1015         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1016             type =Inkscape::NodePath::NODE_CUSP;
1017         }
1018     }
1020     node->type = type;
1022     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1023         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1024         node->knot->setSize (node->selected? 11 : 9);
1025         sp_knot_update_ctrl(node->knot);
1026     } else {
1027         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1028         node->knot->setSize (node->selected? 9 : 7);
1029         sp_knot_update_ctrl(node->knot);
1030     }
1032     // if one of handles is mouseovered, preserve its position
1033     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1034         sp_node_adjust_handle(node, 1);
1035     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1036         sp_node_adjust_handle(node, -1);
1037     } else {
1038         sp_node_adjust_handles(node);
1039     }
1041     sp_node_update_handles(node);
1043     sp_nodepath_update_statusbar(node->subpath->nodepath);
1045     return node;
1048 bool
1049 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1051         Inkscape::NodePath::Node *othernode = side->other;
1052         if (!othernode)
1053             return false;
1054         NRPathcode const code = sp_node_path_code_from_side(node, side);
1055         if (code == NR_LINETO)
1056             return true;
1057         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1058         if (&node->p == side) {
1059             other_to_me = &othernode->n;
1060         } else if (&node->n == side) {
1061             other_to_me = &othernode->p;
1062         } 
1063         if (!other_to_me)
1064             return false;
1065         bool is_line = 
1066              (NR::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1067               NR::L2(node->pos - side->pos) < 1e-6);
1068         return is_line;
1071 /**
1072  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1073  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1074  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1075  * If already cusp and set to cusp, retracts handles.
1076 */
1077 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1079     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1081 /* 
1082   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1083  
1084         if (two_handles) {
1085             // do nothing, adjust_handles called via set_node_type will line them up
1086         } else if (one_handle) {
1087             if (opposite_to_handle_is_line) {
1088                 if (lined_up) {
1089                     // already half-smooth; pull opposite handle too making it fully smooth
1090                 } else {
1091                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1092                 }
1093             } else {
1094                 // pull opposite handle in line with the existing one
1095             }
1096         } else if (no_handles) {
1097             if (both_segments_are_lines OR both_segments_are_curves) {
1098                 //pull both handles
1099             } else {
1100                 // pull the handle opposite to line segment, making node half-smooth
1101             }
1102         }
1103 */
1104         bool p_has_handle = (NR::L2(node->pos  - node->p.pos) > 1e-6);
1105         bool n_has_handle = (NR::L2(node->pos  - node->n.pos) > 1e-6);
1106         bool p_is_line = sp_node_side_is_line(node, &node->p);
1107         bool n_is_line = sp_node_side_is_line(node, &node->n);
1109         if (p_has_handle && n_has_handle) {
1110             // do nothing, adjust_handles will line them up
1111         } else if (p_has_handle || n_has_handle) {
1112             if (p_has_handle && n_is_line) {
1113                 Radial line (node->n.other->pos - node->pos);
1114                 Radial handle (node->pos - node->p.pos);
1115                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1116                     // already half-smooth; pull opposite handle too making it fully smooth
1117                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1118                 } else {
1119                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1120                 }
1121             } else if (n_has_handle && p_is_line) {
1122                 Radial line (node->p.other->pos - node->pos);
1123                 Radial handle (node->pos - node->n.pos);
1124                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1125                     // already half-smooth; pull opposite handle too making it fully smooth
1126                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1127                 } else {
1128                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1129                 }
1130             } else if (p_has_handle && node->n.other) {
1131                 // pull n handle
1132                 node->n.other->code = NR_CURVETO;
1133                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1134                     NR::L2(node->p.pos - node->pos) :
1135                     NR::L2(node->n.other->pos - node->pos) / 3;
1136                 node->n.pos = node->pos - (len / NR::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1137             } else if (n_has_handle && node->p.other) {
1138                 // pull p handle
1139                 node->code = NR_CURVETO;
1140                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1141                     NR::L2(node->n.pos - node->pos) :
1142                     NR::L2(node->p.other->pos - node->pos) / 3;
1143                 node->p.pos = node->pos - (len / NR::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1144             }
1145         } else if (!p_has_handle && !n_has_handle) {
1146             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1147                 // no handles, but both segments are either lnes or curves:
1148                 //pull both handles
1150                 // convert both to curves:
1151                 node->code = NR_CURVETO;
1152                 node->n.other->code = NR_CURVETO;
1154                 NR::Point leg_prev = node->pos - node->p.other->pos;
1155                 NR::Point leg_next = node->pos - node->n.other->pos;
1157                 double norm_leg_prev = L2(leg_prev);
1158                 double norm_leg_next = L2(leg_next);
1160                 NR::Point delta;
1161                 if (norm_leg_next > 0.0) {
1162                     delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1163                     (&delta)->normalize();
1164                 }
1166                 if (type == Inkscape::NodePath::NODE_SYMM) {
1167                     double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1168                     node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1169                     node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1170                 } else {
1171                     // length of handle is proportional to distance to adjacent node
1172                     node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1173                     node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1174                 }
1176             } else {
1177                 // pull the handle opposite to line segment, making it half-smooth
1178                 if (p_is_line && node->n.other) {
1179                     if (type != Inkscape::NodePath::NODE_SYMM) {
1180                         // pull n handle
1181                         node->n.other->code = NR_CURVETO;
1182                         double len =  NR::L2(node->n.other->pos - node->pos) / 3;
1183                         node->n.pos = node->pos + (len / NR::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1184                     }
1185                 } else if (n_is_line && node->p.other) {
1186                     if (type != Inkscape::NodePath::NODE_SYMM) {
1187                         // pull p handle
1188                         node->code = NR_CURVETO;
1189                         double len =  NR::L2(node->p.other->pos - node->pos) / 3;
1190                         node->p.pos = node->pos + (len / NR::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1191                     }
1192                 }
1193             }
1194         }
1195     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1196         // cusping a cusp: retract nodes
1197         node->p.pos = node->pos;
1198         node->n.pos = node->pos;
1199     }
1201     sp_nodepath_set_node_type (node, type);
1204 /**
1205  * Move node to point, and adjust its and neighbouring handles.
1206  */
1207 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1209     NR::Point delta = p - node->pos;
1210     node->pos = p;
1212     node->p.pos += delta;
1213     node->n.pos += delta;
1215     Inkscape::NodePath::Node *node_p = NULL;
1216     Inkscape::NodePath::Node *node_n = NULL;
1218     if (node->p.other) {
1219         if (node->code == NR_LINETO) {
1220             sp_node_adjust_handle(node, 1);
1221             sp_node_adjust_handle(node->p.other, -1);
1222             node_p = node->p.other;
1223         }
1224     }
1225     if (node->n.other) {
1226         if (node->n.other->code == NR_LINETO) {
1227             sp_node_adjust_handle(node, -1);
1228             sp_node_adjust_handle(node->n.other, 1);
1229             node_n = node->n.other;
1230         }
1231     }
1233     // this function is only called from batch movers that will update display at the end
1234     // themselves, so here we just move all the knots without emitting move signals, for speed
1235     sp_node_update_handles(node, false);
1236     if (node_n) {
1237         sp_node_update_handles(node_n, false);
1238     }
1239     if (node_p) {
1240         sp_node_update_handles(node_p, false);
1241     }
1244 /**
1245  * Call sp_node_moveto() for node selection and handle possible snapping.
1246  */
1247 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1248                                             bool const snap, bool constrained = false, 
1249                                             Inkscape::Snapper::ConstraintLine const &constraint = NR::Point())
1251     NR::Coord best = NR_HUGE;
1252     NR::Point delta(dx, dy);
1253     NR::Point best_pt = delta;
1254     Inkscape::SnappedPoint best_abs;
1255     
1256     if (snap) {    
1257         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1258          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1259          * must provide that information. */
1260           
1261         // Build a list of the unselected nodes to which the snapper should snap 
1262         std::vector<NR::Point> unselected_nodes;
1263         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1264             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1265             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1266                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1267                 if (!node->selected) {
1268                     unselected_nodes.push_back(node->pos);
1269                 }    
1270             }
1271         }        
1272         
1273         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1274         
1275         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1276             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1277             m.setup(NULL, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1278             Inkscape::SnappedPoint s;
1279             if (constrained) {
1280                 Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1281                 dedicated_constraint.setPoint(n->pos);
1282                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, dedicated_constraint);
1283             } else {
1284                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta);
1285             }            
1286             if (s.getSnapped() && (s.getDistance() < best)) {
1287                 best = s.getDistance();
1288                 best_abs = s;
1289                 best_pt = s.getPoint() - n->pos;
1290             }
1291         }
1292                         
1293         if (best_abs.getSnapped()) {
1294             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1295         } else {
1296             nodepath->desktop->snapindicator->remove_snappoint();    
1297         }
1298     }
1300     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1301         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1302         sp_node_moveto(n, n->pos + best_pt);
1303     }
1305     // do not update repr here so that node dragging is acceptably fast
1306     update_object(nodepath);
1309 /**
1310 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1311 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1312 near x = 0.
1313  */
1314 double
1315 sculpt_profile (double x, double alpha, guint profile)
1317     if (x >= 1)
1318         return 0;
1319     if (x <= 0)
1320         return 1;
1322     switch (profile) {
1323         case SCULPT_PROFILE_LINEAR:
1324         return 1 - x;
1325         case SCULPT_PROFILE_BELL:
1326         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1327         case SCULPT_PROFILE_ELLIPTIC:
1328         return sqrt(1 - x*x);
1329     }
1331     return 1;
1334 double
1335 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1337     // extremely primitive for now, don't have time to look for the real one
1338     double lower = NR::L2(b - a);
1339     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1340     return (lower + upper)/2;
1343 void
1344 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1346     n->pos = n->origin + delta;
1347     n->n.pos = n->n.origin + delta_n;
1348     n->p.pos = n->p.origin + delta_p;
1349     sp_node_adjust_handles(n);
1350     sp_node_update_handles(n, false);
1353 /**
1354  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1355  * on how far they are from the dragged node n.
1356  */
1357 static void
1358 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1360     g_assert (n);
1361     g_assert (nodepath);
1362     g_assert (n->subpath->nodepath == nodepath);
1364     double pressure = n->knot->pressure;
1365     if (pressure == 0)
1366         pressure = 0.5; // default
1367     pressure = CLAMP (pressure, 0.2, 0.8);
1369     // map pressure to alpha = 1/5 ... 5
1370     double alpha = 1 - 2 * fabs(pressure - 0.5);
1371     if (pressure > 0.5)
1372         alpha = 1/alpha;
1374     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1376     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1377         // Only one subpath has selected nodes:
1378         // use linear mode, where the distance from n to node being dragged is calculated along the path
1380         double n_sel_range = 0, p_sel_range = 0;
1381         guint n_nodes = 0, p_nodes = 0;
1382         guint n_sel_nodes = 0, p_sel_nodes = 0;
1384         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1385         {
1386             double n_range = 0, p_range = 0;
1387             bool n_going = true, p_going = true;
1388             Inkscape::NodePath::Node *n_node = n;
1389             Inkscape::NodePath::Node *p_node = n;
1390             do {
1391                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1392                 if (n_node && n_going)
1393                     n_node = n_node->n.other;
1394                 if (n_node == NULL) {
1395                     n_going = false;
1396                 } else {
1397                     n_nodes ++;
1398                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1399                     if (n_node->selected) {
1400                         n_sel_nodes ++;
1401                         n_sel_range = n_range;
1402                     }
1403                     if (n_node == p_node) {
1404                         n_going = false;
1405                         p_going = false;
1406                     }
1407                 }
1408                 if (p_node && p_going)
1409                     p_node = p_node->p.other;
1410                 if (p_node == NULL) {
1411                     p_going = false;
1412                 } else {
1413                     p_nodes ++;
1414                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1415                     if (p_node->selected) {
1416                         p_sel_nodes ++;
1417                         p_sel_range = p_range;
1418                     }
1419                     if (p_node == n_node) {
1420                         n_going = false;
1421                         p_going = false;
1422                     }
1423                 }
1424             } while (n_going || p_going);
1425         }
1427         // Second pass: actually move nodes in this subpath
1428         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1429         {
1430             double n_range = 0, p_range = 0;
1431             bool n_going = true, p_going = true;
1432             Inkscape::NodePath::Node *n_node = n;
1433             Inkscape::NodePath::Node *p_node = n;
1434             do {
1435                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1436                 if (n_node && n_going)
1437                     n_node = n_node->n.other;
1438                 if (n_node == NULL) {
1439                     n_going = false;
1440                 } else {
1441                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1442                     if (n_node->selected) {
1443                         sp_nodepath_move_node_and_handles (n_node,
1444                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1445                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1446                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1447                     }
1448                     if (n_node == p_node) {
1449                         n_going = false;
1450                         p_going = false;
1451                     }
1452                 }
1453                 if (p_node && p_going)
1454                     p_node = p_node->p.other;
1455                 if (p_node == NULL) {
1456                     p_going = false;
1457                 } else {
1458                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1459                     if (p_node->selected) {
1460                         sp_nodepath_move_node_and_handles (p_node,
1461                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1462                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1463                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1464                     }
1465                     if (p_node == n_node) {
1466                         n_going = false;
1467                         p_going = false;
1468                     }
1469                 }
1470             } while (n_going || p_going);
1471         }
1473     } else {
1474         // Multiple subpaths have selected nodes:
1475         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1476         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1477         // fix the pear-like shape when sculpting e.g. a ring
1479         // First pass: calculate range
1480         gdouble direct_range = 0;
1481         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1482             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1483             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1484                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1485                 if (node->selected) {
1486                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1487                 }
1488             }
1489         }
1491         // Second pass: actually move nodes
1492         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1493             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1494             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1495                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1496                 if (node->selected) {
1497                     if (direct_range > 1e-6) {
1498                         sp_nodepath_move_node_and_handles (node,
1499                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1500                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1501                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1502                     } else {
1503                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1504                     }
1506                 }
1507             }
1508         }
1509     }
1511     // do not update repr here so that node dragging is acceptably fast
1512     update_object(nodepath);
1516 /**
1517  * Move node selection to point, adjust its and neighbouring handles,
1518  * handle possible snapping, and commit the change with possible undo.
1519  */
1520 void
1521 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1523     if (!nodepath) return;
1525     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1527     if (dx == 0) {
1528         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1529     } else if (dy == 0) {
1530         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1531     } else {
1532         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1533     }
1536 /**
1537  * Move node selection off screen and commit the change.
1538  */
1539 void
1540 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1542     // borrowed from sp_selection_move_screen in selection-chemistry.c
1543     // we find out the current zoom factor and divide deltas by it
1544     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1546     gdouble zoom = desktop->current_zoom();
1547     gdouble zdx = dx / zoom;
1548     gdouble zdy = dy / zoom;
1550     if (!nodepath) return;
1552     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1554     if (dx == 0) {
1555         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1556     } else if (dy == 0) {
1557         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1558     } else {
1559         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1560     }
1563 /**
1564  * Move selected nodes to the absolute position given
1565  */
1566 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis)
1568     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1569         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1570         NR::Point npos(axis == NR::X ? val : n->pos[NR::X], axis == NR::Y ? val : n->pos[NR::Y]);
1571         sp_node_moveto(n, npos);
1572     }
1574     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1577 /**
1578  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1579  */
1580 NR::Maybe<NR::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1582     NR::Maybe<NR::Coord> no_coord = NR::Nothing();
1583     g_return_val_if_fail(nodepath->selected, no_coord);
1585     // determine coordinate of first selected node
1586     GList *nsel = nodepath->selected;
1587     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1588     NR::Coord coord = n->pos[axis];
1589     bool coincide = true;
1591     // compare it to the coordinates of all the other selected nodes
1592     for (GList *l = nsel->next; l != NULL; l = l->next) {
1593         n = (Inkscape::NodePath::Node *) l->data;
1594         if (n->pos[axis] != coord) {
1595             coincide = false;
1596         }
1597     }
1598     if (coincide) {
1599         return coord;
1600     } else {
1601         NR::Rect bbox = sp_node_selected_bbox(nodepath);
1602         // currently we return the coordinate of the bounding box midpoint because I don't know how
1603         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1604         return bbox.midpoint()[axis];
1605     }
1608 /** If they don't yet exist, creates knot and line for the given side of the node */
1609 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1611     if (!side->knot) {
1612         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"));
1614         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1615         side->knot->setSize (7);
1616         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1617         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1618         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1619         sp_knot_update_ctrl(side->knot);
1621         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1622         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1623         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1624         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1625         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1626         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1627     }
1629     if (!side->line) {
1630         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1631                                         SP_TYPE_CTRLLINE, NULL);
1632     }
1635 /**
1636  * Ensure the given handle of the node is visible/invisible, update its screen position
1637  */
1638 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1640     g_assert(node != NULL);
1642    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1643     NRPathcode code = sp_node_path_code_from_side(node, side);
1645     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1647     if (show_handle) {
1648         if (!side->knot) { // No handle knot at all
1649             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1650             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1651             side->knot->pos = side->pos;
1652             if (side->knot->item)
1653                 SP_CTRL(side->knot->item)->moveto(side->pos);
1654             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1655             sp_knot_show(side->knot);
1656         } else {
1657             if (side->knot->pos != side->pos) { // only if it's really moved
1658                 if (fire_move_signals) {
1659                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1660                 } else {
1661                     sp_knot_moveto(side->knot, &side->pos);
1662                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1663                 }
1664             }
1665             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1666                 sp_knot_show(side->knot);
1667             }
1668         }
1669         sp_canvas_item_show(side->line);
1670     } else {
1671         if (side->knot) {
1672             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1673                 sp_knot_hide(side->knot);
1674             }
1675         }
1676         if (side->line) {
1677             sp_canvas_item_hide(side->line);
1678         }
1679     }
1682 /**
1683  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1684  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1685  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1686  * updated; otherwise, just move the knots silently (used in batch moves).
1687  */
1688 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1690     g_assert(node != NULL);
1692     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1693         sp_knot_show(node->knot);
1694     }
1696     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1697         if (fire_move_signals)
1698             sp_knot_set_position(node->knot, &node->pos, 0);
1699         else
1700             sp_knot_moveto(node->knot, &node->pos);
1701     }
1703     gboolean show_handles = node->selected;
1704     if (node->p.other != NULL) {
1705         if (node->p.other->selected) show_handles = TRUE;
1706     }
1707     if (node->n.other != NULL) {
1708         if (node->n.other->selected) show_handles = TRUE;
1709     }
1711     if (node->subpath->nodepath->show_handles == false)
1712         show_handles = FALSE;
1714     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1715     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1718 /**
1719  * Call sp_node_update_handles() for all nodes on subpath.
1720  */
1721 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1723     g_assert(subpath != NULL);
1725     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1726         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1727     }
1730 /**
1731  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1732  */
1733 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1735     g_assert(nodepath != NULL);
1737     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1738         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1739     }
1742 void
1743 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1745     if (nodepath == NULL) return;
1747     nodepath->show_handles = show;
1748     sp_nodepath_update_handles(nodepath);
1751 /**
1752  * Adds all selected nodes in nodepath to list.
1753  */
1754 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1756     StlConv<Node *>::list(l, selected);
1757 /// \todo this adds a copying, rework when the selection becomes a stl list
1760 /**
1761  * Align selected nodes on the specified axis.
1762  */
1763 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1765     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1766         return;
1767     }
1769     if ( !nodepath->selected->next ) { // only one node selected
1770         return;
1771     }
1772    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1773     NR::Point dest(pNode->pos);
1774     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1775         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1776         if (pNode) {
1777             dest[axis] = pNode->pos[axis];
1778             sp_node_moveto(pNode, dest);
1779         }
1780     }
1782     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1785 /// Helper struct.
1786 struct NodeSort
1788    Inkscape::NodePath::Node *_node;
1789     NR::Coord _coord;
1790     /// \todo use vectorof pointers instead of calling copy ctor
1791     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1792         _node(node), _coord(node->pos[axis])
1793     {}
1795 };
1797 static bool operator<(NodeSort const &a, NodeSort const &b)
1799     return (a._coord < b._coord);
1802 /**
1803  * Distribute selected nodes on the specified axis.
1804  */
1805 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1807     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1808         return;
1809     }
1811     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1812         return;
1813     }
1815    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1816     std::vector<NodeSort> sorted;
1817     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1818         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1819         if (pNode) {
1820             NodeSort n(pNode, axis);
1821             sorted.push_back(n);
1822             //dest[axis] = pNode->pos[axis];
1823             //sp_node_moveto(pNode, dest);
1824         }
1825     }
1826     std::sort(sorted.begin(), sorted.end());
1827     unsigned int len = sorted.size();
1828     //overall bboxes span
1829     float dist = (sorted.back()._coord -
1830                   sorted.front()._coord);
1831     //new distance between each bbox
1832     float step = (dist) / (len - 1);
1833     float pos = sorted.front()._coord;
1834     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1835           it < sorted.end();
1836           it ++ )
1837     {
1838         NR::Point dest((*it)._node->pos);
1839         dest[axis] = pos;
1840         sp_node_moveto((*it)._node, dest);
1841         pos += step;
1842     }
1844     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1848 /**
1849  * Call sp_nodepath_line_add_node() for all selected segments.
1850  */
1851 void
1852 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1854     if (!nodepath) {
1855         return;
1856     }
1858     GList *nl = NULL;
1860     int n_added = 0;
1862     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1863        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1864         g_assert(t->selected);
1865         if (t->p.other && t->p.other->selected) {
1866             nl = g_list_prepend(nl, t);
1867         }
1868     }
1870     while (nl) {
1871        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1872        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1873        sp_nodepath_node_select(n, TRUE, FALSE);
1874        n_added ++;
1875        nl = g_list_remove(nl, t);
1876     }
1878     /** \todo fixme: adjust ? */
1879     sp_nodepath_update_handles(nodepath);
1881     if (n_added > 1) {
1882         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1883     } else if (n_added > 0) {
1884         sp_nodepath_update_repr(nodepath, _("Add node"));
1885     }
1887     sp_nodepath_update_statusbar(nodepath);
1890 /**
1891  * Select segment nearest to point
1892  */
1893 void
1894 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1896     if (!nodepath) {
1897         return;
1898     }
1900     sp_nodepath_ensure_livarot_path(nodepath);
1901     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1902     if (!maybe_position) {
1903         return;
1904     }
1905     Path::cut_position position = *maybe_position;
1907     //find segment to segment
1908     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1910     //fixme: this can return NULL, so check before proceeding.
1911     g_return_if_fail(e != NULL);
1913     gboolean force = FALSE;
1914     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1915         force = TRUE;
1916     }
1917     sp_nodepath_node_select(e, (gboolean) toggle, force);
1918     if (e->p.other)
1919         sp_nodepath_node_select(e->p.other, TRUE, force);
1921     sp_nodepath_update_handles(nodepath);
1923     sp_nodepath_update_statusbar(nodepath);
1926 /**
1927  * Add a node nearest to point
1928  */
1929 void
1930 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1932     if (!nodepath) {
1933         return;
1934     }
1936     sp_nodepath_ensure_livarot_path(nodepath);
1937     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1938     if (!maybe_position) {
1939         return;
1940     }
1941     Path::cut_position position = *maybe_position;
1943     //find segment to split
1944     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1946     //don't know why but t seems to flip for lines
1947     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1948         position.t = 1.0 - position.t;
1949     }
1950     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1951     sp_nodepath_node_select(n, FALSE, TRUE);
1953     /* fixme: adjust ? */
1954     sp_nodepath_update_handles(nodepath);
1956     sp_nodepath_update_repr(nodepath, _("Add node"));
1958     sp_nodepath_update_statusbar(nodepath);
1961 /*
1962  * Adjusts a segment so that t moves by a certain delta for dragging
1963  * converts lines to curves
1964  *
1965  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1966  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1967  */
1968 void
1969 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1971     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1973     //fixme: e and e->p can be NULL, so check for those before proceeding
1974     g_return_if_fail(e != NULL);
1975     g_return_if_fail(&e->p != NULL);
1977     /* feel good is an arbitrary parameter that distributes the delta between handles
1978      * if t of the drag point is less than 1/6 distance form the endpoint only
1979      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1980      */
1981     double feel_good;
1982     if (t <= 1.0 / 6.0)
1983         feel_good = 0;
1984     else if (t <= 0.5)
1985         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1986     else if (t <= 5.0 / 6.0)
1987         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1988     else
1989         feel_good = 1;
1991     //if we're dragging a line convert it to a curve
1992     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1993         sp_nodepath_set_line_type(e, NR_CURVETO);
1994     }
1996     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1997     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1998     e->p.other->n.pos += offsetcoord0;
1999     e->p.pos += offsetcoord1;
2001     // adjust handles of adjacent nodes where necessary
2002     sp_node_adjust_handle(e,1);
2003     sp_node_adjust_handle(e->p.other,-1);
2005     sp_nodepath_update_handles(e->subpath->nodepath);
2007     update_object(e->subpath->nodepath);
2009     sp_nodepath_update_statusbar(e->subpath->nodepath);
2013 /**
2014  * Call sp_nodepath_break() for all selected segments.
2015  */
2016 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2018     if (!nodepath) return;
2020     GList *tempin = g_list_copy(nodepath->selected);
2021     GList *temp = NULL;
2022     for (GList *l = tempin; l != NULL; l = l->next) {
2023        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2024        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2025         if (nn == NULL) continue; // no break, no new node
2026         temp = g_list_prepend(temp, nn);
2027     }
2028     g_list_free(tempin);
2030     if (temp) {
2031         sp_nodepath_deselect(nodepath);
2032     }
2033     for (GList *l = temp; l != NULL; l = l->next) {
2034         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2035     }
2037     sp_nodepath_update_handles(nodepath);
2039     sp_nodepath_update_repr(nodepath, _("Break path"));
2042 /**
2043  * Duplicate the selected node(s).
2044  */
2045 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2047     if (!nodepath) {
2048         return;
2049     }
2051     GList *temp = NULL;
2052     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2053        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2054        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2055         if (nn == NULL) continue; // could not duplicate
2056         temp = g_list_prepend(temp, nn);
2057     }
2059     if (temp) {
2060         sp_nodepath_deselect(nodepath);
2061     }
2062     for (GList *l = temp; l != NULL; l = l->next) {
2063         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2064     }
2066     sp_nodepath_update_handles(nodepath);
2068     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2071 /**
2072  *  Internal function to join two nodes by merging them into one.
2073  */
2074 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2076     /* a and b are endpoints */
2078     // if one of the two nodes is mouseovered, fix its position
2079     NR::Point c;
2080     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2081         c = a->pos;
2082     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2083         c = b->pos;
2084     } else {
2085         // otherwise, move joined node to the midpoint
2086         c = (a->pos + b->pos) / 2;
2087     }
2089     if (a->subpath == b->subpath) {
2090        Inkscape::NodePath::SubPath *sp = a->subpath;
2091         sp_nodepath_subpath_close(sp);
2092         sp_node_moveto (sp->first, c);
2094         sp_nodepath_update_handles(sp->nodepath);
2095         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2096         return;
2097     }
2099     /* a and b are separate subpaths */
2100     Inkscape::NodePath::SubPath *sa = a->subpath;
2101     Inkscape::NodePath::SubPath *sb = b->subpath;
2102     NR::Point p;
2103     Inkscape::NodePath::Node *n;
2104     NRPathcode code;
2105     if (a == sa->first) {
2106         // we will now reverse sa, so that a is its last node, not first, and drop that node
2107         p = sa->first->n.pos;
2108         code = (NRPathcode)sa->first->n.other->code;
2109         // create new subpath
2110        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2111        // create a first moveto node on it
2112         n = sa->last;
2113         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2114         n = n->p.other;
2115         if (n == sa->first) n = NULL;
2116         while (n) {
2117             // copy the rest of the nodes from sa to t, going backwards
2118             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2119             n = n->p.other;
2120             if (n == sa->first) n = NULL;
2121         }
2122         // replace sa with t
2123         sp_nodepath_subpath_destroy(sa);
2124         sa = t;
2125     } else if (a == sa->last) {
2126         // a is already last, just drop it
2127         p = sa->last->p.pos;
2128         code = (NRPathcode)sa->last->code;
2129         sp_nodepath_node_destroy(sa->last);
2130     } else {
2131         code = NR_END;
2132         g_assert_not_reached();
2133     }
2135     if (b == sb->first) {
2136         // copy all nodes from b to a, forward 
2137         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2138         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2139             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2140         }
2141     } else if (b == sb->last) {
2142         // copy all nodes from b to a, backward 
2143         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2144         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2145             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2146         }
2147     } else {
2148         g_assert_not_reached();
2149     }
2150     /* and now destroy sb */
2152     sp_nodepath_subpath_destroy(sb);
2154     sp_nodepath_update_handles(sa->nodepath);
2156     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2158     sp_nodepath_update_statusbar(nodepath);
2161 /**
2162  *  Internal function to join two nodes by adding a segment between them.
2163  */
2164 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2166     if (a->subpath == b->subpath) {
2167        Inkscape::NodePath::SubPath *sp = a->subpath;
2169         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2170         sp->closed = TRUE;
2172         sp->first->p.other = sp->last;
2173         sp->last->n.other  = sp->first;
2175         sp_node_handle_mirror_p_to_n(sp->last);
2176         sp_node_handle_mirror_n_to_p(sp->first);
2178         sp->first->code = sp->last->code;
2179         sp->first       = sp->last;
2181         sp_nodepath_update_handles(sp->nodepath);
2183         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2185         return;
2186     }
2188     /* a and b are separate subpaths */
2189     Inkscape::NodePath::SubPath *sa = a->subpath;
2190     Inkscape::NodePath::SubPath *sb = b->subpath;
2192     Inkscape::NodePath::Node *n;
2193     NR::Point p;
2194     NRPathcode code;
2195     if (a == sa->first) {
2196         code = (NRPathcode) sa->first->n.other->code;
2197        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2198         n = sa->last;
2199         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2200         for (n = n->p.other; n != NULL; n = n->p.other) {
2201             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2202         }
2203         sp_nodepath_subpath_destroy(sa);
2204         sa = t;
2205     } else if (a == sa->last) {
2206         code = (NRPathcode)sa->last->code;
2207     } else {
2208         code = NR_END;
2209         g_assert_not_reached();
2210     }
2212     if (b == sb->first) {
2213         n = sb->first;
2214         sp_node_handle_mirror_p_to_n(sa->last);
2215         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2216         sp_node_handle_mirror_n_to_p(sa->last);
2217         for (n = n->n.other; n != NULL; n = n->n.other) {
2218             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2219         }
2220     } else if (b == sb->last) {
2221         n = sb->last;
2222         sp_node_handle_mirror_p_to_n(sa->last);
2223         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2224         sp_node_handle_mirror_n_to_p(sa->last);
2225         for (n = n->p.other; n != NULL; n = n->p.other) {
2226             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2227         }
2228     } else {
2229         g_assert_not_reached();
2230     }
2231     /* and now destroy sb */
2233     sp_nodepath_subpath_destroy(sb);
2235     sp_nodepath_update_handles(sa->nodepath);
2237     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2240 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2242 /**
2243  * Internal function to handle joining two nodes.
2244  */
2245 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2247     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2249     if (g_list_length(nodepath->selected) != 2) {
2250         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2251         return;
2252     }
2254     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2255     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2257     g_assert(a != b);
2258     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2259         // someone tried to join an orphan node (i.e. a single-node subpath).
2260         // this is not worth an error message, just fail silently.
2261         return;
2262     }
2264     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2265         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2266         return;
2267     }
2269     switch(mode) {
2270         case NODE_JOIN_ENDPOINTS:
2271             do_node_selected_join(nodepath, a, b);
2272             break;
2273         case NODE_JOIN_SEGMENT:
2274             do_node_selected_join_segment(nodepath, a, b);
2275             break;
2276     }
2279 /**
2280  *  Join two nodes by merging them into one.
2281  */
2282 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2284     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2287 /**
2288  *  Join two nodes by adding a segment between them.
2289  */
2290 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2292     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2295 /**
2296  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2297  */
2298 void sp_node_delete_preserve(GList *nodes_to_delete)
2300     GSList *nodepaths = NULL;
2302     while (nodes_to_delete) {
2303         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2304         Inkscape::NodePath::SubPath *sp = node->subpath;
2305         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2306         Inkscape::NodePath::Node *sample_cursor = NULL;
2307         Inkscape::NodePath::Node *sample_end = NULL;
2308         Inkscape::NodePath::Node *delete_cursor = node;
2309         bool just_delete = false;
2311         //find the start of this contiguous selection
2312         //move left to the first node that is not selected
2313         //or the start of the non-closed path
2314         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2315             delete_cursor = curr;
2316         }
2318         //just delete at the beginning of an open path
2319         if (!delete_cursor->p.other) {
2320             sample_cursor = delete_cursor;
2321             just_delete = true;
2322         } else {
2323             sample_cursor = delete_cursor->p.other;
2324         }
2326         //calculate points for each segment
2327         int rate = 5;
2328         float period = 1.0 / rate;
2329         std::vector<NR::Point> data;
2330         if (!just_delete) {
2331             data.push_back(sample_cursor->pos);
2332             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2333                 //just delete at the end of an open path
2334                 if (!sp->closed && curr == sp->last) {
2335                     just_delete = true;
2336                     break;
2337                 }
2339                 //sample points on the contiguous selected segment
2340                 NR::Point *bez;
2341                 bez = new NR::Point [4];
2342                 bez[0] = curr->pos;
2343                 bez[1] = curr->n.pos;
2344                 bez[2] = curr->n.other->p.pos;
2345                 bez[3] = curr->n.other->pos;
2346                 for (int i=1; i<rate; i++) {
2347                     gdouble t = i * period;
2348                     NR::Point p = bezier_pt(3, bez, t);
2349                     data.push_back(p);
2350                 }
2351                 data.push_back(curr->n.other->pos);
2353                 sample_end = curr->n.other;
2354                 //break if we've come full circle or hit the end of the selection
2355                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2356                     break;
2357                 }
2358             }
2359         }
2361         if (!just_delete) {
2362             //calculate the best fitting single segment and adjust the endpoints
2363             NR::Point *adata;
2364             adata = new NR::Point [data.size()];
2365             copy(data.begin(), data.end(), adata);
2367             NR::Point *bez;
2368             bez = new NR::Point [4];
2369             //would decreasing error create a better fitting approximation?
2370             gdouble error = 1.0;
2371             gint ret;
2372             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2374             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2375             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2376             //the resulting nodes behave as expected.
2377             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2378                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2379             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2380                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2382             //adjust endpoints
2383             sample_cursor->n.pos = bez[1];
2384             sample_end->p.pos = bez[2];
2385         }
2387         //destroy this contiguous selection
2388         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2389             Inkscape::NodePath::Node *temp = delete_cursor;
2390             if (delete_cursor->n.other == delete_cursor) {
2391                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2392                 delete_cursor = NULL;
2393             } else {
2394                 delete_cursor = delete_cursor->n.other;
2395             }
2396             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2397             sp_nodepath_node_destroy(temp);
2398         }
2400         sp_nodepath_update_handles(nodepath);
2402         if (!g_slist_find(nodepaths, nodepath))
2403             nodepaths = g_slist_prepend (nodepaths, nodepath);
2404     }
2406     for (GSList *i = nodepaths; i; i = i->next) {
2407         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2408         // different nodepaths will give us one undo event per nodepath
2409         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2411         // if the entire nodepath is removed, delete the selected object.
2412         if (nodepath->subpaths == NULL ||
2413             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2414             //at least 2
2415             sp_nodepath_get_node_count(nodepath) < 2) {
2416             SPDocument *document = sp_desktop_document (nodepath->desktop);
2417             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2418             //delete this nodepath's object, not the entire selection! (though at this time, this
2419             //does not matter)
2420             sp_selection_delete();
2421             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2422                               _("Delete nodes"));
2423         } else {
2424             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2425             sp_nodepath_update_statusbar(nodepath);
2426         }
2427     }
2429     g_slist_free (nodepaths);
2432 /**
2433  * Delete one or more selected nodes.
2434  */
2435 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2437     if (!nodepath) return;
2438     if (!nodepath->selected) return;
2440     /** \todo fixme: do it the right way */
2441     while (nodepath->selected) {
2442        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2443         sp_nodepath_node_destroy(node);
2444     }
2447     //clean up the nodepath (such as for trivial subpaths)
2448     sp_nodepath_cleanup(nodepath);
2450     sp_nodepath_update_handles(nodepath);
2452     // if the entire nodepath is removed, delete the selected object.
2453     if (nodepath->subpaths == NULL ||
2454         sp_nodepath_get_node_count(nodepath) < 2) {
2455         SPDocument *document = sp_desktop_document (nodepath->desktop);
2456         sp_selection_delete();
2457         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2458                           _("Delete nodes"));
2459         return;
2460     }
2462     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2464     sp_nodepath_update_statusbar(nodepath);
2467 /**
2468  * Delete one or more segments between two selected nodes.
2469  * This is the code for 'split'.
2470  */
2471 void
2472 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2474    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2475    Inkscape::NodePath::Node *curr, *next;     //Iterators
2477     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2479     if (g_list_length(nodepath->selected) != 2) {
2480         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2481                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2482         return;
2483     }
2485     //Selected nodes, not inclusive
2486    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2487    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2489     if ( ( a==b)                       ||  //same node
2490          (a->subpath  != b->subpath )  ||  //not the same path
2491          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2492          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2493     {
2494         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2495                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2496         return;
2497     }
2499     //###########################################
2500     //# BEGIN EDITS
2501     //###########################################
2502     //##################################
2503     //# CLOSED PATH
2504     //##################################
2505     if (a->subpath->closed) {
2508         gboolean reversed = FALSE;
2510         //Since we can go in a circle, we need to find the shorter distance.
2511         //  a->b or b->a
2512         start = end = NULL;
2513         int distance    = 0;
2514         int minDistance = 0;
2515         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2516             if (curr==b) {
2517                 //printf("a to b:%d\n", distance);
2518                 start = a;//go from a to b
2519                 end   = b;
2520                 minDistance = distance;
2521                 //printf("A to B :\n");
2522                 break;
2523             }
2524             distance++;
2525         }
2527         //try again, the other direction
2528         distance = 0;
2529         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2530             if (curr==a) {
2531                 //printf("b to a:%d\n", distance);
2532                 if (distance < minDistance) {
2533                     start    = b;  //we go from b to a
2534                     end      = a;
2535                     reversed = TRUE;
2536                     //printf("B to A\n");
2537                 }
2538                 break;
2539             }
2540             distance++;
2541         }
2544         //Copy everything from 'end' to 'start' to a new subpath
2545        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2546         for (curr=end ; curr ; curr=curr->n.other) {
2547             NRPathcode code = (NRPathcode) curr->code;
2548             if (curr == end)
2549                 code = NR_MOVETO;
2550             sp_nodepath_node_new(t, NULL,
2551                                  (Inkscape::NodePath::NodeType)curr->type, code,
2552                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2553             if (curr == start)
2554                 break;
2555         }
2556         sp_nodepath_subpath_destroy(a->subpath);
2559     }
2563     //##################################
2564     //# OPEN PATH
2565     //##################################
2566     else {
2568         //We need to get the direction of the list between A and B
2569         //Can we walk from a to b?
2570         start = end = NULL;
2571         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2572             if (curr==b) {
2573                 start = a;  //did it!  we go from a to b
2574                 end   = b;
2575                 //printf("A to B\n");
2576                 break;
2577             }
2578         }
2579         if (!start) {//didn't work?  let's try the other direction
2580             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2581                 if (curr==a) {
2582                     start = b;  //did it!  we go from b to a
2583                     end   = a;
2584                     //printf("B to A\n");
2585                     break;
2586                 }
2587             }
2588         }
2589         if (!start) {
2590             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2591                                                      _("Cannot find path between nodes."));
2592             return;
2593         }
2597         //Copy everything after 'end' to a new subpath
2598        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2599         for (curr=end ; curr ; curr=curr->n.other) {
2600             NRPathcode code = (NRPathcode) curr->code;
2601             if (curr == end)
2602                 code = NR_MOVETO;
2603             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2604                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2605         }
2607         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2608         for (curr = start->n.other ; curr  ; curr=next) {
2609             next = curr->n.other;
2610             sp_nodepath_node_destroy(curr);
2611         }
2613     }
2614     //###########################################
2615     //# END EDITS
2616     //###########################################
2618     //clean up the nodepath (such as for trivial subpaths)
2619     sp_nodepath_cleanup(nodepath);
2621     sp_nodepath_update_handles(nodepath);
2623     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2625     sp_nodepath_update_statusbar(nodepath);
2628 /**
2629  * Call sp_nodepath_set_line() for all selected segments.
2630  */
2631 void
2632 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2634     if (nodepath == NULL) return;
2636     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2637        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2638         g_assert(n->selected);
2639         if (n->p.other && n->p.other->selected) {
2640             sp_nodepath_set_line_type(n, code);
2641         }
2642     }
2644     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2647 /**
2648  * Call sp_nodepath_convert_node_type() for all selected nodes.
2649  */
2650 void
2651 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2653     if (nodepath == NULL) return;
2655     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2657     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2658         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2659     }
2661     sp_nodepath_update_repr(nodepath, _("Change node type"));
2664 /**
2665  * Change select status of node, update its own and neighbour handles.
2666  */
2667 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2669     node->selected = selected;
2671     if (selected) {
2672         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2673         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2674         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2675         sp_knot_update_ctrl(node->knot);
2676     } else {
2677         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2678         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2679         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2680         sp_knot_update_ctrl(node->knot);
2681     }
2683     sp_node_update_handles(node);
2684     if (node->n.other) sp_node_update_handles(node->n.other);
2685     if (node->p.other) sp_node_update_handles(node->p.other);
2688 /**
2689 \brief Select a node
2690 \param node     The node to select
2691 \param incremental   If true, add to selection, otherwise deselect others
2692 \param override   If true, always select this node, otherwise toggle selected status
2693 */
2694 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2696     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2698     if (incremental) {
2699         if (override) {
2700             if (!g_list_find(nodepath->selected, node)) {
2701                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2702             }
2703             sp_node_set_selected(node, TRUE);
2704         } else { // toggle
2705             if (node->selected) {
2706                 g_assert(g_list_find(nodepath->selected, node));
2707                 nodepath->selected = g_list_remove(nodepath->selected, node);
2708             } else {
2709                 g_assert(!g_list_find(nodepath->selected, node));
2710                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2711             }
2712             sp_node_set_selected(node, !node->selected);
2713         }
2714     } else {
2715         sp_nodepath_deselect(nodepath);
2716         nodepath->selected = g_list_prepend(nodepath->selected, node);
2717         sp_node_set_selected(node, TRUE);
2718     }
2720     sp_nodepath_update_statusbar(nodepath);
2724 /**
2725 \brief Deselect all nodes in the nodepath
2726 */
2727 void
2728 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2730     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2732     while (nodepath->selected) {
2733         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2734         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2735     }
2736     sp_nodepath_update_statusbar(nodepath);
2739 /**
2740 \brief Select or invert selection of all nodes in the nodepath
2741 */
2742 void
2743 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2745     if (!nodepath) return;
2747     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2748        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2749         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2750            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2751            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2752         }
2753     }
2756 /**
2757  * If nothing selected, does the same as sp_nodepath_select_all();
2758  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2759  * (i.e., similar to "select all in layer", with the "selected" subpaths
2760  * being treated as "layers" in the path).
2761  */
2762 void
2763 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2765     if (!nodepath) return;
2767     if (g_list_length (nodepath->selected) == 0) {
2768         sp_nodepath_select_all (nodepath, invert);
2769         return;
2770     }
2772     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2773     GSList *subpaths = NULL;
2775     for (GList *l = copy; l != NULL; l = l->next) {
2776         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2777         Inkscape::NodePath::SubPath *subpath = n->subpath;
2778         if (!g_slist_find (subpaths, subpath))
2779             subpaths = g_slist_prepend (subpaths, subpath);
2780     }
2782     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2783         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2784         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2785             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2786             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2787         }
2788     }
2790     g_slist_free (subpaths);
2791     g_list_free (copy);
2794 /**
2795  * \brief Select the node after the last selected; if none is selected,
2796  * select the first within path.
2797  */
2798 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2800     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2802    Inkscape::NodePath::Node *last = NULL;
2803     if (nodepath->selected) {
2804         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2805            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2806             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2807             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2808                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2809                 if (node->selected) {
2810                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2811                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2812                             if (spl->next) { // there's a next subpath
2813                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2814                                 last = subpath_next->first;
2815                             } else if (spl->prev) { // there's a previous subpath
2816                                 last = NULL; // to be set later to the first node of first subpath
2817                             } else {
2818                                 last = node->n.other;
2819                             }
2820                         } else {
2821                             last = node->n.other;
2822                         }
2823                     } else {
2824                         if (node->n.other) {
2825                             last = node->n.other;
2826                         } else {
2827                             if (spl->next) { // there's a next subpath
2828                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2829                                 last = subpath_next->first;
2830                             } else if (spl->prev) { // there's a previous subpath
2831                                 last = NULL; // to be set later to the first node of first subpath
2832                             } else {
2833                                 last = (Inkscape::NodePath::Node *) subpath->first;
2834                             }
2835                         }
2836                     }
2837                 }
2838             }
2839         }
2840         sp_nodepath_deselect(nodepath);
2841     }
2843     if (last) { // there's at least one more node after selected
2844         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2845     } else { // no more nodes, select the first one in first subpath
2846        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2847         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2848     }
2851 /**
2852  * \brief Select the node before the first selected; if none is selected,
2853  * select the last within path
2854  */
2855 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2857     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2859    Inkscape::NodePath::Node *last = NULL;
2860     if (nodepath->selected) {
2861         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2862            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2863             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2864                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2865                 if (node->selected) {
2866                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2867                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2868                             if (spl->prev) { // there's a prev subpath
2869                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2870                                 last = subpath_prev->last;
2871                             } else if (spl->next) { // there's a next subpath
2872                                 last = NULL; // to be set later to the last node of last subpath
2873                             } else {
2874                                 last = node->p.other;
2875                             }
2876                         } else {
2877                             last = node->p.other;
2878                         }
2879                     } else {
2880                         if (node->p.other) {
2881                             last = node->p.other;
2882                         } else {
2883                             if (spl->prev) { // there's a prev subpath
2884                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2885                                 last = subpath_prev->last;
2886                             } else if (spl->next) { // there's a next subpath
2887                                 last = NULL; // to be set later to the last node of last subpath
2888                             } else {
2889                                 last = (Inkscape::NodePath::Node *) subpath->last;
2890                             }
2891                         }
2892                     }
2893                 }
2894             }
2895         }
2896         sp_nodepath_deselect(nodepath);
2897     }
2899     if (last) { // there's at least one more node before selected
2900         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2901     } else { // no more nodes, select the last one in last subpath
2902         GList *spl = g_list_last(nodepath->subpaths);
2903        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2904         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2905     }
2908 /**
2909  * \brief Select all nodes that are within the rectangle.
2910  */
2911 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2913     if (!incremental) {
2914         sp_nodepath_deselect(nodepath);
2915     }
2917     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2918        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2919         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2920            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2922             if (b.contains(node->pos)) {
2923                 sp_nodepath_node_select(node, TRUE, TRUE);
2924             }
2925         }
2926     }
2930 void
2931 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2933     g_assert (n);
2934     g_assert (nodepath);
2935     g_assert (n->subpath->nodepath == nodepath);
2937     if (g_list_length (nodepath->selected) == 0) {
2938         if (grow > 0) {
2939             sp_nodepath_node_select(n, TRUE, TRUE);
2940         }
2941         return;
2942     }
2944     if (g_list_length (nodepath->selected) == 1) {
2945         if (grow < 0) {
2946             sp_nodepath_deselect (nodepath);
2947             return;
2948         }
2949     }
2951         double n_sel_range = 0, p_sel_range = 0;
2952             Inkscape::NodePath::Node *farthest_n_node = n;
2953             Inkscape::NodePath::Node *farthest_p_node = n;
2955         // Calculate ranges
2956         {
2957             double n_range = 0, p_range = 0;
2958             bool n_going = true, p_going = true;
2959             Inkscape::NodePath::Node *n_node = n;
2960             Inkscape::NodePath::Node *p_node = n;
2961             do {
2962                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2963                 if (n_node && n_going)
2964                     n_node = n_node->n.other;
2965                 if (n_node == NULL) {
2966                     n_going = false;
2967                 } else {
2968                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2969                     if (n_node->selected) {
2970                         n_sel_range = n_range;
2971                         farthest_n_node = n_node;
2972                     }
2973                     if (n_node == p_node) {
2974                         n_going = false;
2975                         p_going = false;
2976                     }
2977                 }
2978                 if (p_node && p_going)
2979                     p_node = p_node->p.other;
2980                 if (p_node == NULL) {
2981                     p_going = false;
2982                 } else {
2983                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2984                     if (p_node->selected) {
2985                         p_sel_range = p_range;
2986                         farthest_p_node = p_node;
2987                     }
2988                     if (p_node == n_node) {
2989                         n_going = false;
2990                         p_going = false;
2991                     }
2992                 }
2993             } while (n_going || p_going);
2994         }
2996     if (grow > 0) {
2997         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2998                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2999         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3000                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3001         }
3002     } else {
3003         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3004                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3005         } else if (farthest_p_node && farthest_p_node->selected) {
3006                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3007         }
3008     }
3011 void
3012 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3014     g_assert (n);
3015     g_assert (nodepath);
3016     g_assert (n->subpath->nodepath == nodepath);
3018     if (g_list_length (nodepath->selected) == 0) {
3019         if (grow > 0) {
3020             sp_nodepath_node_select(n, TRUE, TRUE);
3021         }
3022         return;
3023     }
3025     if (g_list_length (nodepath->selected) == 1) {
3026         if (grow < 0) {
3027             sp_nodepath_deselect (nodepath);
3028             return;
3029         }
3030     }
3032     Inkscape::NodePath::Node *farthest_selected = NULL;
3033     double farthest_dist = 0;
3035     Inkscape::NodePath::Node *closest_unselected = NULL;
3036     double closest_dist = NR_HUGE;
3038     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3039        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3040         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3041            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3042            if (node == n)
3043                continue;
3044            if (node->selected) {
3045                if (NR::L2(node->pos - n->pos) > farthest_dist) {
3046                    farthest_dist = NR::L2(node->pos - n->pos);
3047                    farthest_selected = node;
3048                }
3049            } else {
3050                if (NR::L2(node->pos - n->pos) < closest_dist) {
3051                    closest_dist = NR::L2(node->pos - n->pos);
3052                    closest_unselected = node;
3053                }
3054            }
3055         }
3056     }
3058     if (grow > 0) {
3059         if (closest_unselected) {
3060             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3061         }
3062     } else {
3063         if (farthest_selected) {
3064             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3065         }
3066     }
3070 /**
3071 \brief  Saves all nodes' and handles' current positions in their origin members
3072 */
3073 void
3074 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3076     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3077        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3078         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3079            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3080            n->origin = n->pos;
3081            n->p.origin = n->p.pos;
3082            n->n.origin = n->n.pos;
3083         }
3084     }
3087 /**
3088 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3089 */
3090 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3092     if (!nodepath->selected) {
3093         return NULL;
3094     }
3096     GList *r = NULL;
3097     guint i = 0;
3098     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3099        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3100         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3101            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3102             i++;
3103             if (node->selected) {
3104                 r = g_list_append(r, GINT_TO_POINTER(i));
3105             }
3106         }
3107     }
3108     return r;
3111 /**
3112 \brief  Restores selection by selecting nodes whose positions are in the list
3113 */
3114 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3116     sp_nodepath_deselect(nodepath);
3118     guint i = 0;
3119     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3120        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3121         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3122            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3123             i++;
3124             if (g_list_find(r, GINT_TO_POINTER(i))) {
3125                 sp_nodepath_node_select(node, TRUE, TRUE);
3126             }
3127         }
3128     }
3132 /**
3133 \brief Adjusts handle according to node type and line code.
3134 */
3135 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3137     g_assert(node);
3139    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3140    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3142    // nothing to do if we are an end node
3143     if (me->other == NULL) return;
3144     if (other->other == NULL) return;
3146     // nothing to do if we are a cusp node
3147     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3149     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3150     NRPathcode mecode;
3151     if (which_adjust == 1) {
3152         mecode = (NRPathcode)me->other->code;
3153     } else {
3154         mecode = (NRPathcode)node->code;
3155     }
3156     if (mecode == NR_LINETO) return;
3158     if (sp_node_side_is_line(node, other)) {
3159         // other is a line, and we are either smooth or symm
3160        Inkscape::NodePath::Node *othernode = other->other;
3161         double len = NR::L2(me->pos - node->pos);
3162         NR::Point delta = node->pos - othernode->pos;
3163         double linelen = NR::L2(delta);
3164         if (linelen < 1e-18)
3165             return;
3166         me->pos = node->pos + (len / linelen)*delta;
3167         return;
3168     }
3170     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3171         // symmetrize 
3172         me->pos = 2 * node->pos - other->pos;
3173         return;
3174     } else {
3175         // smoothify
3176         double len = NR::L2(me->pos - node->pos);
3177         NR::Point delta = other->pos - node->pos;
3178         double otherlen = NR::L2(delta);
3179         if (otherlen < 1e-18) return;
3180         me->pos = node->pos - (len / otherlen) * delta;
3181     }
3184 /**
3185  \brief Adjusts both handles according to node type and line code
3186  */
3187 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3189     g_assert(node);
3191     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3193     /* we are either smooth or symm */
3195     if (node->p.other == NULL) return;
3196     if (node->n.other == NULL) return;
3198     if (sp_node_side_is_line(node, &node->p)) {
3199         sp_node_adjust_handle(node, 1);
3200         return;
3201     }
3203     if (sp_node_side_is_line(node, &node->n)) {
3204         sp_node_adjust_handle(node, -1);
3205         return;
3206     }
3208     /* both are curves */
3209     NR::Point const delta( node->n.pos - node->p.pos );
3211     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3212         node->p.pos = node->pos - delta / 2;
3213         node->n.pos = node->pos + delta / 2;
3214         return;
3215     }
3217     /* We are smooth */
3218     double plen = NR::L2(node->p.pos - node->pos);
3219     if (plen < 1e-18) return;
3220     double nlen = NR::L2(node->n.pos - node->pos);
3221     if (nlen < 1e-18) return;
3222     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3223     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3226 /**
3227  * Node event callback.
3228  */
3229 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3231     gboolean ret = FALSE;
3232     switch (event->type) {
3233         case GDK_ENTER_NOTIFY:
3234             Inkscape::NodePath::Path::active_node = n;
3235             break;
3236         case GDK_LEAVE_NOTIFY:
3237             Inkscape::NodePath::Path::active_node = NULL;
3238             break;
3239         case GDK_SCROLL:
3240             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3241                 switch (event->scroll.direction) {
3242                     case GDK_SCROLL_UP:
3243                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3244                         break;
3245                     case GDK_SCROLL_DOWN:
3246                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3247                         break;
3248                     default:
3249                         break;
3250                 }
3251                 ret = TRUE;
3252             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3253                 switch (event->scroll.direction) {
3254                     case GDK_SCROLL_UP:
3255                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3256                         break;
3257                     case GDK_SCROLL_DOWN:
3258                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3259                         break;
3260                     default:
3261                         break;
3262                 }
3263                 ret = TRUE;
3264             }
3265             break;
3266         case GDK_KEY_PRESS:
3267             switch (get_group0_keyval (&event->key)) {
3268                 case GDK_space:
3269                     if (event->key.state & GDK_BUTTON1_MASK) {
3270                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3271                         stamp_repr(nodepath);
3272                         ret = TRUE;
3273                     }
3274                     break;
3275                 case GDK_Page_Up:
3276                     if (event->key.state & GDK_CONTROL_MASK) {
3277                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3278                     } else {
3279                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3280                     }
3281                     break;
3282                 case GDK_Page_Down:
3283                     if (event->key.state & GDK_CONTROL_MASK) {
3284                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3285                     } else {
3286                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3287                     }
3288                     break;
3289                 default:
3290                     break;
3291             }
3292             break;
3293         default:
3294             break;
3295     }
3297     return ret;
3300 /**
3301  * Handle keypress on node; directly called.
3302  */
3303 gboolean node_key(GdkEvent *event)
3305     Inkscape::NodePath::Path *np;
3307     // there is no way to verify nodes so set active_node to nil when deleting!!
3308     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3310     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3311         gint ret = FALSE;
3312         switch (get_group0_keyval (&event->key)) {
3313             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3314             case GDK_BackSpace:
3315                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3316                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3317                 sp_nodepath_update_repr(np, _("Delete node"));
3318                 Inkscape::NodePath::Path::active_node = NULL;
3319                 ret = TRUE;
3320                 break;
3321             case GDK_c:
3322                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3323                 ret = TRUE;
3324                 break;
3325             case GDK_s:
3326                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3327                 ret = TRUE;
3328                 break;
3329             case GDK_y:
3330                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3331                 ret = TRUE;
3332                 break;
3333             case GDK_b:
3334                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3335                 ret = TRUE;
3336                 break;
3337         }
3338         return ret;
3339     }
3340     return FALSE;
3343 /**
3344  * Mouseclick on node callback.
3345  */
3346 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3348    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3350     if (state & GDK_CONTROL_MASK) {
3351         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3353         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3354             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3355                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3356             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3357                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3358             } else {
3359                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3360             }
3361             sp_nodepath_update_repr(nodepath, _("Change node type"));
3362             sp_nodepath_update_statusbar(nodepath);
3364         } else { //ctrl+alt+click: delete node
3365             GList *node_to_delete = NULL;
3366             node_to_delete = g_list_append(node_to_delete, n);
3367             sp_node_delete_preserve(node_to_delete);
3368         }
3370     } else {
3371         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3372     }
3375 /**
3376  * Mouse grabbed node callback.
3377  */
3378 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3380    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3382     if (!n->selected) {
3383         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3384     }
3386     n->is_dragging = true;
3387     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3389     sp_nodepath_remember_origins (n->subpath->nodepath);
3392 /**
3393  * Mouse ungrabbed node callback.
3394  */
3395 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3397    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3399    n->dragging_out = NULL;
3400    n->is_dragging = false;
3401    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3403    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3406 /**
3407  * The point on a line, given by its angle, closest to the given point.
3408  * \param p  A point.
3409  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3410  * \param closest  Pointer to the point struct where the result is stored.
3411  * \todo FIXME: use dot product perhaps?
3412  */
3413 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3415     if (a == HUGE_VAL) { // vertical
3416         *closest = NR::Point(0, (*p)[NR::Y]);
3417     } else {
3418         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3419         (*closest)[NR::Y] = a * (*closest)[NR::X];
3420     }
3423 /**
3424  * Distance from the point to a line given by its angle.
3425  * \param p  A point.
3426  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3427  */
3428 static double point_line_distance(NR::Point *p, double a)
3430     NR::Point c;
3431     point_line_closest(p, a, &c);
3432     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]));
3435 /**
3436  * Callback for node "request" signal.
3437  * \todo fixme: This goes to "moved" event? (lauris)
3438  */
3439 static gboolean
3440 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3442     double yn, xn, yp, xp;
3443     double an, ap, na, pa;
3444     double d_an, d_ap, d_na, d_pa;
3445     gboolean collinear = FALSE;
3446     NR::Point c;
3447     NR::Point pr;
3449     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3451     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3453     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3454     if ( (!n->subpath->nodepath->straight_path) &&
3455          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3456            || n->dragging_out ) )
3457     {
3458        NR::Point mouse = (*p);
3460        if (!n->dragging_out) {
3461            // This is the first drag-out event; find out which handle to drag out
3462            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3463            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3465            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3466                return FALSE;
3468            Inkscape::NodePath::NodeSide *opposite;
3469            if (appr_p > appr_n) { // closer to p
3470                n->dragging_out = &n->p;
3471                opposite = &n->n;
3472                n->code = NR_CURVETO;
3473            } else if (appr_p < appr_n) { // closer to n
3474                n->dragging_out = &n->n;
3475                opposite = &n->p;
3476                n->n.other->code = NR_CURVETO;
3477            } else { // p and n nodes are the same
3478                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3479                    n->dragging_out = &n->p;
3480                    opposite = &n->n;
3481                    n->code = NR_CURVETO;
3482                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3483                    n->dragging_out = &n->n;
3484                    opposite = &n->p;
3485                    n->n.other->code = NR_CURVETO;
3486                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3487                    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);
3488                    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);
3489                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3490                        n->dragging_out = &n->n;
3491                        opposite = &n->p;
3492                        n->n.other->code = NR_CURVETO;
3493                    } else { // closer to other's n handle
3494                        n->dragging_out = &n->p;
3495                        opposite = &n->n;
3496                        n->code = NR_CURVETO;
3497                    }
3498                }
3499            }
3501            // if there's another handle, make sure the one we drag out starts parallel to it
3502            if (opposite->pos != n->pos) {
3503                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3504            }
3506            // knots might not be created yet!
3507            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3508            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3509        }
3511        // pass this on to the handle-moved callback
3512        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3513        sp_node_update_handles(n);
3514        return TRUE;
3515    }
3517     if (state & GDK_CONTROL_MASK) { // constrained motion
3519         // calculate relative distances of handles
3520         // n handle:
3521         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3522         xn = n->n.pos[NR::X] - n->pos[NR::X];
3523         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3524         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3525             if (n->n.other) { // if there is the next point
3526                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3527                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3528                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3529             }
3530         }
3531         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3532         if (yn < 0) { xn = -xn; yn = -yn; }
3534         // p handle:
3535         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3536         xp = n->p.pos[NR::X] - n->pos[NR::X];
3537         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3538         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3539             if (n->p.other) {
3540                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3541                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3542                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3543             }
3544         }
3545         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3546         if (yp < 0) { xp = -xp; yp = -yp; }
3548         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3549             // sliding on handles, only if at least one of the handles is non-vertical
3550             // (otherwise it's the same as ctrl+drag anyway)
3552             // calculate angles of the handles
3553             if (xn == 0) {
3554                 if (yn == 0) { // no handle, consider it the continuation of the other one
3555                     an = 0;
3556                     collinear = TRUE;
3557                 }
3558                 else an = 0; // vertical; set the angle to horizontal
3559             } else an = yn/xn;
3561             if (xp == 0) {
3562                 if (yp == 0) { // no handle, consider it the continuation of the other one
3563                     ap = an;
3564                 }
3565                 else ap = 0; // vertical; set the angle to horizontal
3566             } else  ap = yp/xp;
3568             if (collinear) an = ap;
3570             // angles of the perpendiculars; HUGE_VAL means vertical
3571             if (an == 0) na = HUGE_VAL; else na = -1/an;
3572             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3574             // mouse point relative to the node's original pos
3575             pr = (*p) - n->origin;
3577             // distances to the four lines (two handles and two perpendiculars)
3578             d_an = point_line_distance(&pr, an);
3579             d_na = point_line_distance(&pr, na);
3580             d_ap = point_line_distance(&pr, ap);
3581             d_pa = point_line_distance(&pr, pa);
3583             // find out which line is the closest, save its closest point in c
3584             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3585                 point_line_closest(&pr, an, &c);
3586             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3587                 point_line_closest(&pr, ap, &c);
3588             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3589                 point_line_closest(&pr, na, &c);
3590             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3591                 point_line_closest(&pr, pa, &c);
3592             }
3594             // move the node to the closest point
3595             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3596                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3597                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y], 
3598                                             true);
3600         } else {  // constraining to hor/vert
3602             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3603                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3604                                                 (*p)[NR::X] - n->pos[NR::X], 
3605                                                 n->origin[NR::Y] - n->pos[NR::Y],
3606                                                 true, 
3607                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::X]));
3608             } else { // snap to vert
3609                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3610                                                 n->origin[NR::X] - n->pos[NR::X],
3611                                                 (*p)[NR::Y] - n->pos[NR::Y],
3612                                                 true,
3613                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::Y]));
3614             }
3615         }
3616     } else { // move freely
3617         if (n->is_dragging) {
3618             if (state & GDK_MOD1_MASK) { // sculpt
3619                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3620             } else {
3621                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3622                                             (*p)[NR::X] - n->pos[NR::X],
3623                                             (*p)[NR::Y] - n->pos[NR::Y],
3624                                             (state & GDK_SHIFT_MASK) == 0);
3625             }
3626         }
3627     }
3629     n->subpath->nodepath->desktop->scroll_to_point(p);
3631     return TRUE;
3634 /**
3635  * Node handle clicked callback.
3636  */
3637 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3639    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3641     if (state & GDK_CONTROL_MASK) { // "delete" handle
3642         if (n->p.knot == knot) {
3643             n->p.pos = n->pos;
3644         } else if (n->n.knot == knot) {
3645             n->n.pos = n->pos;
3646         }
3647         sp_node_update_handles(n);
3648         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3649         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3650         sp_nodepath_update_statusbar(nodepath);
3652     } else { // just select or add to selection, depending in Shift
3653         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3654     }
3657 /**
3658  * Node handle grabbed callback.
3659  */
3660 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3662    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3664     if (!n->selected) {
3665         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3666     }
3668     // remember the origin point of the handle
3669     if (n->p.knot == knot) {
3670         n->p.origin_radial = n->p.pos - n->pos;
3671     } else if (n->n.knot == knot) {
3672         n->n.origin_radial = n->n.pos - n->pos;
3673     } else {
3674         g_assert_not_reached();
3675     }
3677     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3680 /**
3681  * Node handle ungrabbed callback.
3682  */
3683 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3685    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3687     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3688     if (n->p.knot == knot) {
3689         n->p.origin_radial.a = 0;
3690         sp_knot_set_position(knot, &n->p.pos, state);
3691     } else if (n->n.knot == knot) {
3692         n->n.origin_radial.a = 0;
3693         sp_knot_set_position(knot, &n->n.pos, state);
3694     } else {
3695         g_assert_not_reached();
3696     }
3698     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3701 /**
3702  * Node handle "request" signal callback.
3703  */
3704 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint /*state*/, gpointer data)
3706     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3708     Inkscape::NodePath::NodeSide *me, *opposite;
3709     gint which;
3710     if (n->p.knot == knot) {
3711         me = &n->p;
3712         opposite = &n->n;
3713         which = -1;
3714     } else if (n->n.knot == knot) {
3715         me = &n->n;
3716         opposite = &n->p;
3717         which = 1;
3718     } else {
3719         me = opposite = NULL;
3720         which = 0;
3721         g_assert_not_reached();
3722     }
3724     SPDesktop *desktop = n->subpath->nodepath->desktop;
3725     SnapManager &m = desktop->namedview->snap_manager;
3726     m.setup(desktop, n->subpath->nodepath->item);
3727     Inkscape::SnappedPoint s ;
3729     Inkscape::NodePath::Node *othernode = opposite->other;
3730     if (othernode) {
3731         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3732             /* We are smooth node adjacent with line */
3733             NR::Point const delta = *p - n->pos;
3734             NR::Coord const len = NR::L2(delta);
3735             Inkscape::NodePath::Node *othernode = opposite->other;
3736             NR::Point const ndelta = n->pos - othernode->pos;
3737             NR::Coord const linelen = NR::L2(ndelta);
3738             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3739                 NR::Coord const scal = dot(delta, ndelta) / linelen;
3740                 (*p) = n->pos + (scal / linelen) * ndelta;
3741             }
3742             s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta));
3743         } else {
3744             s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3745         }
3746     } else {
3747         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3748     }
3749     
3750     s.getPoint(*p);
3751     
3752     sp_node_adjust_handle(n, -which);
3754     return FALSE;
3757 /**
3758  * Node handle moved callback.
3759  */
3760 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3762    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3764    Inkscape::NodePath::NodeSide *me;
3765    Inkscape::NodePath::NodeSide *other;
3766     if (n->p.knot == knot) {
3767         me = &n->p;
3768         other = &n->n;
3769     } else if (n->n.knot == knot) {
3770         me = &n->n;
3771         other = &n->p;
3772     } else {
3773         me = NULL;
3774         other = NULL;
3775         g_assert_not_reached();
3776     }
3778     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3779     Radial rme(me->pos - n->pos);
3780     Radial rother(other->pos - n->pos);
3781     Radial rnew(*p - n->pos);
3783     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3784         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3785         /* 0 interpreted as "no snapping". */
3787         // 1. Snap to the closest PI/snaps angle, starting from zero.
3788         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3790         // 2. Snap to the original angle, its opposite and perpendiculars
3791         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3792             /* The closest PI/2 angle, starting from original angle */
3793             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3795             // Snap to the closest.
3796             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3797                        ? a_snapped
3798                        : a_ortho );
3799         }
3801         // 3. Snap to the angle of the opposite line, if any
3802         Inkscape::NodePath::Node *othernode = other->other;
3803         if (othernode) {
3804             NR::Point other_to_snap(0,0);
3805             if (sp_node_side_is_line(n, other)) {
3806                 other_to_snap = othernode->pos - n->pos;
3807             } else {
3808                 other_to_snap = other->pos - n->pos;
3809             }
3810             if (NR::L2(other_to_snap) > 1e-3) {
3811                 Radial rother_to_snap(other_to_snap);
3812                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
3813                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
3815                 // Snap to the closest.
3816                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
3817                        ? a_snapped
3818                        : a_oppo );
3819             }
3820         }
3822         rnew.a = a_snapped;
3823     }
3825     if (state & GDK_MOD1_MASK) {
3826         // lock handle length
3827         rnew.r = me->origin_radial.r;
3828     }
3830     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3831         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3832         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3833         rother.a += rnew.a - rme.a;
3834         other->pos = NR::Point(rother) + n->pos;
3835         if (other->knot) {
3836             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3837             sp_knot_moveto(other->knot, &other->pos);
3838         }
3839     }
3841     me->pos = NR::Point(rnew) + n->pos;
3842     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3844     // move knot, but without emitting the signal:
3845     // we cannot emit a "moved" signal because we're now processing it
3846     sp_knot_moveto(me->knot, &(me->pos));
3848     update_object(n->subpath->nodepath);
3850     /* status text */
3851     SPDesktop *desktop = n->subpath->nodepath->desktop;
3852     if (!desktop) return;
3853     SPEventContext *ec = desktop->event_context;
3854     if (!ec) return;
3855     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3856     if (!mc) return;
3858     double degrees = 180 / M_PI * rnew.a;
3859     if (degrees > 180) degrees -= 360;
3860     if (degrees < -180) degrees += 360;
3861     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3862         degrees = angle_to_compass (degrees);
3864     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3866     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
3867          _("<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);
3869     g_string_free(length, TRUE);
3872 /**
3873  * Node handle event callback.
3874  */
3875 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3877     gboolean ret = FALSE;
3878     switch (event->type) {
3879         case GDK_KEY_PRESS:
3880             switch (get_group0_keyval (&event->key)) {
3881                 case GDK_space:
3882                     if (event->key.state & GDK_BUTTON1_MASK) {
3883                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3884                         stamp_repr(nodepath);
3885                         ret = TRUE;
3886                     }
3887                     break;
3888                 default:
3889                     break;
3890             }
3891             break;
3892         case GDK_ENTER_NOTIFY:
3893             // we use an experimentally determined threshold that seems to work fine
3894             if (NR::L2(n->pos - knot->pos) < 0.75)
3895                 Inkscape::NodePath::Path::active_node = n;
3896             break;
3897         case GDK_LEAVE_NOTIFY:
3898             // we use an experimentally determined threshold that seems to work fine
3899             if (NR::L2(n->pos - knot->pos) < 0.75)
3900                 Inkscape::NodePath::Path::active_node = NULL;
3901             break;
3902         default:
3903             break;
3904     }
3906     return ret;
3909 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3910                                  Radial &rme, Radial &rother, gboolean const both)
3912     rme.a += angle;
3913     if ( both
3914          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3915          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3916     {
3917         rother.a += angle;
3918     }
3921 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3922                                         Radial &rme, Radial &rother, gboolean const both)
3924     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3926     gdouble r;
3927     if ( both
3928          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3929          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3930     {
3931         r = MAX(rme.r, rother.r);
3932     } else {
3933         r = rme.r;
3934     }
3936     gdouble const weird_angle = atan2(norm_angle, r);
3937 /* Bulia says norm_angle is just the visible distance that the
3938  * object's end must travel on the screen.  Left as 'angle' for want of
3939  * a better name.*/
3941     rme.a += weird_angle;
3942     if ( both
3943          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3944          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3945     {
3946         rother.a += weird_angle;
3947     }
3950 /**
3951  * Rotate one node.
3952  */
3953 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3955     Inkscape::NodePath::NodeSide *me, *other;
3956     bool both = false;
3958     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3959     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3961     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3962         me = &(n->p);
3963         other = &(n->n);
3964     } else if (!n->p.other) {
3965         me = &(n->n);
3966         other = &(n->p);
3967     } else {
3968         if (which > 0) { // right handle
3969             if (xn > xp) {
3970                 me = &(n->n);
3971                 other = &(n->p);
3972             } else {
3973                 me = &(n->p);
3974                 other = &(n->n);
3975             }
3976         } else if (which < 0){ // left handle
3977             if (xn <= xp) {
3978                 me = &(n->n);
3979                 other = &(n->p);
3980             } else {
3981                 me = &(n->p);
3982                 other = &(n->n);
3983             }
3984         } else { // both handles
3985             me = &(n->n);
3986             other = &(n->p);
3987             both = true;
3988         }
3989     }
3991     Radial rme(me->pos - n->pos);
3992     Radial rother(other->pos - n->pos);
3994     if (screen) {
3995         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3996     } else {
3997         node_rotate_one_internal (*n, angle, rme, rother, both);
3998     }
4000     me->pos = n->pos + NR::Point(rme);
4002     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4003         other->pos =  n->pos + NR::Point(rother);
4004     }
4006     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4007     // so here we just move all the knots without emitting move signals, for speed
4008     sp_node_update_handles(n, false);
4011 /**
4012  * Rotate selected nodes.
4013  */
4014 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4016     if (!nodepath || !nodepath->selected) return;
4018     if (g_list_length(nodepath->selected) == 1) {
4019        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4020         node_rotate_one (n, angle, which, screen);
4021     } else {
4022        // rotate as an object:
4024         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4025         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4026         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4027             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4028             box.expandTo (n->pos); // contain all selected nodes
4029         }
4031         gdouble rot;
4032         if (screen) {
4033             gdouble const zoom = nodepath->desktop->current_zoom();
4034             gdouble const zmove = angle / zoom;
4035             gdouble const r = NR::L2(box.max() - box.midpoint());
4036             rot = atan2(zmove, r);
4037         } else {
4038             rot = angle;
4039         }
4041         NR::Point rot_center;
4042         if (Inkscape::NodePath::Path::active_node == NULL)
4043             rot_center = box.midpoint();
4044         else
4045             rot_center = Inkscape::NodePath::Path::active_node->pos;
4047         NR::Matrix t =
4048             NR::Matrix (NR::translate(-rot_center)) *
4049             NR::Matrix (NR::rotate(rot)) *
4050             NR::Matrix (NR::translate(rot_center));
4052         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4053             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4054             n->pos *= t;
4055             n->n.pos *= t;
4056             n->p.pos *= t;
4057             sp_node_update_handles(n, false);
4058         }
4059     }
4061     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4064 /**
4065  * Scale one node.
4066  */
4067 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4069     bool both = false;
4070     Inkscape::NodePath::NodeSide *me, *other;
4072     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4073     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4075     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4076         me = &(n->p);
4077         other = &(n->n);
4078         n->code = NR_CURVETO;
4079     } else if (!n->p.other) {
4080         me = &(n->n);
4081         other = &(n->p);
4082         if (n->n.other)
4083             n->n.other->code = NR_CURVETO;
4084     } else {
4085         if (which > 0) { // right handle
4086             if (xn > xp) {
4087                 me = &(n->n);
4088                 other = &(n->p);
4089                 if (n->n.other)
4090                     n->n.other->code = NR_CURVETO;
4091             } else {
4092                 me = &(n->p);
4093                 other = &(n->n);
4094                 n->code = NR_CURVETO;
4095             }
4096         } else if (which < 0){ // left handle
4097             if (xn <= xp) {
4098                 me = &(n->n);
4099                 other = &(n->p);
4100                 if (n->n.other)
4101                     n->n.other->code = NR_CURVETO;
4102             } else {
4103                 me = &(n->p);
4104                 other = &(n->n);
4105                 n->code = NR_CURVETO;
4106             }
4107         } else { // both handles
4108             me = &(n->n);
4109             other = &(n->p);
4110             both = true;
4111             n->code = NR_CURVETO;
4112             if (n->n.other)
4113                 n->n.other->code = NR_CURVETO;
4114         }
4115     }
4117     Radial rme(me->pos - n->pos);
4118     Radial rother(other->pos - n->pos);
4120     rme.r += grow;
4121     if (rme.r < 0) rme.r = 0;
4122     if (rme.a == HUGE_VAL) {
4123         if (me->other) { // if direction is unknown, initialize it towards the next node
4124             Radial rme_next(me->other->pos - n->pos);
4125             rme.a = rme_next.a;
4126         } else { // if there's no next, initialize to 0
4127             rme.a = 0;
4128         }
4129     }
4130     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4131         rother.r += grow;
4132         if (rother.r < 0) rother.r = 0;
4133         if (rother.a == HUGE_VAL) {
4134             rother.a = rme.a + M_PI;
4135         }
4136     }
4138     me->pos = n->pos + NR::Point(rme);
4140     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4141         other->pos = n->pos + NR::Point(rother);
4142     }
4144     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4145     // so here we just move all the knots without emitting move signals, for speed
4146     sp_node_update_handles(n, false);
4149 /**
4150  * Scale selected nodes.
4151  */
4152 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4154     if (!nodepath || !nodepath->selected) return;
4156     if (g_list_length(nodepath->selected) == 1) {
4157         // scale handles of the single selected node
4158         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4159         node_scale_one (n, grow, which);
4160     } else {
4161         // scale nodes as an "object":
4163         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4164         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4165         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4166             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4167             box.expandTo (n->pos); // contain all selected nodes
4168         }
4170         double scale = (box.maxExtent() + grow)/box.maxExtent();
4172         NR::Point scale_center;
4173         if (Inkscape::NodePath::Path::active_node == NULL)
4174             scale_center = box.midpoint();
4175         else
4176             scale_center = Inkscape::NodePath::Path::active_node->pos;
4178         NR::Matrix t =
4179             NR::Matrix (NR::translate(-scale_center)) *
4180             NR::Matrix (NR::scale(scale, scale)) *
4181             NR::Matrix (NR::translate(scale_center));
4183         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4184             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4185             n->pos *= t;
4186             n->n.pos *= t;
4187             n->p.pos *= t;
4188             sp_node_update_handles(n, false);
4189         }
4190     }
4192     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4195 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4197     if (!nodepath) return;
4198     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4201 /**
4202  * Flip selected nodes horizontally/vertically.
4203  */
4204 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
4206     if (!nodepath || !nodepath->selected) return;
4208     if (g_list_length(nodepath->selected) == 1 && !center) {
4209         // flip handles of the single selected node
4210         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4211         double temp = n->p.pos[axis];
4212         n->p.pos[axis] = n->n.pos[axis];
4213         n->n.pos[axis] = temp;
4214         sp_node_update_handles(n, false);
4215     } else {
4216         // scale nodes as an "object":
4218         NR::Rect box = sp_node_selected_bbox (nodepath);
4219         if (!center) {
4220             center = box.midpoint();
4221         }
4222         NR::Matrix t =
4223             NR::Matrix (NR::translate(- *center)) *
4224             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
4225             NR::Matrix (NR::translate(*center));
4227         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4228             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4229             n->pos *= t;
4230             n->n.pos *= t;
4231             n->p.pos *= t;
4232             sp_node_update_handles(n, false);
4233         }
4234     }
4236     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4239 NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4241     g_assert (nodepath->selected);
4243     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4244     NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4245     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4246         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4247         box.expandTo (n->pos); // contain all selected nodes
4248     }
4249     return box;
4252 //-----------------------------------------------
4253 /**
4254  * Return new subpath under given nodepath.
4255  */
4256 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4258     g_assert(nodepath);
4259     g_assert(nodepath->desktop);
4261    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4263     s->nodepath = nodepath;
4264     s->closed = FALSE;
4265     s->nodes = NULL;
4266     s->first = NULL;
4267     s->last = NULL;
4269     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4270     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4271     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4273     return s;
4276 /**
4277  * Destroy nodes in subpath, then subpath itself.
4278  */
4279 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4281     g_assert(subpath);
4282     g_assert(subpath->nodepath);
4283     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4285     while (subpath->nodes) {
4286         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4287     }
4289     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4291     g_free(subpath);
4294 /**
4295  * Link head to tail in subpath.
4296  */
4297 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4299     g_assert(!sp->closed);
4300     g_assert(sp->last != sp->first);
4301     g_assert(sp->first->code == NR_MOVETO);
4303     sp->closed = TRUE;
4305     //Link the head to the tail
4306     sp->first->p.other = sp->last;
4307     sp->last->n.other  = sp->first;
4308     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4309     sp->first          = sp->last;
4311     //Remove the extra end node
4312     sp_nodepath_node_destroy(sp->last->n.other);
4315 /**
4316  * Open closed (loopy) subpath at node.
4317  */
4318 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4320     g_assert(sp->closed);
4321     g_assert(n->subpath == sp);
4322     g_assert(sp->first == sp->last);
4324     /* We create new startpoint, current node will become last one */
4326    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4327                                                 &n->pos, &n->pos, &n->n.pos);
4330     sp->closed        = FALSE;
4332     //Unlink to make a head and tail
4333     sp->first         = new_path;
4334     sp->last          = n;
4335     n->n.other        = NULL;
4336     new_path->p.other = NULL;
4339 /**
4340  * Return new node in subpath with given properties.
4341  * \param pos Position of node.
4342  * \param ppos Handle position in previous direction
4343  * \param npos Handle position in previous direction
4344  */
4345 Inkscape::NodePath::Node *
4346 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)
4348     g_assert(sp);
4349     g_assert(sp->nodepath);
4350     g_assert(sp->nodepath->desktop);
4352     if (nodechunk == NULL)
4353         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4355     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4357     n->subpath  = sp;
4359     if (type != Inkscape::NodePath::NODE_NONE) {
4360         // use the type from sodipodi:nodetypes
4361         n->type = type;
4362     } else {
4363         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4364             // points are (almost) collinear
4365             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4366                 // endnode, or a node with a retracted handle
4367                 n->type = Inkscape::NodePath::NODE_CUSP;
4368             } else {
4369                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4370             }
4371         } else {
4372             n->type = Inkscape::NodePath::NODE_CUSP;
4373         }
4374     }
4376     n->code     = code;
4377     n->selected = FALSE;
4378     n->pos      = *pos;
4379     n->p.pos    = *ppos;
4380     n->n.pos    = *npos;
4382     n->dragging_out = NULL;
4384     Inkscape::NodePath::Node *prev;
4385     if (next) {
4386         //g_assert(g_list_find(sp->nodes, next));
4387         prev = next->p.other;
4388     } else {
4389         prev = sp->last;
4390     }
4392     if (prev)
4393         prev->n.other = n;
4394     else
4395         sp->first = n;
4397     if (next)
4398         next->p.other = n;
4399     else
4400         sp->last = n;
4402     n->p.other = prev;
4403     n->n.other = next;
4405     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"));
4406     sp_knot_set_position(n->knot, pos, 0);
4408     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4409     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4410     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4411     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4412     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4413     sp_knot_update_ctrl(n->knot);
4415     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4416     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4417     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4418     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4419     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4420     sp_knot_show(n->knot);
4422     // We only create handle knots and lines on demand
4423     n->p.knot = NULL;
4424     n->p.line = NULL;
4425     n->n.knot = NULL;
4426     n->n.line = NULL;
4428     sp->nodes = g_list_prepend(sp->nodes, n);
4430     return n;
4433 /**
4434  * Destroy node and its knots, link neighbors in subpath.
4435  */
4436 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4438     g_assert(node);
4439     g_assert(node->subpath);
4440     g_assert(SP_IS_KNOT(node->knot));
4442    Inkscape::NodePath::SubPath *sp = node->subpath;
4444     if (node->selected) { // first, deselect
4445         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4446         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4447     }
4449     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4451     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4452     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4453     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4454     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4455     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4456     g_object_unref(G_OBJECT(node->knot));
4458     if (node->p.knot) {
4459         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4460         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4461         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4462         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4463         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4464         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4465         g_object_unref(G_OBJECT(node->p.knot));
4466         node->p.knot = NULL;
4467     }
4469     if (node->n.knot) {
4470         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4471         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4472         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4473         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4474         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4475         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4476         g_object_unref(G_OBJECT(node->n.knot));
4477         node->n.knot = NULL;
4478     }
4480     if (node->p.line)
4481         gtk_object_destroy(GTK_OBJECT(node->p.line));
4482     if (node->n.line)
4483         gtk_object_destroy(GTK_OBJECT(node->n.line));
4485     if (sp->nodes) { // there are others nodes on the subpath
4486         if (sp->closed) {
4487             if (sp->first == node) {
4488                 g_assert(sp->last == node);
4489                 sp->first = node->n.other;
4490                 sp->last = sp->first;
4491             }
4492             node->p.other->n.other = node->n.other;
4493             node->n.other->p.other = node->p.other;
4494         } else {
4495             if (sp->first == node) {
4496                 sp->first = node->n.other;
4497                 sp->first->code = NR_MOVETO;
4498             }
4499             if (sp->last == node) sp->last = node->p.other;
4500             if (node->p.other) node->p.other->n.other = node->n.other;
4501             if (node->n.other) node->n.other->p.other = node->p.other;
4502         }
4503     } else { // this was the last node on subpath
4504         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4505     }
4507     g_mem_chunk_free(nodechunk, node);
4510 /**
4511  * Returns one of the node's two sides.
4512  * \param which Indicates which side.
4513  * \return Pointer to previous node side if which==-1, next if which==1.
4514  */
4515 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4517     g_assert(node);
4519     switch (which) {
4520         case -1:
4521             return &node->p;
4522         case 1:
4523             return &node->n;
4524         default:
4525             break;
4526     }
4528     g_assert_not_reached();
4530     return NULL;
4533 /**
4534  * Return the other side of the node, given one of its sides.
4535  */
4536 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4538     g_assert(node);
4540     if (me == &node->p) return &node->n;
4541     if (me == &node->n) return &node->p;
4543     g_assert_not_reached();
4545     return NULL;
4548 /**
4549  * Return NRPathcode on the given side of the node.
4550  */
4551 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4553     g_assert(node);
4555     if (me == &node->p) {
4556         if (node->p.other) return (NRPathcode)node->code;
4557         return NR_MOVETO;
4558     }
4560     if (me == &node->n) {
4561         if (node->n.other) return (NRPathcode)node->n.other->code;
4562         return NR_MOVETO;
4563     }
4565     g_assert_not_reached();
4567     return NR_END;
4570 /**
4571  * Return node with the given index
4572  */
4573 Inkscape::NodePath::Node *
4574 sp_nodepath_get_node_by_index(int index)
4576     Inkscape::NodePath::Node *e = NULL;
4578     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4579     if (!nodepath) {
4580         return e;
4581     }
4583     //find segment
4584     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4586         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4587         int n = g_list_length(sp->nodes);
4588         if (sp->closed) {
4589             n++;
4590         }
4592         //if the piece belongs to this subpath grab it
4593         //otherwise move onto the next subpath
4594         if (index < n) {
4595             e = sp->first;
4596             for (int i = 0; i < index; ++i) {
4597                 e = e->n.other;
4598             }
4599             break;
4600         } else {
4601             if (sp->closed) {
4602                 index -= (n+1);
4603             } else {
4604                 index -= n;
4605             }
4606         }
4607     }
4609     return e;
4612 /**
4613  * Returns plain text meaning of node type.
4614  */
4615 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4617     unsigned retracted = 0;
4618     bool endnode = false;
4620     for (int which = -1; which <= 1; which += 2) {
4621         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4622         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4623             retracted ++;
4624         if (!side->other)
4625             endnode = true;
4626     }
4628     if (retracted == 0) {
4629         if (endnode) {
4630                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4631                 return _("end node");
4632         } else {
4633             switch (node->type) {
4634                 case Inkscape::NodePath::NODE_CUSP:
4635                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4636                     return _("cusp");
4637                 case Inkscape::NodePath::NODE_SMOOTH:
4638                     // TRANSLATORS: "smooth" is an adjective here
4639                     return _("smooth");
4640                 case Inkscape::NodePath::NODE_SYMM:
4641                     return _("symmetric");
4642             }
4643         }
4644     } else if (retracted == 1) {
4645         if (endnode) {
4646             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4647             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4648         } else {
4649             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4650         }
4651     } else {
4652         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4653     }
4655     return NULL;
4658 /**
4659  * Handles content of statusbar as long as node tool is active.
4660  */
4661 void
4662 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4664     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");
4665     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4667     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4668     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4669     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4670     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4672     SPDesktop *desktop = NULL;
4673     if (nodepath) {
4674         desktop = nodepath->desktop;
4675     } else {
4676         desktop = SP_ACTIVE_DESKTOP;
4677     }
4679     SPEventContext *ec = desktop->event_context;
4680     if (!ec) return;
4681     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4682     if (!mc) return;
4684     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4686     if (selected_nodes == 0) {
4687         Inkscape::Selection *sel = desktop->selection;
4688         if (!sel || sel->isEmpty()) {
4689             mc->setF(Inkscape::NORMAL_MESSAGE,
4690                      _("Select a single object to edit its nodes or handles."));
4691         } else {
4692             if (nodepath) {
4693             mc->setF(Inkscape::NORMAL_MESSAGE,
4694                      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.",
4695                               "<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.",
4696                               total_nodes),
4697                      total_nodes);
4698             } else {
4699                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4700                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4701                 } else {
4702                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4703                 }
4704             }
4705         }
4706     } else if (nodepath && selected_nodes == 1) {
4707         mc->setF(Inkscape::NORMAL_MESSAGE,
4708                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4709                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4710                           total_nodes),
4711                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4712     } else {
4713         if (selected_subpaths > 1) {
4714             mc->setF(Inkscape::NORMAL_MESSAGE,
4715                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4716                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4717                               total_nodes),
4718                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4719         } else {
4720             mc->setF(Inkscape::NORMAL_MESSAGE,
4721                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4722                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4723                               total_nodes),
4724                      selected_nodes, total_nodes, when_selected);
4725         }
4726     }
4729 /*
4730  * returns a *copy* of the curve of that object.
4731  */
4732 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4733     if (!object)
4734         return NULL;
4736     SPCurve *curve = NULL;
4737     if (SP_IS_PATH(object)) {
4738         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4739         curve = curve_new->copy();
4740     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4741         const gchar *svgd = object->repr->attribute(key);
4742         if (svgd) {
4743             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4744             SPCurve *curve_new = new SPCurve(pv);
4745             if (curve_new) {
4746                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4747             }
4748         }
4749     }
4751     return curve;
4754 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4755     if (!np || !np->object || !curve)
4756         return;
4758     if (SP_IS_PATH(np->object)) {
4759         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4760             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4761         } else {
4762             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4763         }
4764     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4765         // FIXME: this writing to string and then reading from string is bound to be slow.
4766         // create a method to convert from curve directly to 2geom...
4767         gchar *svgpath = sp_svg_write_path( np->curve->get_pathvector() );
4768         LIVEPATHEFFECT(np->object)->lpe->setParameter(np->repr_key, svgpath);
4769         g_free(svgpath);
4771         np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4772     }
4775 SPCanvasItem *
4776 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) {
4777     SPCurve *flash_curve = curve->copy();
4778     Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity();
4779     flash_curve->transform(i2d);
4780     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4781     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4782     // unless we also flash the nodes...
4783     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4784     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4785     sp_canvas_item_show(canvasitem);
4786     flash_curve->unref();
4787     return canvasitem;
4790 SPCanvasItem *
4791 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) {
4792     return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path),
4793                                            prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff));
4796 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4797     np->show_helperpath = show;
4799     if (show) {
4800         SPCurve *helper_curve = np->curve->copy();
4801         helper_curve->transform(to_2geom(np->i2d));
4802         if (!np->helper_path) {
4803             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4804             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);
4805             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4806             sp_canvas_item_move_to_z(np->helper_path, 0);
4807             sp_canvas_item_show(np->helper_path);
4808         } else {
4809             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4810         }
4811         helper_curve->unref();
4812     } else {
4813         if (np->helper_path) {
4814             GtkObject *temp = np->helper_path;
4815             np->helper_path = NULL;
4816             gtk_object_destroy(temp);
4817         }
4818     }
4821 /* sp_nodepath_make_straight_path:
4822  *   Prevents user from curving the path by dragging a segment or activating handles etc.
4823  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
4824  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
4825  */
4826 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4827     np->straight_path = true;
4828     np->show_handles = false;
4829     g_message("add code to make the path straight.");
4830     // do sp_nodepath_convert_node_type on all nodes?
4831     // coding tip: search for this text : "Make selected segments lines"
4835 /*
4836   Local Variables:
4837   mode:c++
4838   c-file-style:"stroustrup"
4839   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4840   indent-tabs-mode:nil
4841   fill-column:99
4842   End:
4843 */
4844 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :