Code

noop: address ‘no newline at end of file’ warning
[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 "libnr/n-art-bpath.h"
25 #include "libnr/nr-path.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/parameter/parameter.h"
55 #include "util/mathfns.h"
56 #include "display/snap-indicator.h"
57 #include "snapped-point.h"
59 class NR::Matrix;
61 /// \todo
62 /// evil evil evil. FIXME: conflict of two different Path classes!
63 /// There is a conflict in the namespace between two classes named Path.
64 /// #include "sp-flowtext.h"
65 /// #include "sp-flowregion.h"
67 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
68 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
69 GType sp_flowregion_get_type (void);
70 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
71 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
72 GType sp_flowtext_get_type (void);
73 // end evil workaround
75 #include "helper/stlport.h"
78 /// \todo fixme: Implement these via preferences */
80 #define NODE_FILL          0xbfbfbf00
81 #define NODE_STROKE        0x000000ff
82 #define NODE_FILL_HI       0xff000000
83 #define NODE_STROKE_HI     0x000000ff
84 #define NODE_FILL_SEL      0x0000ffff
85 #define NODE_STROKE_SEL    0x000000ff
86 #define NODE_FILL_SEL_HI   0xff000000
87 #define NODE_STROKE_SEL_HI 0x000000ff
88 #define KNOT_FILL          0xffffffff
89 #define KNOT_STROKE        0x000000ff
90 #define KNOT_FILL_HI       0xff000000
91 #define KNOT_STROKE_HI     0x000000ff
93 static GMemChunk *nodechunk = NULL;
95 /* Creation from object */
97 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
98 static gchar *parse_nodetypes(gchar const *types, gint length);
100 /* Object updating */
102 static void stamp_repr(Inkscape::NodePath::Path *np);
103 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
104 static gchar *create_typestr(Inkscape::NodePath::Path *np);
106 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
108 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
110 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
112 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
114 /* Adjust handle placement, if the node or the other handle is moved */
115 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
116 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
118 /* Node event callbacks */
119 static void node_clicked(SPKnot *knot, guint state, gpointer data);
120 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
121 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
122 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
124 /* Handle event callbacks */
125 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
126 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
127 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
128 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
129 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
130 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
132 /* Constructors and destructors */
134 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
135 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
136 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
137 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
138 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
139                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
140 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
142 /* Helpers */
144 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
145 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
146 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
148 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
149 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
151 // active_node indicates mouseover node
152 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
154 /**
155  * \brief Creates new nodepath from item
156  */
157 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
159     Inkscape::XML::Node *repr = object->repr;
161     /** \todo
162      * FIXME: remove this. We don't want to edit paths inside flowtext.
163      * Instead we will build our flowtext with cloned paths, so that the
164      * real paths are outside the flowtext and thus editable as usual.
165      */
166     if (SP_IS_FLOWTEXT(object)) {
167         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
168             if SP_IS_FLOWREGION(child) {
169                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
170                 if (grandchild && SP_IS_PATH(grandchild)) {
171                     object = SP_ITEM(grandchild);
172                     break;
173                 }
174             }
175         }
176     }
178     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
180     if (curve == NULL)
181         return NULL;
183     NArtBpath *bpath = sp_curve_first_bpath(curve);
184     gint length = curve->end;
185     if (length == 0) {
186         sp_curve_unref(curve);
187         return NULL; // prevent crash for one-node paths
188     }
190     //Create new nodepath
191     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
192     if (!np) {
193         sp_curve_unref(curve);
194         return NULL;
195     }
197     // Set defaults
198     np->desktop     = desktop;
199     np->object      = object;
200     np->subpaths    = NULL;
201     np->selected    = NULL;
202     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
203     np->livarot_path = NULL;
204     np->local_change = 0;
205     np->show_handles = show_handles;
206     np->helper_path = NULL;
207     np->helperpath_rgba = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
208     np->helperpath_width = 1.0;
209     np->curve = sp_curve_copy(curve);
210     np->show_helperpath = prefs_get_int_attribute ("tools.nodes", "show_helperpath",  0) == 1;
211     np->straight_path = false;
212     if (IS_LIVEPATHEFFECT(object) && item) {
213         np->item = item;
214     } else {
215         np->item = SP_ITEM(object);
216     }
218     // we need to update item's transform from the repr here,
219     // because they may be out of sync when we respond
220     // to a change in repr by regenerating nodepath     --bb
221     sp_object_read_attr(SP_OBJECT(np->item), "transform");
223     np->i2d  = sp_item_i2d_affine(np->item);
224     np->d2i  = np->i2d.inverse();
226     np->repr = repr;
227     if (repr_key_in) { // apparantly the object is an LPEObject
228         np->repr_key = g_strdup(repr_key_in);
229         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
230         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
231         if (lpeparam) {
232             lpeparam->param_setup_nodepath(np);
233         }
234     } else {
235         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
236         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
237             np->repr_key = g_strdup("inkscape:original-d");
239             LivePathEffectObject *lpeobj = sp_lpe_item_get_livepatheffectobject(SP_LPE_ITEM(np->object));
240             if (lpeobj && lpeobj->lpe) {
241                 lpeobj->lpe->setup_nodepath(np);
242             }
243         } else {
244             np->repr_key = g_strdup("d");
245         }
246     }
248     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
249     gchar *typestr = parse_nodetypes(nodetypes, length);
251     // create the subpath(s) from the bpath
252     NArtBpath *b = bpath;
253     while (b->code != NR_END) {
254         b = subpath_from_bpath(np, b, typestr + (b - bpath));
255     }
257     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
258     np->subpaths = g_list_reverse(np->subpaths);
260     g_free(typestr);
261     sp_curve_unref(curve);
263     // create the livarot representation from the same item
264     sp_nodepath_ensure_livarot_path(np);
266     // Draw helper curve
267     if (np->show_helperpath) {
268         SPCurve *helper_curve = sp_curve_copy(np->curve);
269         sp_curve_transform(helper_curve, np->i2d );
270         np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(desktop), helper_curve);
271         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);
272         sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
273         sp_canvas_item_move_to_z(np->helper_path, 0);
274         sp_canvas_item_show(np->helper_path);
275         sp_curve_unref(helper_curve);
276     }
278     return np;
281 /**
282  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
283  */
284 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
286     if (!np)  //soft fail, like delete
287         return;
289     while (np->subpaths) {
290         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
291     }
293     //Inform the ShapeEditor that made me, if any, that I am gone.
294     if (np->shape_editor)
295         np->shape_editor->nodepath_destroyed();
297     g_assert(!np->selected);
299     if (np->livarot_path) {
300         delete np->livarot_path;
301         np->livarot_path = NULL;
302     }
304     if (np->helper_path) {
305         GtkObject *temp = np->helper_path;
306         np->helper_path = NULL;
307         gtk_object_destroy(temp);
308     }
309     if (np->curve) {
310         sp_curve_unref(np->curve);
311         np->curve = NULL;
312     }
314     if (np->repr_key) {
315         g_free(np->repr_key);
316         np->repr_key = NULL;
317     }
318     if (np->repr_nodetypes_key) {
319         g_free(np->repr_nodetypes_key);
320         np->repr_nodetypes_key = NULL;
321     }
323     np->desktop = NULL;
325     g_free(np);
329 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
331     if (np && np->livarot_path == NULL) {
332         SPCurve *curve = create_curve(np);
333         NArtBpath *bpath = SP_CURVE_BPATH(curve);
334         np->livarot_path = bpath_to_Path(bpath);
336         if (np->livarot_path)
337             np->livarot_path->ConvertWithBackData(0.01);
339         sp_curve_unref(curve);
340     }
344 /**
345  *  Return the node count of a given NodeSubPath.
346  */
347 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
349     if (!subpath)
350         return 0;
351     gint nodeCount = g_list_length(subpath->nodes);
352     return nodeCount;
355 /**
356  *  Return the node count of a given NodePath.
357  */
358 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
360     if (!np)
361         return 0;
362     gint nodeCount = 0;
363     for (GList *item = np->subpaths ; item ; item=item->next) {
364        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
365         nodeCount += g_list_length(subpath->nodes);
366     }
367     return nodeCount;
370 /**
371  *  Return the subpath count of a given NodePath.
372  */
373 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
375     if (!np)
376         return 0;
377     return g_list_length (np->subpaths);
380 /**
381  *  Return the selected node count of a given NodePath.
382  */
383 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
385     if (!np)
386         return 0;
387     return g_list_length (np->selected);
390 /**
391  *  Return the number of subpaths where nodes are selected in a given NodePath.
392  */
393 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
395     if (!np)
396         return 0;
397     if (!np->selected)
398         return 0;
399     if (!np->selected->next)
400         return 1;
401     gint count = 0;
402     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
403         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
404         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
405             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
406             if (node->selected) {
407                 count ++;
408                 break;
409             }
410         }
411     }
412     return count;
415 /**
416  * Clean up a nodepath after editing.
417  *
418  * Currently we are deleting trivial subpaths.
419  */
420 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
422     GList *badSubPaths = NULL;
424     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
425     for (GList *l = nodepath->subpaths; l ; l=l->next) {
426        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
427        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
428             badSubPaths = g_list_append(badSubPaths, sp);
429     }
431     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
432     //also removes the subpath from nodepath->subpaths
433     for (GList *l = badSubPaths; l ; l=l->next) {
434        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
435         sp_nodepath_subpath_destroy(sp);
436     }
438     g_list_free(badSubPaths);
441 /**
442  * Create new nodepath from b, make it subpath of np.
443  * \param t The node type.
444  * \todo Fixme: t should be a proper type, rather than gchar
445  */
446 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
448     NR::Point ppos, pos, npos;
450     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
452     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
453     bool const closed = (b->code == NR_MOVETO);
455     pos = NR::Point(b->x3, b->y3) * np->i2d;
456     if (b[1].code == NR_CURVETO) {
457         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
458     } else {
459         npos = pos;
460     }
461     Inkscape::NodePath::Node *n;
462     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
463     g_assert(sp->first == n);
464     g_assert(sp->last  == n);
466     b++;
467     t++;
468     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
469         pos = NR::Point(b->x3, b->y3) * np->i2d;
470         if (b->code == NR_CURVETO) {
471             ppos = NR::Point(b->x2, b->y2) * np->i2d;
472         } else {
473             ppos = pos;
474         }
475         if (b[1].code == NR_CURVETO) {
476             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
477         } else {
478             npos = pos;
479         }
480         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
481         b++;
482         t++;
483     }
485     if (closed) sp_nodepath_subpath_close(sp);
487     return b;
490 /**
491  * Convert from sodipodi:nodetypes to new style type string.
492  */
493 static gchar *parse_nodetypes(gchar const *types, gint length)
495     g_assert(length > 0);
497     gchar *typestr = g_new(gchar, length + 1);
499     gint pos = 0;
501     if (types) {
502         for (gint i = 0; types[i] && ( i < length ); i++) {
503             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
504             if (types[i] != '\0') {
505                 switch (types[i]) {
506                     case 's':
507                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
508                         break;
509                     case 'z':
510                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
511                         break;
512                     case 'c':
513                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
514                         break;
515                     default:
516                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
517                         break;
518                 }
519             }
520         }
521     }
523     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
525     return typestr;
528 /**
529  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
530  * updated but repr is not (for speed). Used during curve and node drag.
531  */
532 static void update_object(Inkscape::NodePath::Path *np)
534     g_assert(np);
536     sp_curve_unref(np->curve);
537     np->curve = create_curve(np);
539     sp_nodepath_set_curve(np, np->curve);
541     if (np->show_helperpath) {
542         SPCurve * helper_curve = sp_curve_copy(np->curve);
543         sp_curve_transform(helper_curve, np->i2d );
544         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
545         sp_curve_unref(helper_curve);
546     }
549 /**
550  * Update XML path node with data from path object.
551  */
552 static void update_repr_internal(Inkscape::NodePath::Path *np)
554     g_assert(np);
556     Inkscape::XML::Node *repr = np->object->repr;
558     sp_curve_unref(np->curve);
559     np->curve = create_curve(np);
561     gchar *typestr = create_typestr(np);
562     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
564     // determine if path has an effect applied and write to correct "d" attribute.
565     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
566         np->local_change++;
567         repr->setAttribute(np->repr_key, svgpath);
568     }
570     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
571         np->local_change++;
572         repr->setAttribute(np->repr_nodetypes_key, typestr);
573     }
575     g_free(svgpath);
576     g_free(typestr);
578     if (np->show_helperpath) {
579         SPCurve * helper_curve = sp_curve_copy(np->curve);
580         sp_curve_transform(helper_curve, np->i2d );
581         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
582         sp_curve_unref(helper_curve);
583     }
584  }
586 /**
587  * Update XML path node with data from path object, commit changes forever.
588  */
589 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
591     //fixme: np can be NULL, so check before proceeding
592     g_return_if_fail(np != NULL);
594     if (np->livarot_path) {
595         delete np->livarot_path;
596         np->livarot_path = NULL;
597     }
599     update_repr_internal(np);
600     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
602     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
603                      annotation);
606 /**
607  * Update XML path node with data from path object, commit changes with undo.
608  */
609 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
611     if (np->livarot_path) {
612         delete np->livarot_path;
613         np->livarot_path = NULL;
614     }
616     update_repr_internal(np);
617     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
618                            annotation);
621 /**
622  * Make duplicate of path, replace corresponding XML node in tree, commit.
623  */
624 static void stamp_repr(Inkscape::NodePath::Path *np)
626     g_assert(np);
628     Inkscape::XML::Node *old_repr = np->object->repr;
629     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
631     // remember the position of the item
632     gint pos = old_repr->position();
633     // remember parent
634     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
636     SPCurve *curve = create_curve(np);
637     gchar *typestr = create_typestr(np);
639     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
641     new_repr->setAttribute(np->repr_key, svgpath);
642     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
644     // add the new repr to the parent
645     parent->appendChild(new_repr);
646     // move to the saved position
647     new_repr->setPosition(pos > 0 ? pos : 0);
649     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
650                      _("Stamp"));
652     Inkscape::GC::release(new_repr);
653     g_free(svgpath);
654     g_free(typestr);
655     sp_curve_unref(curve);
658 /**
659  * Create curve from path.
660  */
661 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
663     SPCurve *curve = sp_curve_new();
665     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
666        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
667         sp_curve_moveto(curve,
668                         sp->first->pos * np->d2i);
669        Inkscape::NodePath::Node *n = sp->first->n.other;
670         while (n) {
671             NR::Point const end_pt = n->pos * np->d2i;
672             switch (n->code) {
673                 case NR_LINETO:
674                     sp_curve_lineto(curve, end_pt);
675                     break;
676                 case NR_CURVETO:
677                     sp_curve_curveto(curve,
678                                      n->p.other->n.pos * np->d2i,
679                                      n->p.pos * np->d2i,
680                                      end_pt);
681                     break;
682                 default:
683                     g_assert_not_reached();
684                     break;
685             }
686             if (n != sp->last) {
687                 n = n->n.other;
688             } else {
689                 n = NULL;
690             }
691         }
692         if (sp->closed) {
693             sp_curve_closepath(curve);
694         }
695     }
697     return curve;
700 /**
701  * Convert path type string to sodipodi:nodetypes style.
702  */
703 static gchar *create_typestr(Inkscape::NodePath::Path *np)
705     gchar *typestr = g_new(gchar, 32);
706     gint len = 32;
707     gint pos = 0;
709     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
710        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
712         if (pos >= len) {
713             typestr = g_renew(gchar, typestr, len + 32);
714             len += 32;
715         }
717         typestr[pos++] = 'c';
719        Inkscape::NodePath::Node *n;
720         n = sp->first->n.other;
721         while (n) {
722             gchar code;
724             switch (n->type) {
725                 case Inkscape::NodePath::NODE_CUSP:
726                     code = 'c';
727                     break;
728                 case Inkscape::NodePath::NODE_SMOOTH:
729                     code = 's';
730                     break;
731                 case Inkscape::NodePath::NODE_SYMM:
732                     code = 'z';
733                     break;
734                 default:
735                     g_assert_not_reached();
736                     code = '\0';
737                     break;
738             }
740             if (pos >= len) {
741                 typestr = g_renew(gchar, typestr, len + 32);
742                 len += 32;
743             }
745             typestr[pos++] = code;
747             if (n != sp->last) {
748                 n = n->n.other;
749             } else {
750                 n = NULL;
751             }
752         }
753     }
755     if (pos >= len) {
756         typestr = g_renew(gchar, typestr, len + 1);
757         len += 1;
758     }
760     typestr[pos++] = '\0';
762     return typestr;
765 /**
766  * Returns current path in context. // later eliminate this function at all!
767  */
768 static Inkscape::NodePath::Path *sp_nodepath_current()
770     if (!SP_ACTIVE_DESKTOP) {
771         return NULL;
772     }
774     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
776     if (!SP_IS_NODE_CONTEXT(event_context)) {
777         return NULL;
778     }
780     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
785 /**
786  \brief Fills node and handle positions for three nodes, splitting line
787   marked by end at distance t.
788  */
789 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
791     g_assert(new_path != NULL);
792     g_assert(end      != NULL);
794     g_assert(end->p.other == new_path);
795    Inkscape::NodePath::Node *start = new_path->p.other;
796     g_assert(start);
798     if (end->code == NR_LINETO) {
799         new_path->type =Inkscape::NodePath::NODE_CUSP;
800         new_path->code = NR_LINETO;
801         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
802     } else {
803         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
804         new_path->code = NR_CURVETO;
805         gdouble s      = 1 - t;
806         for (int dim = 0; dim < 2; dim++) {
807             NR::Coord const f000 = start->pos[dim];
808             NR::Coord const f001 = start->n.pos[dim];
809             NR::Coord const f011 = end->p.pos[dim];
810             NR::Coord const f111 = end->pos[dim];
811             NR::Coord const f00t = s * f000 + t * f001;
812             NR::Coord const f01t = s * f001 + t * f011;
813             NR::Coord const f11t = s * f011 + t * f111;
814             NR::Coord const f0tt = s * f00t + t * f01t;
815             NR::Coord const f1tt = s * f01t + t * f11t;
816             NR::Coord const fttt = s * f0tt + t * f1tt;
817             start->n.pos[dim]    = f00t;
818             new_path->p.pos[dim] = f0tt;
819             new_path->pos[dim]   = fttt;
820             new_path->n.pos[dim] = f1tt;
821             end->p.pos[dim]      = f11t;
822         }
823     }
826 /**
827  * Adds new node on direct line between two nodes, activates handles of all
828  * three nodes.
829  */
830 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
832     g_assert(end);
833     g_assert(end->subpath);
834     g_assert(g_list_find(end->subpath->nodes, end));
836    Inkscape::NodePath::Node *start = end->p.other;
837     g_assert( start->n.other == end );
838    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
839                                                end,
840                                                (NRPathcode)end->code == NR_LINETO?
841                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
842                                                (NRPathcode)end->code,
843                                                &start->pos, &start->pos, &start->n.pos);
844     sp_nodepath_line_midpoint(newnode, end, t);
846     sp_node_adjust_handles(start);
847     sp_node_update_handles(start);
848     sp_node_update_handles(newnode);
849     sp_node_adjust_handles(end);
850     sp_node_update_handles(end);
852     return newnode;
855 /**
856 \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
857 */
858 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
860     g_assert(node);
861     g_assert(node->subpath);
862     g_assert(g_list_find(node->subpath->nodes, node));
864    Inkscape::NodePath::SubPath *sp = node->subpath;
865     Inkscape::NodePath::Path *np    = sp->nodepath;
867     if (sp->closed) {
868         sp_nodepath_subpath_open(sp, node);
869         return sp->first;
870     } else {
871         // no break for end nodes
872         if (node == sp->first) return NULL;
873         if (node == sp->last ) return NULL;
875         // create a new subpath
876        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
878         // duplicate the break node as start of the new subpath
879        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
881         while (node->n.other) { // copy the remaining nodes into the new subpath
882            Inkscape::NodePath::Node *n  = node->n.other;
883            Inkscape::NodePath::Node *nn = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
884             if (n->selected) {
885                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
886             }
887             sp_nodepath_node_destroy(n); // remove the point on the original subpath
888         }
890         return newnode;
891     }
894 /**
895  * Duplicate node and connect to neighbours.
896  */
897 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
899     g_assert(node);
900     g_assert(node->subpath);
901     g_assert(g_list_find(node->subpath->nodes, node));
903    Inkscape::NodePath::SubPath *sp = node->subpath;
905     NRPathcode code = (NRPathcode) node->code;
906     if (code == NR_MOVETO) { // if node is the endnode,
907         node->code = NR_LINETO; // new one is inserted before it, so change that to line
908     }
910     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
912     if (!node->n.other || !node->p.other) // if node is an endnode, select it
913         return node;
914     else
915         return newnode; // otherwise select the newly created node
918 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
920     node->p.pos = (node->pos + (node->pos - node->n.pos));
923 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
925     node->n.pos = (node->pos + (node->pos - node->p.pos));
928 /**
929  * Change line type at node, with side effects on neighbours.
930  */
931 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
933     g_assert(end);
934     g_assert(end->subpath);
935     g_assert(end->p.other);
937     if (end->code == static_cast< guint > ( code ) )
938         return;
940    Inkscape::NodePath::Node *start = end->p.other;
942     end->code = code;
944     if (code == NR_LINETO) {
945         if (start->code == NR_LINETO) {
946             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
947         }
948         if (end->n.other) {
949             if (end->n.other->code == NR_LINETO) {
950                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
951             }
952         }
953     } else {
954         NR::Point delta = end->pos - start->pos;
955         start->n.pos = start->pos + delta / 3;
956         end->p.pos = end->pos - delta / 3;
957         sp_node_adjust_handle(start, 1);
958         sp_node_adjust_handle(end, -1);
959     }
961     sp_node_update_handles(start);
962     sp_node_update_handles(end);
965 /**
966  * Change node type, and its handles accordingly.
967  */
968 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
970     g_assert(node);
971     g_assert(node->subpath);
973     if ((node->p.other != NULL) && (node->n.other != NULL)) {
974         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
975             type =Inkscape::NodePath::NODE_CUSP;
976         }
977     }
979     node->type = type;
981     if (node->type == Inkscape::NodePath::NODE_CUSP) {
982         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
983         node->knot->setSize (node->selected? 11 : 9);
984         sp_knot_update_ctrl(node->knot);
985     } else {
986         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
987         node->knot->setSize (node->selected? 9 : 7);
988         sp_knot_update_ctrl(node->knot);
989     }
991     // if one of handles is mouseovered, preserve its position
992     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
993         sp_node_adjust_handle(node, 1);
994     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
995         sp_node_adjust_handle(node, -1);
996     } else {
997         sp_node_adjust_handles(node);
998     }
1000     sp_node_update_handles(node);
1002     sp_nodepath_update_statusbar(node->subpath->nodepath);
1004     return node;
1007 bool
1008 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1010         Inkscape::NodePath::Node *othernode = side->other;
1011         if (!othernode)
1012             return false;
1013         NRPathcode const code = sp_node_path_code_from_side(node, side);
1014         if (code == NR_LINETO)
1015             return true;
1016         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1017         if (&node->p == side) {
1018             other_to_me = &othernode->n;
1019         } else if (&node->n == side) {
1020             other_to_me = &othernode->p;
1021         } 
1022         if (!other_to_me)
1023             return false;
1024         bool is_line = 
1025              (NR::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1026               NR::L2(node->pos - side->pos) < 1e-6);
1027         return is_line;
1030 /**
1031  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1032  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1033  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1034  * If already cusp and set to cusp, retracts handles.
1035 */
1036 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1038     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1040 /* 
1041   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1042  
1043         if (two_handles) {
1044             // do nothing, adjust_handles called via set_node_type will line them up
1045         } else if (one_handle) {
1046             if (opposite_to_handle_is_line) {
1047                 if (lined_up) {
1048                     // already half-smooth; pull opposite handle too making it fully smooth
1049                 } else {
1050                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1051                 }
1052             } else {
1053                 // pull opposite handle in line with the existing one
1054             }
1055         } else if (no_handles) {
1056             if (both_segments_are_lines OR both_segments_are_curves) {
1057                 //pull both handles
1058             } else {
1059                 // pull the handle opposite to line segment, making node half-smooth
1060             }
1061         }
1062 */
1063         bool p_has_handle = (NR::L2(node->pos  - node->p.pos) > 1e-6);
1064         bool n_has_handle = (NR::L2(node->pos  - node->n.pos) > 1e-6);
1065         bool p_is_line = sp_node_side_is_line(node, &node->p);
1066         bool n_is_line = sp_node_side_is_line(node, &node->n);
1068         if (p_has_handle && n_has_handle) {
1069             // do nothing, adjust_handles will line them up
1070         } else if (p_has_handle || n_has_handle) {
1071             if (p_has_handle && n_is_line) {
1072                 Radial line (node->n.other->pos - node->pos);
1073                 Radial handle (node->pos - node->p.pos);
1074                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1075                     // already half-smooth; pull opposite handle too making it fully smooth
1076                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1077                 } else {
1078                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1079                 }
1080             } else if (n_has_handle && p_is_line) {
1081                 Radial line (node->p.other->pos - node->pos);
1082                 Radial handle (node->pos - node->n.pos);
1083                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1084                     // already half-smooth; pull opposite handle too making it fully smooth
1085                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1086                 } else {
1087                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1088                 }
1089             } else if (p_has_handle && node->n.other) {
1090                 // pull n handle
1091                 node->n.other->code = NR_CURVETO;
1092                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1093                     NR::L2(node->p.pos - node->pos) :
1094                     NR::L2(node->n.other->pos - node->pos) / 3;
1095                 node->n.pos = node->pos - (len / NR::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1096             } else if (n_has_handle && node->p.other) {
1097                 // pull p handle
1098                 node->code = NR_CURVETO;
1099                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1100                     NR::L2(node->n.pos - node->pos) :
1101                     NR::L2(node->p.other->pos - node->pos) / 3;
1102                 node->p.pos = node->pos - (len / NR::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1103             }
1104         } else if (!p_has_handle && !n_has_handle) {
1105             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1106                 // no handles, but both segments are either lnes or curves:
1107                 //pull both handles
1109                 // convert both to curves:
1110                 node->code = NR_CURVETO;
1111                 node->n.other->code = NR_CURVETO;
1113                 NR::Point leg_prev = node->pos - node->p.other->pos;
1114                 NR::Point leg_next = node->pos - node->n.other->pos;
1116                 double norm_leg_prev = L2(leg_prev);
1117                 double norm_leg_next = L2(leg_next);
1119                 NR::Point delta;
1120                 if (norm_leg_next > 0.0) {
1121                     delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1122                     (&delta)->normalize();
1123                 }
1125                 if (type == Inkscape::NodePath::NODE_SYMM) {
1126                     double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1127                     node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1128                     node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1129                 } else {
1130                     // length of handle is proportional to distance to adjacent node
1131                     node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1132                     node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1133                 }
1135             } else {
1136                 // pull the handle opposite to line segment, making it half-smooth
1137                 if (p_is_line && node->n.other) {
1138                     if (type != Inkscape::NodePath::NODE_SYMM) {
1139                         // pull n handle
1140                         node->n.other->code = NR_CURVETO;
1141                         double len =  NR::L2(node->n.other->pos - node->pos) / 3;
1142                         node->n.pos = node->pos + (len / NR::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1143                     }
1144                 } else if (n_is_line && node->p.other) {
1145                     if (type != Inkscape::NodePath::NODE_SYMM) {
1146                         // pull p handle
1147                         node->code = NR_CURVETO;
1148                         double len =  NR::L2(node->p.other->pos - node->pos) / 3;
1149                         node->p.pos = node->pos + (len / NR::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1150                     }
1151                 }
1152             }
1153         }
1154     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1155         // cusping a cusp: retract nodes
1156         node->p.pos = node->pos;
1157         node->n.pos = node->pos;
1158     }
1160     sp_nodepath_set_node_type (node, type);
1163 /**
1164  * Move node to point, and adjust its and neighbouring handles.
1165  */
1166 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1168     NR::Point delta = p - node->pos;
1169     node->pos = p;
1171     node->p.pos += delta;
1172     node->n.pos += delta;
1174     Inkscape::NodePath::Node *node_p = NULL;
1175     Inkscape::NodePath::Node *node_n = NULL;
1177     if (node->p.other) {
1178         if (node->code == NR_LINETO) {
1179             sp_node_adjust_handle(node, 1);
1180             sp_node_adjust_handle(node->p.other, -1);
1181             node_p = node->p.other;
1182         }
1183     }
1184     if (node->n.other) {
1185         if (node->n.other->code == NR_LINETO) {
1186             sp_node_adjust_handle(node, -1);
1187             sp_node_adjust_handle(node->n.other, 1);
1188             node_n = node->n.other;
1189         }
1190     }
1192     // this function is only called from batch movers that will update display at the end
1193     // themselves, so here we just move all the knots without emitting move signals, for speed
1194     sp_node_update_handles(node, false);
1195     if (node_n) {
1196         sp_node_update_handles(node_n, false);
1197     }
1198     if (node_p) {
1199         sp_node_update_handles(node_p, false);
1200     }
1203 /**
1204  * Call sp_node_moveto() for node selection and handle possible snapping.
1205  */
1206 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1207                                             bool const snap = true)
1209     NR::Coord best = NR_HUGE;
1210     NR::Point delta(dx, dy);
1211     NR::Point best_pt = delta;
1212     Inkscape::SnappedPoint best_abs;
1214     
1215     if (snap) {    
1216         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1217          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1218          * must provide that information. */
1219           
1220         // Build a list of the unselected nodes to which the snapper should snap 
1221         std::vector<NR::Point> unselected_nodes;
1222         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1223             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1224             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1225                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1226                 if (!node->selected) {
1227                     unselected_nodes.push_back(node->pos);
1228                 }    
1229             }
1230         }        
1231         
1232         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1233         
1234         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1235             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1236             Inkscape::SnappedPoint s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);            
1237             if (s.getDistance() < best) {
1238                 best = s.getDistance();
1239                 best_abs = s;
1240                 best_pt = s.getPoint() - n->pos;
1241             }
1242         }
1243                         
1244         if (best_abs.getSnapped()) {
1245             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1246         }
1247     }
1249     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1250         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1251         sp_node_moveto(n, n->pos + best_pt);
1252     }
1254     // do not update repr here so that node dragging is acceptably fast
1255     update_object(nodepath);
1258 /**
1259 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1260 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1261 near x = 0.
1262  */
1263 double
1264 sculpt_profile (double x, double alpha, guint profile)
1266     if (x >= 1)
1267         return 0;
1268     if (x <= 0)
1269         return 1;
1271     switch (profile) {
1272         case SCULPT_PROFILE_LINEAR:
1273         return 1 - x;
1274         case SCULPT_PROFILE_BELL:
1275         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1276         case SCULPT_PROFILE_ELLIPTIC:
1277         return sqrt(1 - x*x);
1278     }
1280     return 1;
1283 double
1284 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1286     // extremely primitive for now, don't have time to look for the real one
1287     double lower = NR::L2(b - a);
1288     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1289     return (lower + upper)/2;
1292 void
1293 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1295     n->pos = n->origin + delta;
1296     n->n.pos = n->n.origin + delta_n;
1297     n->p.pos = n->p.origin + delta_p;
1298     sp_node_adjust_handles(n);
1299     sp_node_update_handles(n, false);
1302 /**
1303  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1304  * on how far they are from the dragged node n.
1305  */
1306 static void
1307 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1309     g_assert (n);
1310     g_assert (nodepath);
1311     g_assert (n->subpath->nodepath == nodepath);
1313     double pressure = n->knot->pressure;
1314     if (pressure == 0)
1315         pressure = 0.5; // default
1316     pressure = CLAMP (pressure, 0.2, 0.8);
1318     // map pressure to alpha = 1/5 ... 5
1319     double alpha = 1 - 2 * fabs(pressure - 0.5);
1320     if (pressure > 0.5)
1321         alpha = 1/alpha;
1323     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1325     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1326         // Only one subpath has selected nodes:
1327         // use linear mode, where the distance from n to node being dragged is calculated along the path
1329         double n_sel_range = 0, p_sel_range = 0;
1330         guint n_nodes = 0, p_nodes = 0;
1331         guint n_sel_nodes = 0, p_sel_nodes = 0;
1333         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1334         {
1335             double n_range = 0, p_range = 0;
1336             bool n_going = true, p_going = true;
1337             Inkscape::NodePath::Node *n_node = n;
1338             Inkscape::NodePath::Node *p_node = n;
1339             do {
1340                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1341                 if (n_node && n_going)
1342                     n_node = n_node->n.other;
1343                 if (n_node == NULL) {
1344                     n_going = false;
1345                 } else {
1346                     n_nodes ++;
1347                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1348                     if (n_node->selected) {
1349                         n_sel_nodes ++;
1350                         n_sel_range = n_range;
1351                     }
1352                     if (n_node == p_node) {
1353                         n_going = false;
1354                         p_going = false;
1355                     }
1356                 }
1357                 if (p_node && p_going)
1358                     p_node = p_node->p.other;
1359                 if (p_node == NULL) {
1360                     p_going = false;
1361                 } else {
1362                     p_nodes ++;
1363                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1364                     if (p_node->selected) {
1365                         p_sel_nodes ++;
1366                         p_sel_range = p_range;
1367                     }
1368                     if (p_node == n_node) {
1369                         n_going = false;
1370                         p_going = false;
1371                     }
1372                 }
1373             } while (n_going || p_going);
1374         }
1376         // Second pass: actually move nodes in this subpath
1377         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1378         {
1379             double n_range = 0, p_range = 0;
1380             bool n_going = true, p_going = true;
1381             Inkscape::NodePath::Node *n_node = n;
1382             Inkscape::NodePath::Node *p_node = n;
1383             do {
1384                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1385                 if (n_node && n_going)
1386                     n_node = n_node->n.other;
1387                 if (n_node == NULL) {
1388                     n_going = false;
1389                 } else {
1390                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1391                     if (n_node->selected) {
1392                         sp_nodepath_move_node_and_handles (n_node,
1393                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1394                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1395                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1396                     }
1397                     if (n_node == p_node) {
1398                         n_going = false;
1399                         p_going = false;
1400                     }
1401                 }
1402                 if (p_node && p_going)
1403                     p_node = p_node->p.other;
1404                 if (p_node == NULL) {
1405                     p_going = false;
1406                 } else {
1407                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1408                     if (p_node->selected) {
1409                         sp_nodepath_move_node_and_handles (p_node,
1410                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1411                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1412                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1413                     }
1414                     if (p_node == n_node) {
1415                         n_going = false;
1416                         p_going = false;
1417                     }
1418                 }
1419             } while (n_going || p_going);
1420         }
1422     } else {
1423         // Multiple subpaths have selected nodes:
1424         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1425         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1426         // fix the pear-like shape when sculpting e.g. a ring
1428         // First pass: calculate range
1429         gdouble direct_range = 0;
1430         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1431             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1432             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1433                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1434                 if (node->selected) {
1435                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1436                 }
1437             }
1438         }
1440         // Second pass: actually move nodes
1441         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1442             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1443             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1444                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1445                 if (node->selected) {
1446                     if (direct_range > 1e-6) {
1447                         sp_nodepath_move_node_and_handles (node,
1448                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1449                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1450                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1451                     } else {
1452                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1453                     }
1455                 }
1456             }
1457         }
1458     }
1460     // do not update repr here so that node dragging is acceptably fast
1461     update_object(nodepath);
1465 /**
1466  * Move node selection to point, adjust its and neighbouring handles,
1467  * handle possible snapping, and commit the change with possible undo.
1468  */
1469 void
1470 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1472     if (!nodepath) return;
1474     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1476     if (dx == 0) {
1477         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1478     } else if (dy == 0) {
1479         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1480     } else {
1481         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1482     }
1485 /**
1486  * Move node selection off screen and commit the change.
1487  */
1488 void
1489 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1491     // borrowed from sp_selection_move_screen in selection-chemistry.c
1492     // we find out the current zoom factor and divide deltas by it
1493     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1495     gdouble zoom = desktop->current_zoom();
1496     gdouble zdx = dx / zoom;
1497     gdouble zdy = dy / zoom;
1499     if (!nodepath) return;
1501     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1503     if (dx == 0) {
1504         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1505     } else if (dy == 0) {
1506         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1507     } else {
1508         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1509     }
1512 /**
1513  * Move selected nodes to the absolute position given
1514  */
1515 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis)
1517     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1518         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1519         NR::Point npos(axis == NR::X ? val : n->pos[NR::X], axis == NR::Y ? val : n->pos[NR::Y]);
1520         sp_node_moveto(n, npos);
1521     }
1523     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1526 /**
1527  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1528  */
1529 NR::Maybe<NR::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1531     NR::Maybe<NR::Coord> no_coord = NR::Nothing();
1532     g_return_val_if_fail(nodepath->selected, no_coord);
1534     // determine coordinate of first selected node
1535     GList *nsel = nodepath->selected;
1536     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1537     NR::Coord coord = n->pos[axis];
1538     bool coincide = true;
1540     // compare it to the coordinates of all the other selected nodes
1541     for (GList *l = nsel->next; l != NULL; l = l->next) {
1542         n = (Inkscape::NodePath::Node *) l->data;
1543         if (n->pos[axis] != coord) {
1544             coincide = false;
1545         }
1546     }
1547     if (coincide) {
1548         return coord;
1549     } else {
1550         NR::Rect bbox = sp_node_selected_bbox(nodepath);
1551         // currently we return the coordinate of the bounding box midpoint because I don't know how
1552         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1553         return bbox.midpoint()[axis];
1554     }
1557 /** If they don't yet exist, creates knot and line for the given side of the node */
1558 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1560     if (!side->knot) {
1561         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"));
1563         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1564         side->knot->setSize (7);
1565         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1566         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1567         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1568         sp_knot_update_ctrl(side->knot);
1570         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1571         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1572         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1573         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1574         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1575         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1576     }
1578     if (!side->line) {
1579         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1580                                         SP_TYPE_CTRLLINE, NULL);
1581     }
1584 /**
1585  * Ensure the given handle of the node is visible/invisible, update its screen position
1586  */
1587 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1589     g_assert(node != NULL);
1591    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1592     NRPathcode code = sp_node_path_code_from_side(node, side);
1594     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1596     if (show_handle) {
1597         if (!side->knot) { // No handle knot at all
1598             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1599             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1600             side->knot->pos = side->pos;
1601             if (side->knot->item)
1602                 SP_CTRL(side->knot->item)->moveto(side->pos);
1603             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1604             sp_knot_show(side->knot);
1605         } else {
1606             if (side->knot->pos != side->pos) { // only if it's really moved
1607                 if (fire_move_signals) {
1608                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1609                 } else {
1610                     sp_knot_moveto(side->knot, &side->pos);
1611                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1612                 }
1613             }
1614             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1615                 sp_knot_show(side->knot);
1616             }
1617         }
1618         sp_canvas_item_show(side->line);
1619     } else {
1620         if (side->knot) {
1621             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1622                 sp_knot_hide(side->knot);
1623             }
1624         }
1625         if (side->line) {
1626             sp_canvas_item_hide(side->line);
1627         }
1628     }
1631 /**
1632  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1633  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1634  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1635  * updated; otherwise, just move the knots silently (used in batch moves).
1636  */
1637 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1639     g_assert(node != NULL);
1641     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1642         sp_knot_show(node->knot);
1643     }
1645     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1646         if (fire_move_signals)
1647             sp_knot_set_position(node->knot, &node->pos, 0);
1648         else
1649             sp_knot_moveto(node->knot, &node->pos);
1650     }
1652     gboolean show_handles = node->selected;
1653     if (node->p.other != NULL) {
1654         if (node->p.other->selected) show_handles = TRUE;
1655     }
1656     if (node->n.other != NULL) {
1657         if (node->n.other->selected) show_handles = TRUE;
1658     }
1660     if (node->subpath->nodepath->show_handles == false)
1661         show_handles = FALSE;
1663     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1664     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1667 /**
1668  * Call sp_node_update_handles() for all nodes on subpath.
1669  */
1670 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1672     g_assert(subpath != NULL);
1674     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1675         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1676     }
1679 /**
1680  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1681  */
1682 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1684     g_assert(nodepath != NULL);
1686     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1687         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1688     }
1691 void
1692 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1694     if (nodepath == NULL) return;
1696     nodepath->show_handles = show;
1697     sp_nodepath_update_handles(nodepath);
1700 /**
1701  * Adds all selected nodes in nodepath to list.
1702  */
1703 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1705     StlConv<Node *>::list(l, selected);
1706 /// \todo this adds a copying, rework when the selection becomes a stl list
1709 /**
1710  * Align selected nodes on the specified axis.
1711  */
1712 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1714     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1715         return;
1716     }
1718     if ( !nodepath->selected->next ) { // only one node selected
1719         return;
1720     }
1721    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1722     NR::Point dest(pNode->pos);
1723     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1724         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1725         if (pNode) {
1726             dest[axis] = pNode->pos[axis];
1727             sp_node_moveto(pNode, dest);
1728         }
1729     }
1731     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1734 /// Helper struct.
1735 struct NodeSort
1737    Inkscape::NodePath::Node *_node;
1738     NR::Coord _coord;
1739     /// \todo use vectorof pointers instead of calling copy ctor
1740     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1741         _node(node), _coord(node->pos[axis])
1742     {}
1744 };
1746 static bool operator<(NodeSort const &a, NodeSort const &b)
1748     return (a._coord < b._coord);
1751 /**
1752  * Distribute selected nodes on the specified axis.
1753  */
1754 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1756     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1757         return;
1758     }
1760     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1761         return;
1762     }
1764    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1765     std::vector<NodeSort> sorted;
1766     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1767         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1768         if (pNode) {
1769             NodeSort n(pNode, axis);
1770             sorted.push_back(n);
1771             //dest[axis] = pNode->pos[axis];
1772             //sp_node_moveto(pNode, dest);
1773         }
1774     }
1775     std::sort(sorted.begin(), sorted.end());
1776     unsigned int len = sorted.size();
1777     //overall bboxes span
1778     float dist = (sorted.back()._coord -
1779                   sorted.front()._coord);
1780     //new distance between each bbox
1781     float step = (dist) / (len - 1);
1782     float pos = sorted.front()._coord;
1783     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1784           it < sorted.end();
1785           it ++ )
1786     {
1787         NR::Point dest((*it)._node->pos);
1788         dest[axis] = pos;
1789         sp_node_moveto((*it)._node, dest);
1790         pos += step;
1791     }
1793     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1797 /**
1798  * Call sp_nodepath_line_add_node() for all selected segments.
1799  */
1800 void
1801 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1803     if (!nodepath) {
1804         return;
1805     }
1807     GList *nl = NULL;
1809     int n_added = 0;
1811     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1812        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1813         g_assert(t->selected);
1814         if (t->p.other && t->p.other->selected) {
1815             nl = g_list_prepend(nl, t);
1816         }
1817     }
1819     while (nl) {
1820        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1821        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1822        sp_nodepath_node_select(n, TRUE, FALSE);
1823        n_added ++;
1824        nl = g_list_remove(nl, t);
1825     }
1827     /** \todo fixme: adjust ? */
1828     sp_nodepath_update_handles(nodepath);
1830     if (n_added > 1) {
1831         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1832     } else if (n_added > 0) {
1833         sp_nodepath_update_repr(nodepath, _("Add node"));
1834     }
1836     sp_nodepath_update_statusbar(nodepath);
1839 /**
1840  * Select segment nearest to point
1841  */
1842 void
1843 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1845     if (!nodepath) {
1846         return;
1847     }
1849     sp_nodepath_ensure_livarot_path(nodepath);
1850     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1851     if (!maybe_position) {
1852         return;
1853     }
1854     Path::cut_position position = *maybe_position;
1856     //find segment to segment
1857     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1859     //fixme: this can return NULL, so check before proceeding.
1860     g_return_if_fail(e != NULL);
1862     gboolean force = FALSE;
1863     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1864         force = TRUE;
1865     }
1866     sp_nodepath_node_select(e, (gboolean) toggle, force);
1867     if (e->p.other)
1868         sp_nodepath_node_select(e->p.other, TRUE, force);
1870     sp_nodepath_update_handles(nodepath);
1872     sp_nodepath_update_statusbar(nodepath);
1875 /**
1876  * Add a node nearest to point
1877  */
1878 void
1879 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1881     if (!nodepath) {
1882         return;
1883     }
1885     sp_nodepath_ensure_livarot_path(nodepath);
1886     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1887     if (!maybe_position) {
1888         return;
1889     }
1890     Path::cut_position position = *maybe_position;
1892     //find segment to split
1893     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1895     //don't know why but t seems to flip for lines
1896     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1897         position.t = 1.0 - position.t;
1898     }
1899     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1900     sp_nodepath_node_select(n, FALSE, TRUE);
1902     /* fixme: adjust ? */
1903     sp_nodepath_update_handles(nodepath);
1905     sp_nodepath_update_repr(nodepath, _("Add node"));
1907     sp_nodepath_update_statusbar(nodepath);
1910 /*
1911  * Adjusts a segment so that t moves by a certain delta for dragging
1912  * converts lines to curves
1913  *
1914  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1915  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1916  */
1917 void
1918 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1920     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1922     //fixme: e and e->p can be NULL, so check for those before proceeding
1923     g_return_if_fail(e != NULL);
1924     g_return_if_fail(&e->p != NULL);
1926     /* feel good is an arbitrary parameter that distributes the delta between handles
1927      * if t of the drag point is less than 1/6 distance form the endpoint only
1928      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1929      */
1930     double feel_good;
1931     if (t <= 1.0 / 6.0)
1932         feel_good = 0;
1933     else if (t <= 0.5)
1934         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1935     else if (t <= 5.0 / 6.0)
1936         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1937     else
1938         feel_good = 1;
1940     //if we're dragging a line convert it to a curve
1941     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1942         sp_nodepath_set_line_type(e, NR_CURVETO);
1943     }
1945     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1946     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1947     e->p.other->n.pos += offsetcoord0;
1948     e->p.pos += offsetcoord1;
1950     // adjust handles of adjacent nodes where necessary
1951     sp_node_adjust_handle(e,1);
1952     sp_node_adjust_handle(e->p.other,-1);
1954     sp_nodepath_update_handles(e->subpath->nodepath);
1956     update_object(e->subpath->nodepath);
1958     sp_nodepath_update_statusbar(e->subpath->nodepath);
1962 /**
1963  * Call sp_nodepath_break() for all selected segments.
1964  */
1965 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
1967     if (!nodepath) return;
1969     GList *temp = NULL;
1970     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1971        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1972        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1973         if (nn == NULL) continue; // no break, no new node
1974         temp = g_list_prepend(temp, nn);
1975     }
1977     if (temp) {
1978         sp_nodepath_deselect(nodepath);
1979     }
1980     for (GList *l = temp; l != NULL; l = l->next) {
1981         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1982     }
1984     sp_nodepath_update_handles(nodepath);
1986     sp_nodepath_update_repr(nodepath, _("Break path"));
1989 /**
1990  * Duplicate the selected node(s).
1991  */
1992 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
1994     if (!nodepath) {
1995         return;
1996     }
1998     GList *temp = NULL;
1999     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2000        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2001        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2002         if (nn == NULL) continue; // could not duplicate
2003         temp = g_list_prepend(temp, nn);
2004     }
2006     if (temp) {
2007         sp_nodepath_deselect(nodepath);
2008     }
2009     for (GList *l = temp; l != NULL; l = l->next) {
2010         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2011     }
2013     sp_nodepath_update_handles(nodepath);
2015     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2018 /**
2019  *  Internal function to join two nodes by merging them into one.
2020  */
2021 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2023     /* a and b are endpoints */
2025     // if one of the two nodes is mouseovered, fix its position
2026     NR::Point c;
2027     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2028         c = a->pos;
2029     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2030         c = b->pos;
2031     } else {
2032         // otherwise, move joined node to the midpoint
2033         c = (a->pos + b->pos) / 2;
2034     }
2036     if (a->subpath == b->subpath) {
2037        Inkscape::NodePath::SubPath *sp = a->subpath;
2038         sp_nodepath_subpath_close(sp);
2039         sp_node_moveto (sp->first, c);
2041         sp_nodepath_update_handles(sp->nodepath);
2042         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2043         return;
2044     }
2046     /* a and b are separate subpaths */
2047     Inkscape::NodePath::SubPath *sa = a->subpath;
2048     Inkscape::NodePath::SubPath *sb = b->subpath;
2049     NR::Point p;
2050     Inkscape::NodePath::Node *n;
2051     NRPathcode code;
2052     if (a == sa->first) {
2053         // we will now reverse sa, so that a is its last node, not first, and drop that node
2054         p = sa->first->n.pos;
2055         code = (NRPathcode)sa->first->n.other->code;
2056         // create new subpath
2057        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2058        // create a first moveto node on it
2059         n = sa->last;
2060         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2061         n = n->p.other;
2062         if (n == sa->first) n = NULL;
2063         while (n) {
2064             // copy the rest of the nodes from sa to t, going backwards
2065             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2066             n = n->p.other;
2067             if (n == sa->first) n = NULL;
2068         }
2069         // replace sa with t
2070         sp_nodepath_subpath_destroy(sa);
2071         sa = t;
2072     } else if (a == sa->last) {
2073         // a is already last, just drop it
2074         p = sa->last->p.pos;
2075         code = (NRPathcode)sa->last->code;
2076         sp_nodepath_node_destroy(sa->last);
2077     } else {
2078         code = NR_END;
2079         g_assert_not_reached();
2080     }
2082     if (b == sb->first) {
2083         // copy all nodes from b to a, forward 
2084         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2085         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2086             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2087         }
2088     } else if (b == sb->last) {
2089         // copy all nodes from b to a, backward 
2090         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2091         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2092             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2093         }
2094     } else {
2095         g_assert_not_reached();
2096     }
2097     /* and now destroy sb */
2099     sp_nodepath_subpath_destroy(sb);
2101     sp_nodepath_update_handles(sa->nodepath);
2103     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2105     sp_nodepath_update_statusbar(nodepath);
2108 /**
2109  *  Internal function to join two nodes by adding a segment between them.
2110  */
2111 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2113     if (a->subpath == b->subpath) {
2114        Inkscape::NodePath::SubPath *sp = a->subpath;
2116         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2117         sp->closed = TRUE;
2119         sp->first->p.other = sp->last;
2120         sp->last->n.other  = sp->first;
2122         sp_node_handle_mirror_p_to_n(sp->last);
2123         sp_node_handle_mirror_n_to_p(sp->first);
2125         sp->first->code = sp->last->code;
2126         sp->first       = sp->last;
2128         sp_nodepath_update_handles(sp->nodepath);
2130         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2132         return;
2133     }
2135     /* a and b are separate subpaths */
2136     Inkscape::NodePath::SubPath *sa = a->subpath;
2137     Inkscape::NodePath::SubPath *sb = b->subpath;
2139     Inkscape::NodePath::Node *n;
2140     NR::Point p;
2141     NRPathcode code;
2142     if (a == sa->first) {
2143         code = (NRPathcode) sa->first->n.other->code;
2144        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2145         n = sa->last;
2146         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2147         for (n = n->p.other; n != NULL; n = n->p.other) {
2148             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2149         }
2150         sp_nodepath_subpath_destroy(sa);
2151         sa = t;
2152     } else if (a == sa->last) {
2153         code = (NRPathcode)sa->last->code;
2154     } else {
2155         code = NR_END;
2156         g_assert_not_reached();
2157     }
2159     if (b == sb->first) {
2160         n = sb->first;
2161         sp_node_handle_mirror_p_to_n(sa->last);
2162         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2163         sp_node_handle_mirror_n_to_p(sa->last);
2164         for (n = n->n.other; n != NULL; n = n->n.other) {
2165             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2166         }
2167     } else if (b == sb->last) {
2168         n = sb->last;
2169         sp_node_handle_mirror_p_to_n(sa->last);
2170         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2171         sp_node_handle_mirror_n_to_p(sa->last);
2172         for (n = n->p.other; n != NULL; n = n->p.other) {
2173             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2174         }
2175     } else {
2176         g_assert_not_reached();
2177     }
2178     /* and now destroy sb */
2180     sp_nodepath_subpath_destroy(sb);
2182     sp_nodepath_update_handles(sa->nodepath);
2184     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2187 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2189 /**
2190  * Internal function to handle joining two nodes.
2191  */
2192 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2194     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2196     if (g_list_length(nodepath->selected) != 2) {
2197         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2198         return;
2199     }
2201     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2202     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2204     g_assert(a != b);
2205     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2206         // someone tried to join an orphan node (i.e. a single-node subpath).
2207         // this is not worth an error message, just fail silently.
2208         return;
2209     }
2211     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2212         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2213         return;
2214     }
2216     switch(mode) {
2217         case NODE_JOIN_ENDPOINTS:
2218             do_node_selected_join(nodepath, a, b);
2219             break;
2220         case NODE_JOIN_SEGMENT:
2221             do_node_selected_join_segment(nodepath, a, b);
2222             break;
2223     }
2226 /**
2227  *  Join two nodes by merging them into one.
2228  */
2229 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2231     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2234 /**
2235  *  Join two nodes by adding a segment between them.
2236  */
2237 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2239     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2242 /**
2243  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2244  */
2245 void sp_node_delete_preserve(GList *nodes_to_delete)
2247     GSList *nodepaths = NULL;
2249     while (nodes_to_delete) {
2250         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2251         Inkscape::NodePath::SubPath *sp = node->subpath;
2252         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2253         Inkscape::NodePath::Node *sample_cursor = NULL;
2254         Inkscape::NodePath::Node *sample_end = NULL;
2255         Inkscape::NodePath::Node *delete_cursor = node;
2256         bool just_delete = false;
2258         //find the start of this contiguous selection
2259         //move left to the first node that is not selected
2260         //or the start of the non-closed path
2261         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2262             delete_cursor = curr;
2263         }
2265         //just delete at the beginning of an open path
2266         if (!delete_cursor->p.other) {
2267             sample_cursor = delete_cursor;
2268             just_delete = true;
2269         } else {
2270             sample_cursor = delete_cursor->p.other;
2271         }
2273         //calculate points for each segment
2274         int rate = 5;
2275         float period = 1.0 / rate;
2276         std::vector<NR::Point> data;
2277         if (!just_delete) {
2278             data.push_back(sample_cursor->pos);
2279             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2280                 //just delete at the end of an open path
2281                 if (!sp->closed && curr == sp->last) {
2282                     just_delete = true;
2283                     break;
2284                 }
2286                 //sample points on the contiguous selected segment
2287                 NR::Point *bez;
2288                 bez = new NR::Point [4];
2289                 bez[0] = curr->pos;
2290                 bez[1] = curr->n.pos;
2291                 bez[2] = curr->n.other->p.pos;
2292                 bez[3] = curr->n.other->pos;
2293                 for (int i=1; i<rate; i++) {
2294                     gdouble t = i * period;
2295                     NR::Point p = bezier_pt(3, bez, t);
2296                     data.push_back(p);
2297                 }
2298                 data.push_back(curr->n.other->pos);
2300                 sample_end = curr->n.other;
2301                 //break if we've come full circle or hit the end of the selection
2302                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2303                     break;
2304                 }
2305             }
2306         }
2308         if (!just_delete) {
2309             //calculate the best fitting single segment and adjust the endpoints
2310             NR::Point *adata;
2311             adata = new NR::Point [data.size()];
2312             copy(data.begin(), data.end(), adata);
2314             NR::Point *bez;
2315             bez = new NR::Point [4];
2316             //would decreasing error create a better fitting approximation?
2317             gdouble error = 1.0;
2318             gint ret;
2319             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2321             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2322             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2323             //the resulting nodes behave as expected.
2324             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2325                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2326             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2327                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2329             //adjust endpoints
2330             sample_cursor->n.pos = bez[1];
2331             sample_end->p.pos = bez[2];
2332         }
2334         //destroy this contiguous selection
2335         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2336             Inkscape::NodePath::Node *temp = delete_cursor;
2337             if (delete_cursor->n.other == delete_cursor) {
2338                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2339                 delete_cursor = NULL;
2340             } else {
2341                 delete_cursor = delete_cursor->n.other;
2342             }
2343             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2344             sp_nodepath_node_destroy(temp);
2345         }
2347         sp_nodepath_update_handles(nodepath);
2349         if (!g_slist_find(nodepaths, nodepath))
2350             nodepaths = g_slist_prepend (nodepaths, nodepath);
2351     }
2353     for (GSList *i = nodepaths; i; i = i->next) {
2354         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2355         // different nodepaths will give us one undo event per nodepath
2356         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2358         // if the entire nodepath is removed, delete the selected object.
2359         if (nodepath->subpaths == NULL ||
2360             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2361             //at least 2
2362             sp_nodepath_get_node_count(nodepath) < 2) {
2363             SPDocument *document = sp_desktop_document (nodepath->desktop);
2364             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2365             //delete this nodepath's object, not the entire selection! (though at this time, this
2366             //does not matter)
2367             sp_selection_delete();
2368             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2369                               _("Delete nodes"));
2370         } else {
2371             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2372             sp_nodepath_update_statusbar(nodepath);
2373         }
2374     }
2376     g_slist_free (nodepaths);
2379 /**
2380  * Delete one or more selected nodes.
2381  */
2382 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2384     if (!nodepath) return;
2385     if (!nodepath->selected) return;
2387     /** \todo fixme: do it the right way */
2388     while (nodepath->selected) {
2389        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2390         sp_nodepath_node_destroy(node);
2391     }
2394     //clean up the nodepath (such as for trivial subpaths)
2395     sp_nodepath_cleanup(nodepath);
2397     sp_nodepath_update_handles(nodepath);
2399     // if the entire nodepath is removed, delete the selected object.
2400     if (nodepath->subpaths == NULL ||
2401         sp_nodepath_get_node_count(nodepath) < 2) {
2402         SPDocument *document = sp_desktop_document (nodepath->desktop);
2403         sp_selection_delete();
2404         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2405                           _("Delete nodes"));
2406         return;
2407     }
2409     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2411     sp_nodepath_update_statusbar(nodepath);
2414 /**
2415  * Delete one or more segments between two selected nodes.
2416  * This is the code for 'split'.
2417  */
2418 void
2419 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2421    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2422    Inkscape::NodePath::Node *curr, *next;     //Iterators
2424     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2426     if (g_list_length(nodepath->selected) != 2) {
2427         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2428                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2429         return;
2430     }
2432     //Selected nodes, not inclusive
2433    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2434    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2436     if ( ( a==b)                       ||  //same node
2437          (a->subpath  != b->subpath )  ||  //not the same path
2438          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2439          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2440     {
2441         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2442                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2443         return;
2444     }
2446     //###########################################
2447     //# BEGIN EDITS
2448     //###########################################
2449     //##################################
2450     //# CLOSED PATH
2451     //##################################
2452     if (a->subpath->closed) {
2455         gboolean reversed = FALSE;
2457         //Since we can go in a circle, we need to find the shorter distance.
2458         //  a->b or b->a
2459         start = end = NULL;
2460         int distance    = 0;
2461         int minDistance = 0;
2462         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2463             if (curr==b) {
2464                 //printf("a to b:%d\n", distance);
2465                 start = a;//go from a to b
2466                 end   = b;
2467                 minDistance = distance;
2468                 //printf("A to B :\n");
2469                 break;
2470             }
2471             distance++;
2472         }
2474         //try again, the other direction
2475         distance = 0;
2476         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2477             if (curr==a) {
2478                 //printf("b to a:%d\n", distance);
2479                 if (distance < minDistance) {
2480                     start    = b;  //we go from b to a
2481                     end      = a;
2482                     reversed = TRUE;
2483                     //printf("B to A\n");
2484                 }
2485                 break;
2486             }
2487             distance++;
2488         }
2491         //Copy everything from 'end' to 'start' to a new subpath
2492        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2493         for (curr=end ; curr ; curr=curr->n.other) {
2494             NRPathcode code = (NRPathcode) curr->code;
2495             if (curr == end)
2496                 code = NR_MOVETO;
2497             sp_nodepath_node_new(t, NULL,
2498                                  (Inkscape::NodePath::NodeType)curr->type, code,
2499                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2500             if (curr == start)
2501                 break;
2502         }
2503         sp_nodepath_subpath_destroy(a->subpath);
2506     }
2510     //##################################
2511     //# OPEN PATH
2512     //##################################
2513     else {
2515         //We need to get the direction of the list between A and B
2516         //Can we walk from a to b?
2517         start = end = NULL;
2518         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2519             if (curr==b) {
2520                 start = a;  //did it!  we go from a to b
2521                 end   = b;
2522                 //printf("A to B\n");
2523                 break;
2524             }
2525         }
2526         if (!start) {//didn't work?  let's try the other direction
2527             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2528                 if (curr==a) {
2529                     start = b;  //did it!  we go from b to a
2530                     end   = a;
2531                     //printf("B to A\n");
2532                     break;
2533                 }
2534             }
2535         }
2536         if (!start) {
2537             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2538                                                      _("Cannot find path between nodes."));
2539             return;
2540         }
2544         //Copy everything after 'end' 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, (Inkscape::NodePath::NodeType)curr->type, code,
2551                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2552         }
2554         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2555         for (curr = start->n.other ; curr  ; curr=next) {
2556             next = curr->n.other;
2557             sp_nodepath_node_destroy(curr);
2558         }
2560     }
2561     //###########################################
2562     //# END EDITS
2563     //###########################################
2565     //clean up the nodepath (such as for trivial subpaths)
2566     sp_nodepath_cleanup(nodepath);
2568     sp_nodepath_update_handles(nodepath);
2570     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2572     sp_nodepath_update_statusbar(nodepath);
2575 /**
2576  * Call sp_nodepath_set_line() for all selected segments.
2577  */
2578 void
2579 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2581     if (nodepath == NULL) return;
2583     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2584        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2585         g_assert(n->selected);
2586         if (n->p.other && n->p.other->selected) {
2587             sp_nodepath_set_line_type(n, code);
2588         }
2589     }
2591     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2594 /**
2595  * Call sp_nodepath_convert_node_type() for all selected nodes.
2596  */
2597 void
2598 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2600     if (nodepath == NULL) return;
2602     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2604     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2605         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2606     }
2608     sp_nodepath_update_repr(nodepath, _("Change node type"));
2611 /**
2612  * Change select status of node, update its own and neighbour handles.
2613  */
2614 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2616     node->selected = selected;
2618     if (selected) {
2619         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2620         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2621         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2622         sp_knot_update_ctrl(node->knot);
2623     } else {
2624         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2625         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2626         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2627         sp_knot_update_ctrl(node->knot);
2628     }
2630     sp_node_update_handles(node);
2631     if (node->n.other) sp_node_update_handles(node->n.other);
2632     if (node->p.other) sp_node_update_handles(node->p.other);
2635 /**
2636 \brief Select a node
2637 \param node     The node to select
2638 \param incremental   If true, add to selection, otherwise deselect others
2639 \param override   If true, always select this node, otherwise toggle selected status
2640 */
2641 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2643     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2645     if (incremental) {
2646         if (override) {
2647             if (!g_list_find(nodepath->selected, node)) {
2648                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2649             }
2650             sp_node_set_selected(node, TRUE);
2651         } else { // toggle
2652             if (node->selected) {
2653                 g_assert(g_list_find(nodepath->selected, node));
2654                 nodepath->selected = g_list_remove(nodepath->selected, node);
2655             } else {
2656                 g_assert(!g_list_find(nodepath->selected, node));
2657                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2658             }
2659             sp_node_set_selected(node, !node->selected);
2660         }
2661     } else {
2662         sp_nodepath_deselect(nodepath);
2663         nodepath->selected = g_list_prepend(nodepath->selected, node);
2664         sp_node_set_selected(node, TRUE);
2665     }
2667     sp_nodepath_update_statusbar(nodepath);
2671 /**
2672 \brief Deselect all nodes in the nodepath
2673 */
2674 void
2675 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2677     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2679     while (nodepath->selected) {
2680         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2681         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2682     }
2683     sp_nodepath_update_statusbar(nodepath);
2686 /**
2687 \brief Select or invert selection of all nodes in the nodepath
2688 */
2689 void
2690 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2692     if (!nodepath) return;
2694     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2695        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2696         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2697            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2698            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2699         }
2700     }
2703 /**
2704  * If nothing selected, does the same as sp_nodepath_select_all();
2705  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2706  * (i.e., similar to "select all in layer", with the "selected" subpaths
2707  * being treated as "layers" in the path).
2708  */
2709 void
2710 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2712     if (!nodepath) return;
2714     if (g_list_length (nodepath->selected) == 0) {
2715         sp_nodepath_select_all (nodepath, invert);
2716         return;
2717     }
2719     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2720     GSList *subpaths = NULL;
2722     for (GList *l = copy; l != NULL; l = l->next) {
2723         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2724         Inkscape::NodePath::SubPath *subpath = n->subpath;
2725         if (!g_slist_find (subpaths, subpath))
2726             subpaths = g_slist_prepend (subpaths, subpath);
2727     }
2729     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2730         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2731         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2732             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2733             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2734         }
2735     }
2737     g_slist_free (subpaths);
2738     g_list_free (copy);
2741 /**
2742  * \brief Select the node after the last selected; if none is selected,
2743  * select the first within path.
2744  */
2745 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2747     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2749    Inkscape::NodePath::Node *last = NULL;
2750     if (nodepath->selected) {
2751         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2752            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2753             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2754             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2755                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2756                 if (node->selected) {
2757                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2758                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2759                             if (spl->next) { // there's a next subpath
2760                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2761                                 last = subpath_next->first;
2762                             } else if (spl->prev) { // there's a previous subpath
2763                                 last = NULL; // to be set later to the first node of first subpath
2764                             } else {
2765                                 last = node->n.other;
2766                             }
2767                         } else {
2768                             last = node->n.other;
2769                         }
2770                     } else {
2771                         if (node->n.other) {
2772                             last = node->n.other;
2773                         } else {
2774                             if (spl->next) { // there's a next subpath
2775                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2776                                 last = subpath_next->first;
2777                             } else if (spl->prev) { // there's a previous subpath
2778                                 last = NULL; // to be set later to the first node of first subpath
2779                             } else {
2780                                 last = (Inkscape::NodePath::Node *) subpath->first;
2781                             }
2782                         }
2783                     }
2784                 }
2785             }
2786         }
2787         sp_nodepath_deselect(nodepath);
2788     }
2790     if (last) { // there's at least one more node after selected
2791         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2792     } else { // no more nodes, select the first one in first subpath
2793        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2794         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2795     }
2798 /**
2799  * \brief Select the node before the first selected; if none is selected,
2800  * select the last within path
2801  */
2802 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2804     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2806    Inkscape::NodePath::Node *last = NULL;
2807     if (nodepath->selected) {
2808         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2809            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2810             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2811                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2812                 if (node->selected) {
2813                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2814                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2815                             if (spl->prev) { // there's a prev subpath
2816                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2817                                 last = subpath_prev->last;
2818                             } else if (spl->next) { // there's a next subpath
2819                                 last = NULL; // to be set later to the last node of last subpath
2820                             } else {
2821                                 last = node->p.other;
2822                             }
2823                         } else {
2824                             last = node->p.other;
2825                         }
2826                     } else {
2827                         if (node->p.other) {
2828                             last = node->p.other;
2829                         } else {
2830                             if (spl->prev) { // there's a prev subpath
2831                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2832                                 last = subpath_prev->last;
2833                             } else if (spl->next) { // there's a next subpath
2834                                 last = NULL; // to be set later to the last node of last subpath
2835                             } else {
2836                                 last = (Inkscape::NodePath::Node *) subpath->last;
2837                             }
2838                         }
2839                     }
2840                 }
2841             }
2842         }
2843         sp_nodepath_deselect(nodepath);
2844     }
2846     if (last) { // there's at least one more node before selected
2847         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2848     } else { // no more nodes, select the last one in last subpath
2849         GList *spl = g_list_last(nodepath->subpaths);
2850        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2851         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2852     }
2855 /**
2856  * \brief Select all nodes that are within the rectangle.
2857  */
2858 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2860     if (!incremental) {
2861         sp_nodepath_deselect(nodepath);
2862     }
2864     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2865        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2866         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2867            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2869             if (b.contains(node->pos)) {
2870                 sp_nodepath_node_select(node, TRUE, TRUE);
2871             }
2872         }
2873     }
2877 void
2878 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2880     g_assert (n);
2881     g_assert (nodepath);
2882     g_assert (n->subpath->nodepath == nodepath);
2884     if (g_list_length (nodepath->selected) == 0) {
2885         if (grow > 0) {
2886             sp_nodepath_node_select(n, TRUE, TRUE);
2887         }
2888         return;
2889     }
2891     if (g_list_length (nodepath->selected) == 1) {
2892         if (grow < 0) {
2893             sp_nodepath_deselect (nodepath);
2894             return;
2895         }
2896     }
2898         double n_sel_range = 0, p_sel_range = 0;
2899             Inkscape::NodePath::Node *farthest_n_node = n;
2900             Inkscape::NodePath::Node *farthest_p_node = n;
2902         // Calculate ranges
2903         {
2904             double n_range = 0, p_range = 0;
2905             bool n_going = true, p_going = true;
2906             Inkscape::NodePath::Node *n_node = n;
2907             Inkscape::NodePath::Node *p_node = n;
2908             do {
2909                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2910                 if (n_node && n_going)
2911                     n_node = n_node->n.other;
2912                 if (n_node == NULL) {
2913                     n_going = false;
2914                 } else {
2915                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2916                     if (n_node->selected) {
2917                         n_sel_range = n_range;
2918                         farthest_n_node = n_node;
2919                     }
2920                     if (n_node == p_node) {
2921                         n_going = false;
2922                         p_going = false;
2923                     }
2924                 }
2925                 if (p_node && p_going)
2926                     p_node = p_node->p.other;
2927                 if (p_node == NULL) {
2928                     p_going = false;
2929                 } else {
2930                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2931                     if (p_node->selected) {
2932                         p_sel_range = p_range;
2933                         farthest_p_node = p_node;
2934                     }
2935                     if (p_node == n_node) {
2936                         n_going = false;
2937                         p_going = false;
2938                     }
2939                 }
2940             } while (n_going || p_going);
2941         }
2943     if (grow > 0) {
2944         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2945                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2946         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2947                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2948         }
2949     } else {
2950         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2951                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2952         } else if (farthest_p_node && farthest_p_node->selected) {
2953                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2954         }
2955     }
2958 void
2959 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2961     g_assert (n);
2962     g_assert (nodepath);
2963     g_assert (n->subpath->nodepath == nodepath);
2965     if (g_list_length (nodepath->selected) == 0) {
2966         if (grow > 0) {
2967             sp_nodepath_node_select(n, TRUE, TRUE);
2968         }
2969         return;
2970     }
2972     if (g_list_length (nodepath->selected) == 1) {
2973         if (grow < 0) {
2974             sp_nodepath_deselect (nodepath);
2975             return;
2976         }
2977     }
2979     Inkscape::NodePath::Node *farthest_selected = NULL;
2980     double farthest_dist = 0;
2982     Inkscape::NodePath::Node *closest_unselected = NULL;
2983     double closest_dist = NR_HUGE;
2985     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2986        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2987         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2988            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2989            if (node == n)
2990                continue;
2991            if (node->selected) {
2992                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2993                    farthest_dist = NR::L2(node->pos - n->pos);
2994                    farthest_selected = node;
2995                }
2996            } else {
2997                if (NR::L2(node->pos - n->pos) < closest_dist) {
2998                    closest_dist = NR::L2(node->pos - n->pos);
2999                    closest_unselected = node;
3000                }
3001            }
3002         }
3003     }
3005     if (grow > 0) {
3006         if (closest_unselected) {
3007             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3008         }
3009     } else {
3010         if (farthest_selected) {
3011             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3012         }
3013     }
3017 /**
3018 \brief  Saves all nodes' and handles' current positions in their origin members
3019 */
3020 void
3021 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3023     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3024        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3025         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3026            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3027            n->origin = n->pos;
3028            n->p.origin = n->p.pos;
3029            n->n.origin = n->n.pos;
3030         }
3031     }
3034 /**
3035 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3036 */
3037 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3039     if (!nodepath->selected) {
3040         return NULL;
3041     }
3043     GList *r = NULL;
3044     guint i = 0;
3045     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3046        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3047         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3048            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3049             i++;
3050             if (node->selected) {
3051                 r = g_list_append(r, GINT_TO_POINTER(i));
3052             }
3053         }
3054     }
3055     return r;
3058 /**
3059 \brief  Restores selection by selecting nodes whose positions are in the list
3060 */
3061 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3063     sp_nodepath_deselect(nodepath);
3065     guint i = 0;
3066     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3067        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3068         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3069            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3070             i++;
3071             if (g_list_find(r, GINT_TO_POINTER(i))) {
3072                 sp_nodepath_node_select(node, TRUE, TRUE);
3073             }
3074         }
3075     }
3079 /**
3080 \brief Adjusts handle according to node type and line code.
3081 */
3082 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3084     g_assert(node);
3086    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3087    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3089    // nothing to do if we are an end node
3090     if (me->other == NULL) return;
3091     if (other->other == NULL) return;
3093     // nothing to do if we are a cusp node
3094     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3096     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3097     NRPathcode mecode;
3098     if (which_adjust == 1) {
3099         mecode = (NRPathcode)me->other->code;
3100     } else {
3101         mecode = (NRPathcode)node->code;
3102     }
3103     if (mecode == NR_LINETO) return;
3105     if (sp_node_side_is_line(node, other)) {
3106         // other is a line, and we are either smooth or symm
3107        Inkscape::NodePath::Node *othernode = other->other;
3108         double len = NR::L2(me->pos - node->pos);
3109         NR::Point delta = node->pos - othernode->pos;
3110         double linelen = NR::L2(delta);
3111         if (linelen < 1e-18)
3112             return;
3113         me->pos = node->pos + (len / linelen)*delta;
3114         return;
3115     }
3117     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3118         // symmetrize 
3119         me->pos = 2 * node->pos - other->pos;
3120         return;
3121     } else {
3122         // smoothify
3123         double len = NR::L2(me->pos - node->pos);
3124         NR::Point delta = other->pos - node->pos;
3125         double otherlen = NR::L2(delta);
3126         if (otherlen < 1e-18) return;
3127         me->pos = node->pos - (len / otherlen) * delta;
3128     }
3131 /**
3132  \brief Adjusts both handles according to node type and line code
3133  */
3134 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3136     g_assert(node);
3138     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3140     /* we are either smooth or symm */
3142     if (node->p.other == NULL) return;
3143     if (node->n.other == NULL) return;
3145     if (sp_node_side_is_line(node, &node->p)) {
3146         sp_node_adjust_handle(node, 1);
3147         return;
3148     }
3150     if (sp_node_side_is_line(node, &node->n)) {
3151         sp_node_adjust_handle(node, -1);
3152         return;
3153     }
3155     /* both are curves */
3156     NR::Point const delta( node->n.pos - node->p.pos );
3158     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3159         node->p.pos = node->pos - delta / 2;
3160         node->n.pos = node->pos + delta / 2;
3161         return;
3162     }
3164     /* We are smooth */
3165     double plen = NR::L2(node->p.pos - node->pos);
3166     if (plen < 1e-18) return;
3167     double nlen = NR::L2(node->n.pos - node->pos);
3168     if (nlen < 1e-18) return;
3169     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3170     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3173 /**
3174  * Node event callback.
3175  */
3176 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3178     gboolean ret = FALSE;
3179     switch (event->type) {
3180         case GDK_ENTER_NOTIFY:
3181             Inkscape::NodePath::Path::active_node = n;
3182             break;
3183         case GDK_LEAVE_NOTIFY:
3184             Inkscape::NodePath::Path::active_node = NULL;
3185             break;
3186         case GDK_SCROLL:
3187             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3188                 switch (event->scroll.direction) {
3189                     case GDK_SCROLL_UP:
3190                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3191                         break;
3192                     case GDK_SCROLL_DOWN:
3193                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3194                         break;
3195                     default:
3196                         break;
3197                 }
3198                 ret = TRUE;
3199             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3200                 switch (event->scroll.direction) {
3201                     case GDK_SCROLL_UP:
3202                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3203                         break;
3204                     case GDK_SCROLL_DOWN:
3205                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3206                         break;
3207                     default:
3208                         break;
3209                 }
3210                 ret = TRUE;
3211             }
3212             break;
3213         case GDK_KEY_PRESS:
3214             switch (get_group0_keyval (&event->key)) {
3215                 case GDK_space:
3216                     if (event->key.state & GDK_BUTTON1_MASK) {
3217                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3218                         stamp_repr(nodepath);
3219                         ret = TRUE;
3220                     }
3221                     break;
3222                 case GDK_Page_Up:
3223                     if (event->key.state & GDK_CONTROL_MASK) {
3224                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3225                     } else {
3226                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3227                     }
3228                     break;
3229                 case GDK_Page_Down:
3230                     if (event->key.state & GDK_CONTROL_MASK) {
3231                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3232                     } else {
3233                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3234                     }
3235                     break;
3236                 default:
3237                     break;
3238             }
3239             break;
3240         default:
3241             break;
3242     }
3244     return ret;
3247 /**
3248  * Handle keypress on node; directly called.
3249  */
3250 gboolean node_key(GdkEvent *event)
3252     Inkscape::NodePath::Path *np;
3254     // there is no way to verify nodes so set active_node to nil when deleting!!
3255     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3257     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3258         gint ret = FALSE;
3259         switch (get_group0_keyval (&event->key)) {
3260             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3261             case GDK_BackSpace:
3262                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3263                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3264                 sp_nodepath_update_repr(np, _("Delete node"));
3265                 Inkscape::NodePath::Path::active_node = NULL;
3266                 ret = TRUE;
3267                 break;
3268             case GDK_c:
3269                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3270                 ret = TRUE;
3271                 break;
3272             case GDK_s:
3273                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3274                 ret = TRUE;
3275                 break;
3276             case GDK_y:
3277                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3278                 ret = TRUE;
3279                 break;
3280             case GDK_b:
3281                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3282                 ret = TRUE;
3283                 break;
3284         }
3285         return ret;
3286     }
3287     return FALSE;
3290 /**
3291  * Mouseclick on node callback.
3292  */
3293 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3295    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3297     if (state & GDK_CONTROL_MASK) {
3298         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3300         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3301             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3302                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3303             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3304                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3305             } else {
3306                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3307             }
3308             sp_nodepath_update_repr(nodepath, _("Change node type"));
3309             sp_nodepath_update_statusbar(nodepath);
3311         } else { //ctrl+alt+click: delete node
3312             GList *node_to_delete = NULL;
3313             node_to_delete = g_list_append(node_to_delete, n);
3314             sp_node_delete_preserve(node_to_delete);
3315         }
3317     } else {
3318         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3319     }
3322 /**
3323  * Mouse grabbed node callback.
3324  */
3325 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3327    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3329     if (!n->selected) {
3330         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3331     }
3333     n->is_dragging = true;
3334     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3336     sp_nodepath_remember_origins (n->subpath->nodepath);
3339 /**
3340  * Mouse ungrabbed node callback.
3341  */
3342 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3344    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3346    n->dragging_out = NULL;
3347    n->is_dragging = false;
3348    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3350    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3353 /**
3354  * The point on a line, given by its angle, closest to the given point.
3355  * \param p  A point.
3356  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3357  * \param closest  Pointer to the point struct where the result is stored.
3358  * \todo FIXME: use dot product perhaps?
3359  */
3360 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3362     if (a == HUGE_VAL) { // vertical
3363         *closest = NR::Point(0, (*p)[NR::Y]);
3364     } else {
3365         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3366         (*closest)[NR::Y] = a * (*closest)[NR::X];
3367     }
3370 /**
3371  * Distance from the point to a line given by its angle.
3372  * \param p  A point.
3373  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3374  */
3375 static double point_line_distance(NR::Point *p, double a)
3377     NR::Point c;
3378     point_line_closest(p, a, &c);
3379     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]));
3382 /**
3383  * Callback for node "request" signal.
3384  * \todo fixme: This goes to "moved" event? (lauris)
3385  */
3386 static gboolean
3387 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3389     double yn, xn, yp, xp;
3390     double an, ap, na, pa;
3391     double d_an, d_ap, d_na, d_pa;
3392     gboolean collinear = FALSE;
3393     NR::Point c;
3394     NR::Point pr;
3396    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3398     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3400    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3401     if ( (!n->subpath->nodepath->straight_path) &&
3402          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3403            || n->dragging_out ) )
3404     {
3405        NR::Point mouse = (*p);
3407        if (!n->dragging_out) {
3408            // This is the first drag-out event; find out which handle to drag out
3409            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3410            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3412            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3413                return FALSE;
3415            Inkscape::NodePath::NodeSide *opposite;
3416            if (appr_p > appr_n) { // closer to p
3417                n->dragging_out = &n->p;
3418                opposite = &n->n;
3419                n->code = NR_CURVETO;
3420            } else if (appr_p < appr_n) { // closer to n
3421                n->dragging_out = &n->n;
3422                opposite = &n->p;
3423                n->n.other->code = NR_CURVETO;
3424            } else { // p and n nodes are the same
3425                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3426                    n->dragging_out = &n->p;
3427                    opposite = &n->n;
3428                    n->code = NR_CURVETO;
3429                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3430                    n->dragging_out = &n->n;
3431                    opposite = &n->p;
3432                    n->n.other->code = NR_CURVETO;
3433                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3434                    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);
3435                    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);
3436                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3437                        n->dragging_out = &n->n;
3438                        opposite = &n->p;
3439                        n->n.other->code = NR_CURVETO;
3440                    } else { // closer to other's n handle
3441                        n->dragging_out = &n->p;
3442                        opposite = &n->n;
3443                        n->code = NR_CURVETO;
3444                    }
3445                }
3446            }
3448            // if there's another handle, make sure the one we drag out starts parallel to it
3449            if (opposite->pos != n->pos) {
3450                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3451            }
3453            // knots might not be created yet!
3454            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3455            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3456        }
3458        // pass this on to the handle-moved callback
3459        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3460        sp_node_update_handles(n);
3461        return TRUE;
3462    }
3464     if (state & GDK_CONTROL_MASK) { // constrained motion
3466         // calculate relative distances of handles
3467         // n handle:
3468         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3469         xn = n->n.pos[NR::X] - n->pos[NR::X];
3470         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3471         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3472             if (n->n.other) { // if there is the next point
3473                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3474                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3475                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3476             }
3477         }
3478         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3479         if (yn < 0) { xn = -xn; yn = -yn; }
3481         // p handle:
3482         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3483         xp = n->p.pos[NR::X] - n->pos[NR::X];
3484         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3485         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3486             if (n->p.other) {
3487                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3488                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3489                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3490             }
3491         }
3492         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3493         if (yp < 0) { xp = -xp; yp = -yp; }
3495         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3496             // sliding on handles, only if at least one of the handles is non-vertical
3497             // (otherwise it's the same as ctrl+drag anyway)
3499             // calculate angles of the handles
3500             if (xn == 0) {
3501                 if (yn == 0) { // no handle, consider it the continuation of the other one
3502                     an = 0;
3503                     collinear = TRUE;
3504                 }
3505                 else an = 0; // vertical; set the angle to horizontal
3506             } else an = yn/xn;
3508             if (xp == 0) {
3509                 if (yp == 0) { // no handle, consider it the continuation of the other one
3510                     ap = an;
3511                 }
3512                 else ap = 0; // vertical; set the angle to horizontal
3513             } else  ap = yp/xp;
3515             if (collinear) an = ap;
3517             // angles of the perpendiculars; HUGE_VAL means vertical
3518             if (an == 0) na = HUGE_VAL; else na = -1/an;
3519             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3521             // mouse point relative to the node's original pos
3522             pr = (*p) - n->origin;
3524             // distances to the four lines (two handles and two perpendiculars)
3525             d_an = point_line_distance(&pr, an);
3526             d_na = point_line_distance(&pr, na);
3527             d_ap = point_line_distance(&pr, ap);
3528             d_pa = point_line_distance(&pr, pa);
3530             // find out which line is the closest, save its closest point in c
3531             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3532                 point_line_closest(&pr, an, &c);
3533             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3534                 point_line_closest(&pr, ap, &c);
3535             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3536                 point_line_closest(&pr, na, &c);
3537             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3538                 point_line_closest(&pr, pa, &c);
3539             }
3541             // move the node to the closest point
3542             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3543                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3544                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3546         } else {  // constraining to hor/vert
3548             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3549                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3550             } else { // snap to vert
3551                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3552             }
3553         }
3554     } else { // move freely
3555         if (n->is_dragging) {
3556             if (state & GDK_MOD1_MASK) { // sculpt
3557                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3558             } else {
3559                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3560                                             (*p)[NR::X] - n->pos[NR::X],
3561                                             (*p)[NR::Y] - n->pos[NR::Y],
3562                                             (state & GDK_SHIFT_MASK) == 0);
3563             }
3564         }
3565     }
3567     n->subpath->nodepath->desktop->scroll_to_point(p);
3569     return TRUE;
3572 /**
3573  * Node handle clicked callback.
3574  */
3575 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3577    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3579     if (state & GDK_CONTROL_MASK) { // "delete" handle
3580         if (n->p.knot == knot) {
3581             n->p.pos = n->pos;
3582         } else if (n->n.knot == knot) {
3583             n->n.pos = n->pos;
3584         }
3585         sp_node_update_handles(n);
3586         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3587         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3588         sp_nodepath_update_statusbar(nodepath);
3590     } else { // just select or add to selection, depending in Shift
3591         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3592     }
3595 /**
3596  * Node handle grabbed callback.
3597  */
3598 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3600    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3602     if (!n->selected) {
3603         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3604     }
3606     // remember the origin point of the handle
3607     if (n->p.knot == knot) {
3608         n->p.origin_radial = n->p.pos - n->pos;
3609     } else if (n->n.knot == knot) {
3610         n->n.origin_radial = n->n.pos - n->pos;
3611     } else {
3612         g_assert_not_reached();
3613     }
3615     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3618 /**
3619  * Node handle ungrabbed callback.
3620  */
3621 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3623    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3625     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3626     if (n->p.knot == knot) {
3627         n->p.origin_radial.a = 0;
3628         sp_knot_set_position(knot, &n->p.pos, state);
3629     } else if (n->n.knot == knot) {
3630         n->n.origin_radial.a = 0;
3631         sp_knot_set_position(knot, &n->n.pos, state);
3632     } else {
3633         g_assert_not_reached();
3634     }
3636     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3639 /**
3640  * Node handle "request" signal callback.
3641  */
3642 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint /*state*/, gpointer data)
3644     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3646     Inkscape::NodePath::NodeSide *me, *opposite;
3647     gint which;
3648     if (n->p.knot == knot) {
3649         me = &n->p;
3650         opposite = &n->n;
3651         which = -1;
3652     } else if (n->n.knot == knot) {
3653         me = &n->n;
3654         opposite = &n->p;
3655         which = 1;
3656     } else {
3657         me = opposite = NULL;
3658         which = 0;
3659         g_assert_not_reached();
3660     }
3662     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3663     Inkscape::SnappedPoint s ;
3665     Inkscape::NodePath::Node *othernode = opposite->other;
3666     if (othernode) {
3667         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3668             /* We are smooth node adjacent with line */
3669             NR::Point const delta = *p - n->pos;
3670             NR::Coord const len = NR::L2(delta);
3671             Inkscape::NodePath::Node *othernode = opposite->other;
3672             NR::Point const ndelta = n->pos - othernode->pos;
3673             NR::Coord const linelen = NR::L2(ndelta);
3674             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3675                 NR::Coord const scal = dot(delta, ndelta) / linelen;
3676                 (*p) = n->pos + (scal / linelen) * ndelta;
3677             }
3678             s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), n->subpath->nodepath->item);
3679         } else {
3680             s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, n->subpath->nodepath->item);
3681         }
3682     } else {
3683         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, n->subpath->nodepath->item);
3684     }
3686     *p = s.getPoint();
3687     if (s.getSnapped()) {
3688         n->subpath->nodepath->desktop->snapindicator->set_new_snappoint(s);
3689     }
3691     sp_node_adjust_handle(n, -which);
3693     return FALSE;
3696 /**
3697  * Node handle moved callback.
3698  */
3699 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3701    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3703    Inkscape::NodePath::NodeSide *me;
3704    Inkscape::NodePath::NodeSide *other;
3705     if (n->p.knot == knot) {
3706         me = &n->p;
3707         other = &n->n;
3708     } else if (n->n.knot == knot) {
3709         me = &n->n;
3710         other = &n->p;
3711     } else {
3712         me = NULL;
3713         other = NULL;
3714         g_assert_not_reached();
3715     }
3717     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3718     Radial rme(me->pos - n->pos);
3719     Radial rother(other->pos - n->pos);
3720     Radial rnew(*p - n->pos);
3722     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3723         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3724         /* 0 interpreted as "no snapping". */
3726         // 1. Snap to the closest PI/snaps angle, starting from zero.
3727         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3729         // 2. Snap to the original angle, its opposite and perpendiculars
3730         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3731             /* The closest PI/2 angle, starting from original angle */
3732             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3734             // Snap to the closest.
3735             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3736                        ? a_snapped
3737                        : a_ortho );
3738         }
3740         // 3. Snap to the angle of the opposite line, if any
3741         Inkscape::NodePath::Node *othernode = other->other;
3742         if (othernode) {
3743             NR::Point other_to_snap(0,0);
3744             if (sp_node_side_is_line(n, other)) {
3745                 other_to_snap = othernode->pos - n->pos;
3746             } else {
3747                 other_to_snap = other->pos - n->pos;
3748             }
3749             if (NR::L2(other_to_snap) > 1e-3) {
3750                 Radial rother_to_snap(other_to_snap);
3751                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
3752                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
3754                 // Snap to the closest.
3755                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
3756                        ? a_snapped
3757                        : a_oppo );
3758             }
3759         }
3761         rnew.a = a_snapped;
3762     }
3764     if (state & GDK_MOD1_MASK) {
3765         // lock handle length
3766         rnew.r = me->origin_radial.r;
3767     }
3769     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3770         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3771         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3772         rother.a += rnew.a - rme.a;
3773         other->pos = NR::Point(rother) + n->pos;
3774         if (other->knot) {
3775             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3776             sp_knot_moveto(other->knot, &other->pos);
3777         }
3778     }
3780     me->pos = NR::Point(rnew) + n->pos;
3781     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3783     // move knot, but without emitting the signal:
3784     // we cannot emit a "moved" signal because we're now processing it
3785     sp_knot_moveto(me->knot, &(me->pos));
3787     update_object(n->subpath->nodepath);
3789     /* status text */
3790     SPDesktop *desktop = n->subpath->nodepath->desktop;
3791     if (!desktop) return;
3792     SPEventContext *ec = desktop->event_context;
3793     if (!ec) return;
3794     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3795     if (!mc) return;
3797     double degrees = 180 / M_PI * rnew.a;
3798     if (degrees > 180) degrees -= 360;
3799     if (degrees < -180) degrees += 360;
3800     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3801         degrees = angle_to_compass (degrees);
3803     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3805     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
3806          _("<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);
3808     g_string_free(length, TRUE);
3811 /**
3812  * Node handle event callback.
3813  */
3814 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3816     gboolean ret = FALSE;
3817     switch (event->type) {
3818         case GDK_KEY_PRESS:
3819             switch (get_group0_keyval (&event->key)) {
3820                 case GDK_space:
3821                     if (event->key.state & GDK_BUTTON1_MASK) {
3822                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3823                         stamp_repr(nodepath);
3824                         ret = TRUE;
3825                     }
3826                     break;
3827                 default:
3828                     break;
3829             }
3830             break;
3831         case GDK_ENTER_NOTIFY:
3832             // we use an experimentally determined threshold that seems to work fine
3833             if (NR::L2(n->pos - knot->pos) < 0.75)
3834                 Inkscape::NodePath::Path::active_node = n;
3835             break;
3836         case GDK_LEAVE_NOTIFY:
3837             // we use an experimentally determined threshold that seems to work fine
3838             if (NR::L2(n->pos - knot->pos) < 0.75)
3839                 Inkscape::NodePath::Path::active_node = NULL;
3840             break;
3841         default:
3842             break;
3843     }
3845     return ret;
3848 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3849                                  Radial &rme, Radial &rother, gboolean const both)
3851     rme.a += angle;
3852     if ( both
3853          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3854          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3855     {
3856         rother.a += angle;
3857     }
3860 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3861                                         Radial &rme, Radial &rother, gboolean const both)
3863     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3865     gdouble r;
3866     if ( both
3867          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3868          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3869     {
3870         r = MAX(rme.r, rother.r);
3871     } else {
3872         r = rme.r;
3873     }
3875     gdouble const weird_angle = atan2(norm_angle, r);
3876 /* Bulia says norm_angle is just the visible distance that the
3877  * object's end must travel on the screen.  Left as 'angle' for want of
3878  * a better name.*/
3880     rme.a += weird_angle;
3881     if ( both
3882          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3883          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3884     {
3885         rother.a += weird_angle;
3886     }
3889 /**
3890  * Rotate one node.
3891  */
3892 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3894     Inkscape::NodePath::NodeSide *me, *other;
3895     bool both = false;
3897     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3898     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3900     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3901         me = &(n->p);
3902         other = &(n->n);
3903     } else if (!n->p.other) {
3904         me = &(n->n);
3905         other = &(n->p);
3906     } else {
3907         if (which > 0) { // right handle
3908             if (xn > xp) {
3909                 me = &(n->n);
3910                 other = &(n->p);
3911             } else {
3912                 me = &(n->p);
3913                 other = &(n->n);
3914             }
3915         } else if (which < 0){ // left handle
3916             if (xn <= xp) {
3917                 me = &(n->n);
3918                 other = &(n->p);
3919             } else {
3920                 me = &(n->p);
3921                 other = &(n->n);
3922             }
3923         } else { // both handles
3924             me = &(n->n);
3925             other = &(n->p);
3926             both = true;
3927         }
3928     }
3930     Radial rme(me->pos - n->pos);
3931     Radial rother(other->pos - n->pos);
3933     if (screen) {
3934         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3935     } else {
3936         node_rotate_one_internal (*n, angle, rme, rother, both);
3937     }
3939     me->pos = n->pos + NR::Point(rme);
3941     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3942         other->pos =  n->pos + NR::Point(rother);
3943     }
3945     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3946     // so here we just move all the knots without emitting move signals, for speed
3947     sp_node_update_handles(n, false);
3950 /**
3951  * Rotate selected nodes.
3952  */
3953 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3955     if (!nodepath || !nodepath->selected) return;
3957     if (g_list_length(nodepath->selected) == 1) {
3958        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3959         node_rotate_one (n, angle, which, screen);
3960     } else {
3961        // rotate as an object:
3963         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3964         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3965         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3966             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3967             box.expandTo (n->pos); // contain all selected nodes
3968         }
3970         gdouble rot;
3971         if (screen) {
3972             gdouble const zoom = nodepath->desktop->current_zoom();
3973             gdouble const zmove = angle / zoom;
3974             gdouble const r = NR::L2(box.max() - box.midpoint());
3975             rot = atan2(zmove, r);
3976         } else {
3977             rot = angle;
3978         }
3980         NR::Point rot_center;
3981         if (Inkscape::NodePath::Path::active_node == NULL)
3982             rot_center = box.midpoint();
3983         else
3984             rot_center = Inkscape::NodePath::Path::active_node->pos;
3986         NR::Matrix t =
3987             NR::Matrix (NR::translate(-rot_center)) *
3988             NR::Matrix (NR::rotate(rot)) *
3989             NR::Matrix (NR::translate(rot_center));
3991         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3992             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3993             n->pos *= t;
3994             n->n.pos *= t;
3995             n->p.pos *= t;
3996             sp_node_update_handles(n, false);
3997         }
3998     }
4000     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4003 /**
4004  * Scale one node.
4005  */
4006 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4008     bool both = false;
4009     Inkscape::NodePath::NodeSide *me, *other;
4011     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4012     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4014     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4015         me = &(n->p);
4016         other = &(n->n);
4017         n->code = NR_CURVETO;
4018     } else if (!n->p.other) {
4019         me = &(n->n);
4020         other = &(n->p);
4021         if (n->n.other)
4022             n->n.other->code = NR_CURVETO;
4023     } else {
4024         if (which > 0) { // right handle
4025             if (xn > xp) {
4026                 me = &(n->n);
4027                 other = &(n->p);
4028                 if (n->n.other)
4029                     n->n.other->code = NR_CURVETO;
4030             } else {
4031                 me = &(n->p);
4032                 other = &(n->n);
4033                 n->code = NR_CURVETO;
4034             }
4035         } else if (which < 0){ // left handle
4036             if (xn <= xp) {
4037                 me = &(n->n);
4038                 other = &(n->p);
4039                 if (n->n.other)
4040                     n->n.other->code = NR_CURVETO;
4041             } else {
4042                 me = &(n->p);
4043                 other = &(n->n);
4044                 n->code = NR_CURVETO;
4045             }
4046         } else { // both handles
4047             me = &(n->n);
4048             other = &(n->p);
4049             both = true;
4050             n->code = NR_CURVETO;
4051             if (n->n.other)
4052                 n->n.other->code = NR_CURVETO;
4053         }
4054     }
4056     Radial rme(me->pos - n->pos);
4057     Radial rother(other->pos - n->pos);
4059     rme.r += grow;
4060     if (rme.r < 0) rme.r = 0;
4061     if (rme.a == HUGE_VAL) {
4062         if (me->other) { // if direction is unknown, initialize it towards the next node
4063             Radial rme_next(me->other->pos - n->pos);
4064             rme.a = rme_next.a;
4065         } else { // if there's no next, initialize to 0
4066             rme.a = 0;
4067         }
4068     }
4069     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4070         rother.r += grow;
4071         if (rother.r < 0) rother.r = 0;
4072         if (rother.a == HUGE_VAL) {
4073             rother.a = rme.a + M_PI;
4074         }
4075     }
4077     me->pos = n->pos + NR::Point(rme);
4079     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4080         other->pos = n->pos + NR::Point(rother);
4081     }
4083     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4084     // so here we just move all the knots without emitting move signals, for speed
4085     sp_node_update_handles(n, false);
4088 /**
4089  * Scale selected nodes.
4090  */
4091 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4093     if (!nodepath || !nodepath->selected) return;
4095     if (g_list_length(nodepath->selected) == 1) {
4096         // scale handles of the single selected node
4097         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4098         node_scale_one (n, grow, which);
4099     } else {
4100         // scale nodes as an "object":
4102         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4103         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4104         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4105             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4106             box.expandTo (n->pos); // contain all selected nodes
4107         }
4109         double scale = (box.maxExtent() + grow)/box.maxExtent();
4111         NR::Point scale_center;
4112         if (Inkscape::NodePath::Path::active_node == NULL)
4113             scale_center = box.midpoint();
4114         else
4115             scale_center = Inkscape::NodePath::Path::active_node->pos;
4117         NR::Matrix t =
4118             NR::Matrix (NR::translate(-scale_center)) *
4119             NR::Matrix (NR::scale(scale, scale)) *
4120             NR::Matrix (NR::translate(scale_center));
4122         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4123             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4124             n->pos *= t;
4125             n->n.pos *= t;
4126             n->p.pos *= t;
4127             sp_node_update_handles(n, false);
4128         }
4129     }
4131     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4134 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4136     if (!nodepath) return;
4137     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4140 /**
4141  * Flip selected nodes horizontally/vertically.
4142  */
4143 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
4145     if (!nodepath || !nodepath->selected) return;
4147     if (g_list_length(nodepath->selected) == 1 && !center) {
4148         // flip handles of the single selected node
4149         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4150         double temp = n->p.pos[axis];
4151         n->p.pos[axis] = n->n.pos[axis];
4152         n->n.pos[axis] = temp;
4153         sp_node_update_handles(n, false);
4154     } else {
4155         // scale nodes as an "object":
4157         NR::Rect box = sp_node_selected_bbox (nodepath);
4158         if (!center) {
4159             center = box.midpoint();
4160         }
4161         NR::Matrix t =
4162             NR::Matrix (NR::translate(- *center)) *
4163             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
4164             NR::Matrix (NR::translate(*center));
4166         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4167             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4168             n->pos *= t;
4169             n->n.pos *= t;
4170             n->p.pos *= t;
4171             sp_node_update_handles(n, false);
4172         }
4173     }
4175     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4178 NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4180     g_assert (nodepath->selected);
4182     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4183     NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4184     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4185         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4186         box.expandTo (n->pos); // contain all selected nodes
4187     }
4188     return box;
4191 //-----------------------------------------------
4192 /**
4193  * Return new subpath under given nodepath.
4194  */
4195 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4197     g_assert(nodepath);
4198     g_assert(nodepath->desktop);
4200    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4202     s->nodepath = nodepath;
4203     s->closed = FALSE;
4204     s->nodes = NULL;
4205     s->first = NULL;
4206     s->last = NULL;
4208     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4209     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4210     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4212     return s;
4215 /**
4216  * Destroy nodes in subpath, then subpath itself.
4217  */
4218 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4220     g_assert(subpath);
4221     g_assert(subpath->nodepath);
4222     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4224     while (subpath->nodes) {
4225         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4226     }
4228     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4230     g_free(subpath);
4233 /**
4234  * Link head to tail in subpath.
4235  */
4236 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4238     g_assert(!sp->closed);
4239     g_assert(sp->last != sp->first);
4240     g_assert(sp->first->code == NR_MOVETO);
4242     sp->closed = TRUE;
4244     //Link the head to the tail
4245     sp->first->p.other = sp->last;
4246     sp->last->n.other  = sp->first;
4247     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4248     sp->first          = sp->last;
4250     //Remove the extra end node
4251     sp_nodepath_node_destroy(sp->last->n.other);
4254 /**
4255  * Open closed (loopy) subpath at node.
4256  */
4257 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4259     g_assert(sp->closed);
4260     g_assert(n->subpath == sp);
4261     g_assert(sp->first == sp->last);
4263     /* We create new startpoint, current node will become last one */
4265    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4266                                                 &n->pos, &n->pos, &n->n.pos);
4269     sp->closed        = FALSE;
4271     //Unlink to make a head and tail
4272     sp->first         = new_path;
4273     sp->last          = n;
4274     n->n.other        = NULL;
4275     new_path->p.other = NULL;
4278 /**
4279  * Return new node in subpath with given properties.
4280  * \param pos Position of node.
4281  * \param ppos Handle position in previous direction
4282  * \param npos Handle position in previous direction
4283  */
4284 Inkscape::NodePath::Node *
4285 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)
4287     g_assert(sp);
4288     g_assert(sp->nodepath);
4289     g_assert(sp->nodepath->desktop);
4291     if (nodechunk == NULL)
4292         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4294     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4296     n->subpath  = sp;
4298     if (type != Inkscape::NodePath::NODE_NONE) {
4299         // use the type from sodipodi:nodetypes
4300         n->type = type;
4301     } else {
4302         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4303             // points are (almost) collinear
4304             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4305                 // endnode, or a node with a retracted handle
4306                 n->type = Inkscape::NodePath::NODE_CUSP;
4307             } else {
4308                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4309             }
4310         } else {
4311             n->type = Inkscape::NodePath::NODE_CUSP;
4312         }
4313     }
4315     n->code     = code;
4316     n->selected = FALSE;
4317     n->pos      = *pos;
4318     n->p.pos    = *ppos;
4319     n->n.pos    = *npos;
4321     n->dragging_out = NULL;
4323     Inkscape::NodePath::Node *prev;
4324     if (next) {
4325         //g_assert(g_list_find(sp->nodes, next));
4326         prev = next->p.other;
4327     } else {
4328         prev = sp->last;
4329     }
4331     if (prev)
4332         prev->n.other = n;
4333     else
4334         sp->first = n;
4336     if (next)
4337         next->p.other = n;
4338     else
4339         sp->last = n;
4341     n->p.other = prev;
4342     n->n.other = next;
4344     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"));
4345     sp_knot_set_position(n->knot, pos, 0);
4347     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4348     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4349     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4350     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4351     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4352     sp_knot_update_ctrl(n->knot);
4354     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4355     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4356     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4357     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4358     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4359     sp_knot_show(n->knot);
4361     // We only create handle knots and lines on demand
4362     n->p.knot = NULL;
4363     n->p.line = NULL;
4364     n->n.knot = NULL;
4365     n->n.line = NULL;
4367     sp->nodes = g_list_prepend(sp->nodes, n);
4369     return n;
4372 /**
4373  * Destroy node and its knots, link neighbors in subpath.
4374  */
4375 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4377     g_assert(node);
4378     g_assert(node->subpath);
4379     g_assert(SP_IS_KNOT(node->knot));
4381    Inkscape::NodePath::SubPath *sp = node->subpath;
4383     if (node->selected) { // first, deselect
4384         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4385         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4386     }
4388     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4390     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4391     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4392     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4393     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4394     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4395     g_object_unref(G_OBJECT(node->knot));
4397     if (node->p.knot) {
4398         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4399         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4400         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4401         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4402         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4403         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4404         g_object_unref(G_OBJECT(node->p.knot));
4405         node->p.knot = NULL;
4406     }
4408     if (node->n.knot) {
4409         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4410         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4411         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4412         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4413         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4414         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4415         g_object_unref(G_OBJECT(node->n.knot));
4416         node->n.knot = NULL;
4417     }
4419     if (node->p.line)
4420         gtk_object_destroy(GTK_OBJECT(node->p.line));
4421     if (node->n.line)
4422         gtk_object_destroy(GTK_OBJECT(node->n.line));
4424     if (sp->nodes) { // there are others nodes on the subpath
4425         if (sp->closed) {
4426             if (sp->first == node) {
4427                 g_assert(sp->last == node);
4428                 sp->first = node->n.other;
4429                 sp->last = sp->first;
4430             }
4431             node->p.other->n.other = node->n.other;
4432             node->n.other->p.other = node->p.other;
4433         } else {
4434             if (sp->first == node) {
4435                 sp->first = node->n.other;
4436                 sp->first->code = NR_MOVETO;
4437             }
4438             if (sp->last == node) sp->last = node->p.other;
4439             if (node->p.other) node->p.other->n.other = node->n.other;
4440             if (node->n.other) node->n.other->p.other = node->p.other;
4441         }
4442     } else { // this was the last node on subpath
4443         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4444     }
4446     g_mem_chunk_free(nodechunk, node);
4449 /**
4450  * Returns one of the node's two sides.
4451  * \param which Indicates which side.
4452  * \return Pointer to previous node side if which==-1, next if which==1.
4453  */
4454 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4456     g_assert(node);
4458     switch (which) {
4459         case -1:
4460             return &node->p;
4461         case 1:
4462             return &node->n;
4463         default:
4464             break;
4465     }
4467     g_assert_not_reached();
4469     return NULL;
4472 /**
4473  * Return the other side of the node, given one of its sides.
4474  */
4475 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4477     g_assert(node);
4479     if (me == &node->p) return &node->n;
4480     if (me == &node->n) return &node->p;
4482     g_assert_not_reached();
4484     return NULL;
4487 /**
4488  * Return NRPathcode on the given side of the node.
4489  */
4490 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4492     g_assert(node);
4494     if (me == &node->p) {
4495         if (node->p.other) return (NRPathcode)node->code;
4496         return NR_MOVETO;
4497     }
4499     if (me == &node->n) {
4500         if (node->n.other) return (NRPathcode)node->n.other->code;
4501         return NR_MOVETO;
4502     }
4504     g_assert_not_reached();
4506     return NR_END;
4509 /**
4510  * Return node with the given index
4511  */
4512 Inkscape::NodePath::Node *
4513 sp_nodepath_get_node_by_index(int index)
4515     Inkscape::NodePath::Node *e = NULL;
4517     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4518     if (!nodepath) {
4519         return e;
4520     }
4522     //find segment
4523     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4525         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4526         int n = g_list_length(sp->nodes);
4527         if (sp->closed) {
4528             n++;
4529         }
4531         //if the piece belongs to this subpath grab it
4532         //otherwise move onto the next subpath
4533         if (index < n) {
4534             e = sp->first;
4535             for (int i = 0; i < index; ++i) {
4536                 e = e->n.other;
4537             }
4538             break;
4539         } else {
4540             if (sp->closed) {
4541                 index -= (n+1);
4542             } else {
4543                 index -= n;
4544             }
4545         }
4546     }
4548     return e;
4551 /**
4552  * Returns plain text meaning of node type.
4553  */
4554 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4556     unsigned retracted = 0;
4557     bool endnode = false;
4559     for (int which = -1; which <= 1; which += 2) {
4560         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4561         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4562             retracted ++;
4563         if (!side->other)
4564             endnode = true;
4565     }
4567     if (retracted == 0) {
4568         if (endnode) {
4569                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4570                 return _("end node");
4571         } else {
4572             switch (node->type) {
4573                 case Inkscape::NodePath::NODE_CUSP:
4574                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4575                     return _("cusp");
4576                 case Inkscape::NodePath::NODE_SMOOTH:
4577                     // TRANSLATORS: "smooth" is an adjective here
4578                     return _("smooth");
4579                 case Inkscape::NodePath::NODE_SYMM:
4580                     return _("symmetric");
4581             }
4582         }
4583     } else if (retracted == 1) {
4584         if (endnode) {
4585             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4586             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4587         } else {
4588             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4589         }
4590     } else {
4591         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4592     }
4594     return NULL;
4597 /**
4598  * Handles content of statusbar as long as node tool is active.
4599  */
4600 void
4601 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4603     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");
4604     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4606     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4607     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4608     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4609     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4611     SPDesktop *desktop = NULL;
4612     if (nodepath) {
4613         desktop = nodepath->desktop;
4614     } else {
4615         desktop = SP_ACTIVE_DESKTOP;
4616     }
4618     SPEventContext *ec = desktop->event_context;
4619     if (!ec) return;
4620     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4621     if (!mc) return;
4623     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4625     if (selected_nodes == 0) {
4626         Inkscape::Selection *sel = desktop->selection;
4627         if (!sel || sel->isEmpty()) {
4628             mc->setF(Inkscape::NORMAL_MESSAGE,
4629                      _("Select a single object to edit its nodes or handles."));
4630         } else {
4631             if (nodepath) {
4632             mc->setF(Inkscape::NORMAL_MESSAGE,
4633                      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.",
4634                               "<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.",
4635                               total_nodes),
4636                      total_nodes);
4637             } else {
4638                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4639                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4640                 } else {
4641                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4642                 }
4643             }
4644         }
4645     } else if (nodepath && selected_nodes == 1) {
4646         mc->setF(Inkscape::NORMAL_MESSAGE,
4647                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4648                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4649                           total_nodes),
4650                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4651     } else {
4652         if (selected_subpaths > 1) {
4653             mc->setF(Inkscape::NORMAL_MESSAGE,
4654                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4655                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4656                               total_nodes),
4657                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4658         } else {
4659             mc->setF(Inkscape::NORMAL_MESSAGE,
4660                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4661                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4662                               total_nodes),
4663                      selected_nodes, total_nodes, when_selected);
4664         }
4665     }
4668 /*
4669  * returns a *copy* of the curve of that object.
4670  */
4671 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4672     if (!object)
4673         return NULL;
4675     SPCurve *curve = NULL;
4676     if (SP_IS_PATH(object)) {
4677         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4678         curve = sp_curve_copy(curve_new);
4679     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4680         const gchar *svgd = object->repr->attribute(key);
4681         if (svgd) {
4682             NArtBpath *bpath = sp_svg_read_path(svgd);
4683             SPCurve *curve_new = sp_curve_new_from_bpath(bpath);
4684             if (curve_new) {
4685                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4686             } else {
4687                 g_free(bpath);
4688             }
4689         }
4690     }
4692     return curve;
4695 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4696     if (!np || !np->object || !curve)
4697         return;
4699     if (SP_IS_PATH(np->object)) {
4700         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4701             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4702         } else {
4703             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4704         }
4705     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4706         // FIXME: this writing to string and then reading from string is bound to be slow.
4707         // create a method to convert from curve directly to 2geom...
4708         gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
4709         LIVEPATHEFFECT(np->object)->lpe->setParameter(np->repr_key, svgpath);
4710         g_free(svgpath);
4712         np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4713     }
4716 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4717     np->show_helperpath = show;
4719     if (show) {
4720         SPCurve *helper_curve = sp_curve_copy(np->curve);
4721         sp_curve_transform(helper_curve, np->i2d );
4722         if (!np->helper_path) {
4723             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4724             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);
4725             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4726             sp_canvas_item_move_to_z(np->helper_path, 0);
4727             sp_canvas_item_show(np->helper_path);
4728         } else {
4729             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4730         }
4731         sp_curve_unref(helper_curve);
4732     } else {
4733         if (np->helper_path) {
4734             GtkObject *temp = np->helper_path;
4735             np->helper_path = NULL;
4736             gtk_object_destroy(temp);
4737         }
4738     }
4741 /* sp_nodepath_make_straight_path:
4742  *   Prevents user from curving the path by dragging a segment or activating handles etc.
4743  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
4744  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
4745  */
4746 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4747     np->straight_path = true;
4748     np->show_handles = false;
4749     g_message("add code to make the path straight.");
4750     // do sp_nodepath_convert_node_type on all nodes?
4751     // coding tip: search for this text : "Make selected segments lines"
4755 /*
4756   Local Variables:
4757   mode:c++
4758   c-file-style:"stroustrup"
4759   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4760   indent-tabs-mode:nil
4761   fill-column:99
4762   End:
4763 */
4764 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :