Code

ff442fd702aad76cfa8ba8f524b26e137e57311f
[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 = curve->first_bpath();
184     gint length = curve->_end;
185     if (length == 0) {
186         curve->unref();
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         curve->unref();
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 = curve->copy();
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     curve->unref();
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 = np->curve->copy();
269         helper_curve->transform(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         helper_curve->unref();
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         np->curve->unref();
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 const *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         curve->unref();
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     np->curve->unref();
537     np->curve = create_curve(np);
539     sp_nodepath_set_curve(np, np->curve);
541     if (np->show_helperpath) {
542         SPCurve * helper_curve = np->curve->copy();
543         helper_curve->transform(np->i2d );
544         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
545         helper_curve->unref();
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     np->curve->unref();
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 = np->curve->copy();
580         helper_curve->transform(np->i2d );
581         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
582         helper_curve->unref();
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     curve->unref();
658 /**
659  * Create curve from path.
660  */
661 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
663     SPCurve *curve = new SPCurve();
665     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
666        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
667         curve->moveto(sp->first->pos * np->d2i);
668        Inkscape::NodePath::Node *n = sp->first->n.other;
669         while (n) {
670             NR::Point const end_pt = n->pos * np->d2i;
671             switch (n->code) {
672                 case NR_LINETO:
673                     curve->lineto(end_pt);
674                     break;
675                 case NR_CURVETO:
676                     curve->curveto(n->p.other->n.pos * np->d2i,
677                                      n->p.pos * np->d2i,
678                                      end_pt);
679                     break;
680                 default:
681                     g_assert_not_reached();
682                     break;
683             }
684             if (n != sp->last) {
685                 n = n->n.other;
686             } else {
687                 n = NULL;
688             }
689         }
690         if (sp->closed) {
691             curve->closepath();
692         }
693     }
695     return curve;
698 /**
699  * Convert path type string to sodipodi:nodetypes style.
700  */
701 static gchar *create_typestr(Inkscape::NodePath::Path *np)
703     gchar *typestr = g_new(gchar, 32);
704     gint len = 32;
705     gint pos = 0;
707     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
708        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
710         if (pos >= len) {
711             typestr = g_renew(gchar, typestr, len + 32);
712             len += 32;
713         }
715         typestr[pos++] = 'c';
717        Inkscape::NodePath::Node *n;
718         n = sp->first->n.other;
719         while (n) {
720             gchar code;
722             switch (n->type) {
723                 case Inkscape::NodePath::NODE_CUSP:
724                     code = 'c';
725                     break;
726                 case Inkscape::NodePath::NODE_SMOOTH:
727                     code = 's';
728                     break;
729                 case Inkscape::NodePath::NODE_SYMM:
730                     code = 'z';
731                     break;
732                 default:
733                     g_assert_not_reached();
734                     code = '\0';
735                     break;
736             }
738             if (pos >= len) {
739                 typestr = g_renew(gchar, typestr, len + 32);
740                 len += 32;
741             }
743             typestr[pos++] = code;
745             if (n != sp->last) {
746                 n = n->n.other;
747             } else {
748                 n = NULL;
749             }
750         }
751     }
753     if (pos >= len) {
754         typestr = g_renew(gchar, typestr, len + 1);
755         len += 1;
756     }
758     typestr[pos++] = '\0';
760     return typestr;
763 /**
764  * Returns current path in context. // later eliminate this function at all!
765  */
766 static Inkscape::NodePath::Path *sp_nodepath_current()
768     if (!SP_ACTIVE_DESKTOP) {
769         return NULL;
770     }
772     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
774     if (!SP_IS_NODE_CONTEXT(event_context)) {
775         return NULL;
776     }
778     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
783 /**
784  \brief Fills node and handle positions for three nodes, splitting line
785   marked by end at distance t.
786  */
787 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
789     g_assert(new_path != NULL);
790     g_assert(end      != NULL);
792     g_assert(end->p.other == new_path);
793    Inkscape::NodePath::Node *start = new_path->p.other;
794     g_assert(start);
796     if (end->code == NR_LINETO) {
797         new_path->type =Inkscape::NodePath::NODE_CUSP;
798         new_path->code = NR_LINETO;
799         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
800     } else {
801         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
802         new_path->code = NR_CURVETO;
803         gdouble s      = 1 - t;
804         for (int dim = 0; dim < 2; dim++) {
805             NR::Coord const f000 = start->pos[dim];
806             NR::Coord const f001 = start->n.pos[dim];
807             NR::Coord const f011 = end->p.pos[dim];
808             NR::Coord const f111 = end->pos[dim];
809             NR::Coord const f00t = s * f000 + t * f001;
810             NR::Coord const f01t = s * f001 + t * f011;
811             NR::Coord const f11t = s * f011 + t * f111;
812             NR::Coord const f0tt = s * f00t + t * f01t;
813             NR::Coord const f1tt = s * f01t + t * f11t;
814             NR::Coord const fttt = s * f0tt + t * f1tt;
815             start->n.pos[dim]    = f00t;
816             new_path->p.pos[dim] = f0tt;
817             new_path->pos[dim]   = fttt;
818             new_path->n.pos[dim] = f1tt;
819             end->p.pos[dim]      = f11t;
820         }
821     }
824 /**
825  * Adds new node on direct line between two nodes, activates handles of all
826  * three nodes.
827  */
828 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
830     g_assert(end);
831     g_assert(end->subpath);
832     g_assert(g_list_find(end->subpath->nodes, end));
834    Inkscape::NodePath::Node *start = end->p.other;
835     g_assert( start->n.other == end );
836    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
837                                                end,
838                                                (NRPathcode)end->code == NR_LINETO?
839                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
840                                                (NRPathcode)end->code,
841                                                &start->pos, &start->pos, &start->n.pos);
842     sp_nodepath_line_midpoint(newnode, end, t);
844     sp_node_adjust_handles(start);
845     sp_node_update_handles(start);
846     sp_node_update_handles(newnode);
847     sp_node_adjust_handles(end);
848     sp_node_update_handles(end);
850     return newnode;
853 /**
854 \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
855 */
856 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
858     g_assert(node);
859     g_assert(node->subpath);
860     g_assert(g_list_find(node->subpath->nodes, node));
862    Inkscape::NodePath::SubPath *sp = node->subpath;
863     Inkscape::NodePath::Path *np    = sp->nodepath;
865     if (sp->closed) {
866         sp_nodepath_subpath_open(sp, node);
867         return sp->first;
868     } else {
869         // no break for end nodes
870         if (node == sp->first) return NULL;
871         if (node == sp->last ) return NULL;
873         // create a new subpath
874        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
876         // duplicate the break node as start of the new subpath
877        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
879         while (node->n.other) { // copy the remaining nodes into the new subpath
880            Inkscape::NodePath::Node *n  = node->n.other;
881            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);
882             if (n->selected) {
883                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
884             }
885             sp_nodepath_node_destroy(n); // remove the point on the original subpath
886         }
888         return newnode;
889     }
892 /**
893  * Duplicate node and connect to neighbours.
894  */
895 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
897     g_assert(node);
898     g_assert(node->subpath);
899     g_assert(g_list_find(node->subpath->nodes, node));
901    Inkscape::NodePath::SubPath *sp = node->subpath;
903     NRPathcode code = (NRPathcode) node->code;
904     if (code == NR_MOVETO) { // if node is the endnode,
905         node->code = NR_LINETO; // new one is inserted before it, so change that to line
906     }
908     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
910     if (!node->n.other || !node->p.other) // if node is an endnode, select it
911         return node;
912     else
913         return newnode; // otherwise select the newly created node
916 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
918     node->p.pos = (node->pos + (node->pos - node->n.pos));
921 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
923     node->n.pos = (node->pos + (node->pos - node->p.pos));
926 /**
927  * Change line type at node, with side effects on neighbours.
928  */
929 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
931     g_assert(end);
932     g_assert(end->subpath);
933     g_assert(end->p.other);
935     if (end->code == static_cast< guint > ( code ) )
936         return;
938    Inkscape::NodePath::Node *start = end->p.other;
940     end->code = code;
942     if (code == NR_LINETO) {
943         if (start->code == NR_LINETO) {
944             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
945         }
946         if (end->n.other) {
947             if (end->n.other->code == NR_LINETO) {
948                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
949             }
950         }
951     } else {
952         NR::Point delta = end->pos - start->pos;
953         start->n.pos = start->pos + delta / 3;
954         end->p.pos = end->pos - delta / 3;
955         sp_node_adjust_handle(start, 1);
956         sp_node_adjust_handle(end, -1);
957     }
959     sp_node_update_handles(start);
960     sp_node_update_handles(end);
963 /**
964  * Change node type, and its handles accordingly.
965  */
966 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
968     g_assert(node);
969     g_assert(node->subpath);
971     if ((node->p.other != NULL) && (node->n.other != NULL)) {
972         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
973             type =Inkscape::NodePath::NODE_CUSP;
974         }
975     }
977     node->type = type;
979     if (node->type == Inkscape::NodePath::NODE_CUSP) {
980         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
981         node->knot->setSize (node->selected? 11 : 9);
982         sp_knot_update_ctrl(node->knot);
983     } else {
984         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
985         node->knot->setSize (node->selected? 9 : 7);
986         sp_knot_update_ctrl(node->knot);
987     }
989     // if one of handles is mouseovered, preserve its position
990     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
991         sp_node_adjust_handle(node, 1);
992     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
993         sp_node_adjust_handle(node, -1);
994     } else {
995         sp_node_adjust_handles(node);
996     }
998     sp_node_update_handles(node);
1000     sp_nodepath_update_statusbar(node->subpath->nodepath);
1002     return node;
1005 bool
1006 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1008         Inkscape::NodePath::Node *othernode = side->other;
1009         if (!othernode)
1010             return false;
1011         NRPathcode const code = sp_node_path_code_from_side(node, side);
1012         if (code == NR_LINETO)
1013             return true;
1014         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1015         if (&node->p == side) {
1016             other_to_me = &othernode->n;
1017         } else if (&node->n == side) {
1018             other_to_me = &othernode->p;
1019         } 
1020         if (!other_to_me)
1021             return false;
1022         bool is_line = 
1023              (NR::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1024               NR::L2(node->pos - side->pos) < 1e-6);
1025         return is_line;
1028 /**
1029  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1030  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1031  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1032  * If already cusp and set to cusp, retracts handles.
1033 */
1034 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1036     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1038 /* 
1039   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1040  
1041         if (two_handles) {
1042             // do nothing, adjust_handles called via set_node_type will line them up
1043         } else if (one_handle) {
1044             if (opposite_to_handle_is_line) {
1045                 if (lined_up) {
1046                     // already half-smooth; pull opposite handle too making it fully smooth
1047                 } else {
1048                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1049                 }
1050             } else {
1051                 // pull opposite handle in line with the existing one
1052             }
1053         } else if (no_handles) {
1054             if (both_segments_are_lines OR both_segments_are_curves) {
1055                 //pull both handles
1056             } else {
1057                 // pull the handle opposite to line segment, making node half-smooth
1058             }
1059         }
1060 */
1061         bool p_has_handle = (NR::L2(node->pos  - node->p.pos) > 1e-6);
1062         bool n_has_handle = (NR::L2(node->pos  - node->n.pos) > 1e-6);
1063         bool p_is_line = sp_node_side_is_line(node, &node->p);
1064         bool n_is_line = sp_node_side_is_line(node, &node->n);
1066         if (p_has_handle && n_has_handle) {
1067             // do nothing, adjust_handles will line them up
1068         } else if (p_has_handle || n_has_handle) {
1069             if (p_has_handle && n_is_line) {
1070                 Radial line (node->n.other->pos - node->pos);
1071                 Radial handle (node->pos - node->p.pos);
1072                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1073                     // already half-smooth; pull opposite handle too making it fully smooth
1074                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1075                 } else {
1076                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1077                 }
1078             } else if (n_has_handle && p_is_line) {
1079                 Radial line (node->p.other->pos - node->pos);
1080                 Radial handle (node->pos - node->n.pos);
1081                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1082                     // already half-smooth; pull opposite handle too making it fully smooth
1083                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1084                 } else {
1085                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1086                 }
1087             } else if (p_has_handle && node->n.other) {
1088                 // pull n handle
1089                 node->n.other->code = NR_CURVETO;
1090                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1091                     NR::L2(node->p.pos - node->pos) :
1092                     NR::L2(node->n.other->pos - node->pos) / 3;
1093                 node->n.pos = node->pos - (len / NR::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1094             } else if (n_has_handle && node->p.other) {
1095                 // pull p handle
1096                 node->code = NR_CURVETO;
1097                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1098                     NR::L2(node->n.pos - node->pos) :
1099                     NR::L2(node->p.other->pos - node->pos) / 3;
1100                 node->p.pos = node->pos - (len / NR::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1101             }
1102         } else if (!p_has_handle && !n_has_handle) {
1103             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1104                 // no handles, but both segments are either lnes or curves:
1105                 //pull both handles
1107                 // convert both to curves:
1108                 node->code = NR_CURVETO;
1109                 node->n.other->code = NR_CURVETO;
1111                 NR::Point leg_prev = node->pos - node->p.other->pos;
1112                 NR::Point leg_next = node->pos - node->n.other->pos;
1114                 double norm_leg_prev = L2(leg_prev);
1115                 double norm_leg_next = L2(leg_next);
1117                 NR::Point delta;
1118                 if (norm_leg_next > 0.0) {
1119                     delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1120                     (&delta)->normalize();
1121                 }
1123                 if (type == Inkscape::NodePath::NODE_SYMM) {
1124                     double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1125                     node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1126                     node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1127                 } else {
1128                     // length of handle is proportional to distance to adjacent node
1129                     node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1130                     node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1131                 }
1133             } else {
1134                 // pull the handle opposite to line segment, making it half-smooth
1135                 if (p_is_line && node->n.other) {
1136                     if (type != Inkscape::NodePath::NODE_SYMM) {
1137                         // pull n handle
1138                         node->n.other->code = NR_CURVETO;
1139                         double len =  NR::L2(node->n.other->pos - node->pos) / 3;
1140                         node->n.pos = node->pos + (len / NR::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1141                     }
1142                 } else if (n_is_line && node->p.other) {
1143                     if (type != Inkscape::NodePath::NODE_SYMM) {
1144                         // pull p handle
1145                         node->code = NR_CURVETO;
1146                         double len =  NR::L2(node->p.other->pos - node->pos) / 3;
1147                         node->p.pos = node->pos + (len / NR::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1148                     }
1149                 }
1150             }
1151         }
1152     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1153         // cusping a cusp: retract nodes
1154         node->p.pos = node->pos;
1155         node->n.pos = node->pos;
1156     }
1158     sp_nodepath_set_node_type (node, type);
1161 /**
1162  * Move node to point, and adjust its and neighbouring handles.
1163  */
1164 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1166     NR::Point delta = p - node->pos;
1167     node->pos = p;
1169     node->p.pos += delta;
1170     node->n.pos += delta;
1172     Inkscape::NodePath::Node *node_p = NULL;
1173     Inkscape::NodePath::Node *node_n = NULL;
1175     if (node->p.other) {
1176         if (node->code == NR_LINETO) {
1177             sp_node_adjust_handle(node, 1);
1178             sp_node_adjust_handle(node->p.other, -1);
1179             node_p = node->p.other;
1180         }
1181     }
1182     if (node->n.other) {
1183         if (node->n.other->code == NR_LINETO) {
1184             sp_node_adjust_handle(node, -1);
1185             sp_node_adjust_handle(node->n.other, 1);
1186             node_n = node->n.other;
1187         }
1188     }
1190     // this function is only called from batch movers that will update display at the end
1191     // themselves, so here we just move all the knots without emitting move signals, for speed
1192     sp_node_update_handles(node, false);
1193     if (node_n) {
1194         sp_node_update_handles(node_n, false);
1195     }
1196     if (node_p) {
1197         sp_node_update_handles(node_p, false);
1198     }
1201 /**
1202  * Call sp_node_moveto() for node selection and handle possible snapping.
1203  */
1204 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1205                                             bool const snap, bool constrained = false, 
1206                                             Inkscape::Snapper::ConstraintLine const &constraint = NR::Point())
1208     NR::Coord best = NR_HUGE;
1209     NR::Point delta(dx, dy);
1210     NR::Point best_pt = delta;
1211     Inkscape::SnappedPoint best_abs;
1212     
1213     if (snap) {    
1214         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1215          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1216          * must provide that information. */
1217           
1218         // Build a list of the unselected nodes to which the snapper should snap 
1219         std::vector<NR::Point> unselected_nodes;
1220         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1221             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1222             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1223                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1224                 if (!node->selected) {
1225                     unselected_nodes.push_back(node->pos);
1226                 }    
1227             }
1228         }        
1229         
1230         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1231         
1232         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1233             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1234             m.setup(NULL, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1235             Inkscape::SnappedPoint s;
1236             if (constrained) {
1237                 Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1238                 dedicated_constraint.setPoint(n->pos);
1239                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, dedicated_constraint);
1240             } else {
1241                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta);
1242             }            
1243             if (s.getSnapped() && (s.getDistance() < best)) {
1244                 best = s.getDistance();
1245                 best_abs = s;
1246                 best_pt = s.getPoint() - n->pos;
1247             }
1248         }
1249                         
1250         if (best_abs.getSnapped()) {
1251             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1252         } else {
1253             nodepath->desktop->snapindicator->remove_snappoint();    
1254         }
1255     }
1257     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1258         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1259         sp_node_moveto(n, n->pos + best_pt);
1260     }
1262     // do not update repr here so that node dragging is acceptably fast
1263     update_object(nodepath);
1266 /**
1267 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1268 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1269 near x = 0.
1270  */
1271 double
1272 sculpt_profile (double x, double alpha, guint profile)
1274     if (x >= 1)
1275         return 0;
1276     if (x <= 0)
1277         return 1;
1279     switch (profile) {
1280         case SCULPT_PROFILE_LINEAR:
1281         return 1 - x;
1282         case SCULPT_PROFILE_BELL:
1283         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1284         case SCULPT_PROFILE_ELLIPTIC:
1285         return sqrt(1 - x*x);
1286     }
1288     return 1;
1291 double
1292 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1294     // extremely primitive for now, don't have time to look for the real one
1295     double lower = NR::L2(b - a);
1296     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1297     return (lower + upper)/2;
1300 void
1301 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1303     n->pos = n->origin + delta;
1304     n->n.pos = n->n.origin + delta_n;
1305     n->p.pos = n->p.origin + delta_p;
1306     sp_node_adjust_handles(n);
1307     sp_node_update_handles(n, false);
1310 /**
1311  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1312  * on how far they are from the dragged node n.
1313  */
1314 static void
1315 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1317     g_assert (n);
1318     g_assert (nodepath);
1319     g_assert (n->subpath->nodepath == nodepath);
1321     double pressure = n->knot->pressure;
1322     if (pressure == 0)
1323         pressure = 0.5; // default
1324     pressure = CLAMP (pressure, 0.2, 0.8);
1326     // map pressure to alpha = 1/5 ... 5
1327     double alpha = 1 - 2 * fabs(pressure - 0.5);
1328     if (pressure > 0.5)
1329         alpha = 1/alpha;
1331     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1333     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1334         // Only one subpath has selected nodes:
1335         // use linear mode, where the distance from n to node being dragged is calculated along the path
1337         double n_sel_range = 0, p_sel_range = 0;
1338         guint n_nodes = 0, p_nodes = 0;
1339         guint n_sel_nodes = 0, p_sel_nodes = 0;
1341         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1342         {
1343             double n_range = 0, p_range = 0;
1344             bool n_going = true, p_going = true;
1345             Inkscape::NodePath::Node *n_node = n;
1346             Inkscape::NodePath::Node *p_node = n;
1347             do {
1348                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1349                 if (n_node && n_going)
1350                     n_node = n_node->n.other;
1351                 if (n_node == NULL) {
1352                     n_going = false;
1353                 } else {
1354                     n_nodes ++;
1355                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1356                     if (n_node->selected) {
1357                         n_sel_nodes ++;
1358                         n_sel_range = n_range;
1359                     }
1360                     if (n_node == p_node) {
1361                         n_going = false;
1362                         p_going = false;
1363                     }
1364                 }
1365                 if (p_node && p_going)
1366                     p_node = p_node->p.other;
1367                 if (p_node == NULL) {
1368                     p_going = false;
1369                 } else {
1370                     p_nodes ++;
1371                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1372                     if (p_node->selected) {
1373                         p_sel_nodes ++;
1374                         p_sel_range = p_range;
1375                     }
1376                     if (p_node == n_node) {
1377                         n_going = false;
1378                         p_going = false;
1379                     }
1380                 }
1381             } while (n_going || p_going);
1382         }
1384         // Second pass: actually move nodes in this subpath
1385         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1386         {
1387             double n_range = 0, p_range = 0;
1388             bool n_going = true, p_going = true;
1389             Inkscape::NodePath::Node *n_node = n;
1390             Inkscape::NodePath::Node *p_node = n;
1391             do {
1392                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1393                 if (n_node && n_going)
1394                     n_node = n_node->n.other;
1395                 if (n_node == NULL) {
1396                     n_going = false;
1397                 } else {
1398                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1399                     if (n_node->selected) {
1400                         sp_nodepath_move_node_and_handles (n_node,
1401                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1402                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1403                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1404                     }
1405                     if (n_node == p_node) {
1406                         n_going = false;
1407                         p_going = false;
1408                     }
1409                 }
1410                 if (p_node && p_going)
1411                     p_node = p_node->p.other;
1412                 if (p_node == NULL) {
1413                     p_going = false;
1414                 } else {
1415                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1416                     if (p_node->selected) {
1417                         sp_nodepath_move_node_and_handles (p_node,
1418                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1419                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1420                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1421                     }
1422                     if (p_node == n_node) {
1423                         n_going = false;
1424                         p_going = false;
1425                     }
1426                 }
1427             } while (n_going || p_going);
1428         }
1430     } else {
1431         // Multiple subpaths have selected nodes:
1432         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1433         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1434         // fix the pear-like shape when sculpting e.g. a ring
1436         // First pass: calculate range
1437         gdouble direct_range = 0;
1438         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1439             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1440             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1441                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1442                 if (node->selected) {
1443                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1444                 }
1445             }
1446         }
1448         // Second pass: actually move nodes
1449         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1450             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1451             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1452                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1453                 if (node->selected) {
1454                     if (direct_range > 1e-6) {
1455                         sp_nodepath_move_node_and_handles (node,
1456                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1457                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1458                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1459                     } else {
1460                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1461                     }
1463                 }
1464             }
1465         }
1466     }
1468     // do not update repr here so that node dragging is acceptably fast
1469     update_object(nodepath);
1473 /**
1474  * Move node selection to point, adjust its and neighbouring handles,
1475  * handle possible snapping, and commit the change with possible undo.
1476  */
1477 void
1478 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1480     if (!nodepath) return;
1482     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1484     if (dx == 0) {
1485         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1486     } else if (dy == 0) {
1487         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1488     } else {
1489         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1490     }
1493 /**
1494  * Move node selection off screen and commit the change.
1495  */
1496 void
1497 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1499     // borrowed from sp_selection_move_screen in selection-chemistry.c
1500     // we find out the current zoom factor and divide deltas by it
1501     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1503     gdouble zoom = desktop->current_zoom();
1504     gdouble zdx = dx / zoom;
1505     gdouble zdy = dy / zoom;
1507     if (!nodepath) return;
1509     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1511     if (dx == 0) {
1512         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1513     } else if (dy == 0) {
1514         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1515     } else {
1516         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1517     }
1520 /**
1521  * Move selected nodes to the absolute position given
1522  */
1523 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis)
1525     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1526         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1527         NR::Point npos(axis == NR::X ? val : n->pos[NR::X], axis == NR::Y ? val : n->pos[NR::Y]);
1528         sp_node_moveto(n, npos);
1529     }
1531     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1534 /**
1535  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1536  */
1537 NR::Maybe<NR::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1539     NR::Maybe<NR::Coord> no_coord = NR::Nothing();
1540     g_return_val_if_fail(nodepath->selected, no_coord);
1542     // determine coordinate of first selected node
1543     GList *nsel = nodepath->selected;
1544     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1545     NR::Coord coord = n->pos[axis];
1546     bool coincide = true;
1548     // compare it to the coordinates of all the other selected nodes
1549     for (GList *l = nsel->next; l != NULL; l = l->next) {
1550         n = (Inkscape::NodePath::Node *) l->data;
1551         if (n->pos[axis] != coord) {
1552             coincide = false;
1553         }
1554     }
1555     if (coincide) {
1556         return coord;
1557     } else {
1558         NR::Rect bbox = sp_node_selected_bbox(nodepath);
1559         // currently we return the coordinate of the bounding box midpoint because I don't know how
1560         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1561         return bbox.midpoint()[axis];
1562     }
1565 /** If they don't yet exist, creates knot and line for the given side of the node */
1566 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1568     if (!side->knot) {
1569         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"));
1571         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1572         side->knot->setSize (7);
1573         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1574         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1575         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1576         sp_knot_update_ctrl(side->knot);
1578         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1579         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1580         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1581         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1582         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1583         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1584     }
1586     if (!side->line) {
1587         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1588                                         SP_TYPE_CTRLLINE, NULL);
1589     }
1592 /**
1593  * Ensure the given handle of the node is visible/invisible, update its screen position
1594  */
1595 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1597     g_assert(node != NULL);
1599    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1600     NRPathcode code = sp_node_path_code_from_side(node, side);
1602     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1604     if (show_handle) {
1605         if (!side->knot) { // No handle knot at all
1606             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1607             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1608             side->knot->pos = side->pos;
1609             if (side->knot->item)
1610                 SP_CTRL(side->knot->item)->moveto(side->pos);
1611             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1612             sp_knot_show(side->knot);
1613         } else {
1614             if (side->knot->pos != side->pos) { // only if it's really moved
1615                 if (fire_move_signals) {
1616                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1617                 } else {
1618                     sp_knot_moveto(side->knot, &side->pos);
1619                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1620                 }
1621             }
1622             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1623                 sp_knot_show(side->knot);
1624             }
1625         }
1626         sp_canvas_item_show(side->line);
1627     } else {
1628         if (side->knot) {
1629             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1630                 sp_knot_hide(side->knot);
1631             }
1632         }
1633         if (side->line) {
1634             sp_canvas_item_hide(side->line);
1635         }
1636     }
1639 /**
1640  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1641  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1642  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1643  * updated; otherwise, just move the knots silently (used in batch moves).
1644  */
1645 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1647     g_assert(node != NULL);
1649     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1650         sp_knot_show(node->knot);
1651     }
1653     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1654         if (fire_move_signals)
1655             sp_knot_set_position(node->knot, &node->pos, 0);
1656         else
1657             sp_knot_moveto(node->knot, &node->pos);
1658     }
1660     gboolean show_handles = node->selected;
1661     if (node->p.other != NULL) {
1662         if (node->p.other->selected) show_handles = TRUE;
1663     }
1664     if (node->n.other != NULL) {
1665         if (node->n.other->selected) show_handles = TRUE;
1666     }
1668     if (node->subpath->nodepath->show_handles == false)
1669         show_handles = FALSE;
1671     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1672     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1675 /**
1676  * Call sp_node_update_handles() for all nodes on subpath.
1677  */
1678 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1680     g_assert(subpath != NULL);
1682     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1683         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1684     }
1687 /**
1688  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1689  */
1690 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1692     g_assert(nodepath != NULL);
1694     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1695         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1696     }
1699 void
1700 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1702     if (nodepath == NULL) return;
1704     nodepath->show_handles = show;
1705     sp_nodepath_update_handles(nodepath);
1708 /**
1709  * Adds all selected nodes in nodepath to list.
1710  */
1711 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1713     StlConv<Node *>::list(l, selected);
1714 /// \todo this adds a copying, rework when the selection becomes a stl list
1717 /**
1718  * Align selected nodes on the specified axis.
1719  */
1720 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1722     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1723         return;
1724     }
1726     if ( !nodepath->selected->next ) { // only one node selected
1727         return;
1728     }
1729    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1730     NR::Point dest(pNode->pos);
1731     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1732         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1733         if (pNode) {
1734             dest[axis] = pNode->pos[axis];
1735             sp_node_moveto(pNode, dest);
1736         }
1737     }
1739     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1742 /// Helper struct.
1743 struct NodeSort
1745    Inkscape::NodePath::Node *_node;
1746     NR::Coord _coord;
1747     /// \todo use vectorof pointers instead of calling copy ctor
1748     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1749         _node(node), _coord(node->pos[axis])
1750     {}
1752 };
1754 static bool operator<(NodeSort const &a, NodeSort const &b)
1756     return (a._coord < b._coord);
1759 /**
1760  * Distribute selected nodes on the specified axis.
1761  */
1762 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1764     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1765         return;
1766     }
1768     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1769         return;
1770     }
1772    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1773     std::vector<NodeSort> sorted;
1774     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1775         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1776         if (pNode) {
1777             NodeSort n(pNode, axis);
1778             sorted.push_back(n);
1779             //dest[axis] = pNode->pos[axis];
1780             //sp_node_moveto(pNode, dest);
1781         }
1782     }
1783     std::sort(sorted.begin(), sorted.end());
1784     unsigned int len = sorted.size();
1785     //overall bboxes span
1786     float dist = (sorted.back()._coord -
1787                   sorted.front()._coord);
1788     //new distance between each bbox
1789     float step = (dist) / (len - 1);
1790     float pos = sorted.front()._coord;
1791     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1792           it < sorted.end();
1793           it ++ )
1794     {
1795         NR::Point dest((*it)._node->pos);
1796         dest[axis] = pos;
1797         sp_node_moveto((*it)._node, dest);
1798         pos += step;
1799     }
1801     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1805 /**
1806  * Call sp_nodepath_line_add_node() for all selected segments.
1807  */
1808 void
1809 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1811     if (!nodepath) {
1812         return;
1813     }
1815     GList *nl = NULL;
1817     int n_added = 0;
1819     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1820        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1821         g_assert(t->selected);
1822         if (t->p.other && t->p.other->selected) {
1823             nl = g_list_prepend(nl, t);
1824         }
1825     }
1827     while (nl) {
1828        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1829        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1830        sp_nodepath_node_select(n, TRUE, FALSE);
1831        n_added ++;
1832        nl = g_list_remove(nl, t);
1833     }
1835     /** \todo fixme: adjust ? */
1836     sp_nodepath_update_handles(nodepath);
1838     if (n_added > 1) {
1839         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1840     } else if (n_added > 0) {
1841         sp_nodepath_update_repr(nodepath, _("Add node"));
1842     }
1844     sp_nodepath_update_statusbar(nodepath);
1847 /**
1848  * Select segment nearest to point
1849  */
1850 void
1851 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1853     if (!nodepath) {
1854         return;
1855     }
1857     sp_nodepath_ensure_livarot_path(nodepath);
1858     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1859     if (!maybe_position) {
1860         return;
1861     }
1862     Path::cut_position position = *maybe_position;
1864     //find segment to segment
1865     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1867     //fixme: this can return NULL, so check before proceeding.
1868     g_return_if_fail(e != NULL);
1870     gboolean force = FALSE;
1871     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1872         force = TRUE;
1873     }
1874     sp_nodepath_node_select(e, (gboolean) toggle, force);
1875     if (e->p.other)
1876         sp_nodepath_node_select(e->p.other, TRUE, force);
1878     sp_nodepath_update_handles(nodepath);
1880     sp_nodepath_update_statusbar(nodepath);
1883 /**
1884  * Add a node nearest to point
1885  */
1886 void
1887 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1889     if (!nodepath) {
1890         return;
1891     }
1893     sp_nodepath_ensure_livarot_path(nodepath);
1894     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1895     if (!maybe_position) {
1896         return;
1897     }
1898     Path::cut_position position = *maybe_position;
1900     //find segment to split
1901     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1903     //don't know why but t seems to flip for lines
1904     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1905         position.t = 1.0 - position.t;
1906     }
1907     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1908     sp_nodepath_node_select(n, FALSE, TRUE);
1910     /* fixme: adjust ? */
1911     sp_nodepath_update_handles(nodepath);
1913     sp_nodepath_update_repr(nodepath, _("Add node"));
1915     sp_nodepath_update_statusbar(nodepath);
1918 /*
1919  * Adjusts a segment so that t moves by a certain delta for dragging
1920  * converts lines to curves
1921  *
1922  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1923  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1924  */
1925 void
1926 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1928     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1930     //fixme: e and e->p can be NULL, so check for those before proceeding
1931     g_return_if_fail(e != NULL);
1932     g_return_if_fail(&e->p != NULL);
1934     /* feel good is an arbitrary parameter that distributes the delta between handles
1935      * if t of the drag point is less than 1/6 distance form the endpoint only
1936      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1937      */
1938     double feel_good;
1939     if (t <= 1.0 / 6.0)
1940         feel_good = 0;
1941     else if (t <= 0.5)
1942         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1943     else if (t <= 5.0 / 6.0)
1944         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1945     else
1946         feel_good = 1;
1948     //if we're dragging a line convert it to a curve
1949     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1950         sp_nodepath_set_line_type(e, NR_CURVETO);
1951     }
1953     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1954     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1955     e->p.other->n.pos += offsetcoord0;
1956     e->p.pos += offsetcoord1;
1958     // adjust handles of adjacent nodes where necessary
1959     sp_node_adjust_handle(e,1);
1960     sp_node_adjust_handle(e->p.other,-1);
1962     sp_nodepath_update_handles(e->subpath->nodepath);
1964     update_object(e->subpath->nodepath);
1966     sp_nodepath_update_statusbar(e->subpath->nodepath);
1970 /**
1971  * Call sp_nodepath_break() for all selected segments.
1972  */
1973 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
1975     if (!nodepath) return;
1977     GList *temp = NULL;
1978     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1979        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1980        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1981         if (nn == NULL) continue; // no break, no new node
1982         temp = g_list_prepend(temp, nn);
1983     }
1985     if (temp) {
1986         sp_nodepath_deselect(nodepath);
1987     }
1988     for (GList *l = temp; l != NULL; l = l->next) {
1989         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1990     }
1992     sp_nodepath_update_handles(nodepath);
1994     sp_nodepath_update_repr(nodepath, _("Break path"));
1997 /**
1998  * Duplicate the selected node(s).
1999  */
2000 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2002     if (!nodepath) {
2003         return;
2004     }
2006     GList *temp = NULL;
2007     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2008        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2009        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2010         if (nn == NULL) continue; // could not duplicate
2011         temp = g_list_prepend(temp, nn);
2012     }
2014     if (temp) {
2015         sp_nodepath_deselect(nodepath);
2016     }
2017     for (GList *l = temp; l != NULL; l = l->next) {
2018         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2019     }
2021     sp_nodepath_update_handles(nodepath);
2023     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2026 /**
2027  *  Internal function to join two nodes by merging them into one.
2028  */
2029 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2031     /* a and b are endpoints */
2033     // if one of the two nodes is mouseovered, fix its position
2034     NR::Point c;
2035     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2036         c = a->pos;
2037     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2038         c = b->pos;
2039     } else {
2040         // otherwise, move joined node to the midpoint
2041         c = (a->pos + b->pos) / 2;
2042     }
2044     if (a->subpath == b->subpath) {
2045        Inkscape::NodePath::SubPath *sp = a->subpath;
2046         sp_nodepath_subpath_close(sp);
2047         sp_node_moveto (sp->first, c);
2049         sp_nodepath_update_handles(sp->nodepath);
2050         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2051         return;
2052     }
2054     /* a and b are separate subpaths */
2055     Inkscape::NodePath::SubPath *sa = a->subpath;
2056     Inkscape::NodePath::SubPath *sb = b->subpath;
2057     NR::Point p;
2058     Inkscape::NodePath::Node *n;
2059     NRPathcode code;
2060     if (a == sa->first) {
2061         // we will now reverse sa, so that a is its last node, not first, and drop that node
2062         p = sa->first->n.pos;
2063         code = (NRPathcode)sa->first->n.other->code;
2064         // create new subpath
2065        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2066        // create a first moveto node on it
2067         n = sa->last;
2068         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2069         n = n->p.other;
2070         if (n == sa->first) n = NULL;
2071         while (n) {
2072             // copy the rest of the nodes from sa to t, going backwards
2073             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2074             n = n->p.other;
2075             if (n == sa->first) n = NULL;
2076         }
2077         // replace sa with t
2078         sp_nodepath_subpath_destroy(sa);
2079         sa = t;
2080     } else if (a == sa->last) {
2081         // a is already last, just drop it
2082         p = sa->last->p.pos;
2083         code = (NRPathcode)sa->last->code;
2084         sp_nodepath_node_destroy(sa->last);
2085     } else {
2086         code = NR_END;
2087         g_assert_not_reached();
2088     }
2090     if (b == sb->first) {
2091         // copy all nodes from b to a, forward 
2092         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2093         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2094             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2095         }
2096     } else if (b == sb->last) {
2097         // copy all nodes from b to a, backward 
2098         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2099         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2100             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2101         }
2102     } else {
2103         g_assert_not_reached();
2104     }
2105     /* and now destroy sb */
2107     sp_nodepath_subpath_destroy(sb);
2109     sp_nodepath_update_handles(sa->nodepath);
2111     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2113     sp_nodepath_update_statusbar(nodepath);
2116 /**
2117  *  Internal function to join two nodes by adding a segment between them.
2118  */
2119 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2121     if (a->subpath == b->subpath) {
2122        Inkscape::NodePath::SubPath *sp = a->subpath;
2124         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2125         sp->closed = TRUE;
2127         sp->first->p.other = sp->last;
2128         sp->last->n.other  = sp->first;
2130         sp_node_handle_mirror_p_to_n(sp->last);
2131         sp_node_handle_mirror_n_to_p(sp->first);
2133         sp->first->code = sp->last->code;
2134         sp->first       = sp->last;
2136         sp_nodepath_update_handles(sp->nodepath);
2138         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2140         return;
2141     }
2143     /* a and b are separate subpaths */
2144     Inkscape::NodePath::SubPath *sa = a->subpath;
2145     Inkscape::NodePath::SubPath *sb = b->subpath;
2147     Inkscape::NodePath::Node *n;
2148     NR::Point p;
2149     NRPathcode code;
2150     if (a == sa->first) {
2151         code = (NRPathcode) sa->first->n.other->code;
2152        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2153         n = sa->last;
2154         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2155         for (n = n->p.other; n != NULL; n = n->p.other) {
2156             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2157         }
2158         sp_nodepath_subpath_destroy(sa);
2159         sa = t;
2160     } else if (a == sa->last) {
2161         code = (NRPathcode)sa->last->code;
2162     } else {
2163         code = NR_END;
2164         g_assert_not_reached();
2165     }
2167     if (b == sb->first) {
2168         n = sb->first;
2169         sp_node_handle_mirror_p_to_n(sa->last);
2170         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2171         sp_node_handle_mirror_n_to_p(sa->last);
2172         for (n = n->n.other; n != NULL; n = n->n.other) {
2173             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2174         }
2175     } else if (b == sb->last) {
2176         n = sb->last;
2177         sp_node_handle_mirror_p_to_n(sa->last);
2178         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2179         sp_node_handle_mirror_n_to_p(sa->last);
2180         for (n = n->p.other; n != NULL; n = n->p.other) {
2181             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2182         }
2183     } else {
2184         g_assert_not_reached();
2185     }
2186     /* and now destroy sb */
2188     sp_nodepath_subpath_destroy(sb);
2190     sp_nodepath_update_handles(sa->nodepath);
2192     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2195 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2197 /**
2198  * Internal function to handle joining two nodes.
2199  */
2200 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2202     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2204     if (g_list_length(nodepath->selected) != 2) {
2205         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2206         return;
2207     }
2209     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2210     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2212     g_assert(a != b);
2213     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2214         // someone tried to join an orphan node (i.e. a single-node subpath).
2215         // this is not worth an error message, just fail silently.
2216         return;
2217     }
2219     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2220         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2221         return;
2222     }
2224     switch(mode) {
2225         case NODE_JOIN_ENDPOINTS:
2226             do_node_selected_join(nodepath, a, b);
2227             break;
2228         case NODE_JOIN_SEGMENT:
2229             do_node_selected_join_segment(nodepath, a, b);
2230             break;
2231     }
2234 /**
2235  *  Join two nodes by merging them into one.
2236  */
2237 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2239     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2242 /**
2243  *  Join two nodes by adding a segment between them.
2244  */
2245 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2247     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2250 /**
2251  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2252  */
2253 void sp_node_delete_preserve(GList *nodes_to_delete)
2255     GSList *nodepaths = NULL;
2257     while (nodes_to_delete) {
2258         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2259         Inkscape::NodePath::SubPath *sp = node->subpath;
2260         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2261         Inkscape::NodePath::Node *sample_cursor = NULL;
2262         Inkscape::NodePath::Node *sample_end = NULL;
2263         Inkscape::NodePath::Node *delete_cursor = node;
2264         bool just_delete = false;
2266         //find the start of this contiguous selection
2267         //move left to the first node that is not selected
2268         //or the start of the non-closed path
2269         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2270             delete_cursor = curr;
2271         }
2273         //just delete at the beginning of an open path
2274         if (!delete_cursor->p.other) {
2275             sample_cursor = delete_cursor;
2276             just_delete = true;
2277         } else {
2278             sample_cursor = delete_cursor->p.other;
2279         }
2281         //calculate points for each segment
2282         int rate = 5;
2283         float period = 1.0 / rate;
2284         std::vector<NR::Point> data;
2285         if (!just_delete) {
2286             data.push_back(sample_cursor->pos);
2287             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2288                 //just delete at the end of an open path
2289                 if (!sp->closed && curr == sp->last) {
2290                     just_delete = true;
2291                     break;
2292                 }
2294                 //sample points on the contiguous selected segment
2295                 NR::Point *bez;
2296                 bez = new NR::Point [4];
2297                 bez[0] = curr->pos;
2298                 bez[1] = curr->n.pos;
2299                 bez[2] = curr->n.other->p.pos;
2300                 bez[3] = curr->n.other->pos;
2301                 for (int i=1; i<rate; i++) {
2302                     gdouble t = i * period;
2303                     NR::Point p = bezier_pt(3, bez, t);
2304                     data.push_back(p);
2305                 }
2306                 data.push_back(curr->n.other->pos);
2308                 sample_end = curr->n.other;
2309                 //break if we've come full circle or hit the end of the selection
2310                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2311                     break;
2312                 }
2313             }
2314         }
2316         if (!just_delete) {
2317             //calculate the best fitting single segment and adjust the endpoints
2318             NR::Point *adata;
2319             adata = new NR::Point [data.size()];
2320             copy(data.begin(), data.end(), adata);
2322             NR::Point *bez;
2323             bez = new NR::Point [4];
2324             //would decreasing error create a better fitting approximation?
2325             gdouble error = 1.0;
2326             gint ret;
2327             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2329             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2330             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2331             //the resulting nodes behave as expected.
2332             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2333                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2334             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2335                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2337             //adjust endpoints
2338             sample_cursor->n.pos = bez[1];
2339             sample_end->p.pos = bez[2];
2340         }
2342         //destroy this contiguous selection
2343         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2344             Inkscape::NodePath::Node *temp = delete_cursor;
2345             if (delete_cursor->n.other == delete_cursor) {
2346                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2347                 delete_cursor = NULL;
2348             } else {
2349                 delete_cursor = delete_cursor->n.other;
2350             }
2351             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2352             sp_nodepath_node_destroy(temp);
2353         }
2355         sp_nodepath_update_handles(nodepath);
2357         if (!g_slist_find(nodepaths, nodepath))
2358             nodepaths = g_slist_prepend (nodepaths, nodepath);
2359     }
2361     for (GSList *i = nodepaths; i; i = i->next) {
2362         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2363         // different nodepaths will give us one undo event per nodepath
2364         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2366         // if the entire nodepath is removed, delete the selected object.
2367         if (nodepath->subpaths == NULL ||
2368             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2369             //at least 2
2370             sp_nodepath_get_node_count(nodepath) < 2) {
2371             SPDocument *document = sp_desktop_document (nodepath->desktop);
2372             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2373             //delete this nodepath's object, not the entire selection! (though at this time, this
2374             //does not matter)
2375             sp_selection_delete();
2376             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2377                               _("Delete nodes"));
2378         } else {
2379             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2380             sp_nodepath_update_statusbar(nodepath);
2381         }
2382     }
2384     g_slist_free (nodepaths);
2387 /**
2388  * Delete one or more selected nodes.
2389  */
2390 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2392     if (!nodepath) return;
2393     if (!nodepath->selected) return;
2395     /** \todo fixme: do it the right way */
2396     while (nodepath->selected) {
2397        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2398         sp_nodepath_node_destroy(node);
2399     }
2402     //clean up the nodepath (such as for trivial subpaths)
2403     sp_nodepath_cleanup(nodepath);
2405     sp_nodepath_update_handles(nodepath);
2407     // if the entire nodepath is removed, delete the selected object.
2408     if (nodepath->subpaths == NULL ||
2409         sp_nodepath_get_node_count(nodepath) < 2) {
2410         SPDocument *document = sp_desktop_document (nodepath->desktop);
2411         sp_selection_delete();
2412         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2413                           _("Delete nodes"));
2414         return;
2415     }
2417     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2419     sp_nodepath_update_statusbar(nodepath);
2422 /**
2423  * Delete one or more segments between two selected nodes.
2424  * This is the code for 'split'.
2425  */
2426 void
2427 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2429    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2430    Inkscape::NodePath::Node *curr, *next;     //Iterators
2432     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2434     if (g_list_length(nodepath->selected) != 2) {
2435         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2436                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2437         return;
2438     }
2440     //Selected nodes, not inclusive
2441    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2442    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2444     if ( ( a==b)                       ||  //same node
2445          (a->subpath  != b->subpath )  ||  //not the same path
2446          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2447          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2448     {
2449         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2450                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2451         return;
2452     }
2454     //###########################################
2455     //# BEGIN EDITS
2456     //###########################################
2457     //##################################
2458     //# CLOSED PATH
2459     //##################################
2460     if (a->subpath->closed) {
2463         gboolean reversed = FALSE;
2465         //Since we can go in a circle, we need to find the shorter distance.
2466         //  a->b or b->a
2467         start = end = NULL;
2468         int distance    = 0;
2469         int minDistance = 0;
2470         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2471             if (curr==b) {
2472                 //printf("a to b:%d\n", distance);
2473                 start = a;//go from a to b
2474                 end   = b;
2475                 minDistance = distance;
2476                 //printf("A to B :\n");
2477                 break;
2478             }
2479             distance++;
2480         }
2482         //try again, the other direction
2483         distance = 0;
2484         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2485             if (curr==a) {
2486                 //printf("b to a:%d\n", distance);
2487                 if (distance < minDistance) {
2488                     start    = b;  //we go from b to a
2489                     end      = a;
2490                     reversed = TRUE;
2491                     //printf("B to A\n");
2492                 }
2493                 break;
2494             }
2495             distance++;
2496         }
2499         //Copy everything from 'end' to 'start' to a new subpath
2500        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2501         for (curr=end ; curr ; curr=curr->n.other) {
2502             NRPathcode code = (NRPathcode) curr->code;
2503             if (curr == end)
2504                 code = NR_MOVETO;
2505             sp_nodepath_node_new(t, NULL,
2506                                  (Inkscape::NodePath::NodeType)curr->type, code,
2507                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2508             if (curr == start)
2509                 break;
2510         }
2511         sp_nodepath_subpath_destroy(a->subpath);
2514     }
2518     //##################################
2519     //# OPEN PATH
2520     //##################################
2521     else {
2523         //We need to get the direction of the list between A and B
2524         //Can we walk from a to b?
2525         start = end = NULL;
2526         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2527             if (curr==b) {
2528                 start = a;  //did it!  we go from a to b
2529                 end   = b;
2530                 //printf("A to B\n");
2531                 break;
2532             }
2533         }
2534         if (!start) {//didn't work?  let's try the other direction
2535             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2536                 if (curr==a) {
2537                     start = b;  //did it!  we go from b to a
2538                     end   = a;
2539                     //printf("B to A\n");
2540                     break;
2541                 }
2542             }
2543         }
2544         if (!start) {
2545             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2546                                                      _("Cannot find path between nodes."));
2547             return;
2548         }
2552         //Copy everything after 'end' to a new subpath
2553        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2554         for (curr=end ; curr ; curr=curr->n.other) {
2555             NRPathcode code = (NRPathcode) curr->code;
2556             if (curr == end)
2557                 code = NR_MOVETO;
2558             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2559                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2560         }
2562         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2563         for (curr = start->n.other ; curr  ; curr=next) {
2564             next = curr->n.other;
2565             sp_nodepath_node_destroy(curr);
2566         }
2568     }
2569     //###########################################
2570     //# END EDITS
2571     //###########################################
2573     //clean up the nodepath (such as for trivial subpaths)
2574     sp_nodepath_cleanup(nodepath);
2576     sp_nodepath_update_handles(nodepath);
2578     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2580     sp_nodepath_update_statusbar(nodepath);
2583 /**
2584  * Call sp_nodepath_set_line() for all selected segments.
2585  */
2586 void
2587 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2589     if (nodepath == NULL) return;
2591     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2592        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2593         g_assert(n->selected);
2594         if (n->p.other && n->p.other->selected) {
2595             sp_nodepath_set_line_type(n, code);
2596         }
2597     }
2599     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2602 /**
2603  * Call sp_nodepath_convert_node_type() for all selected nodes.
2604  */
2605 void
2606 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2608     if (nodepath == NULL) return;
2610     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2612     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2613         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2614     }
2616     sp_nodepath_update_repr(nodepath, _("Change node type"));
2619 /**
2620  * Change select status of node, update its own and neighbour handles.
2621  */
2622 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2624     node->selected = selected;
2626     if (selected) {
2627         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2628         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2629         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2630         sp_knot_update_ctrl(node->knot);
2631     } else {
2632         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2633         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2634         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2635         sp_knot_update_ctrl(node->knot);
2636     }
2638     sp_node_update_handles(node);
2639     if (node->n.other) sp_node_update_handles(node->n.other);
2640     if (node->p.other) sp_node_update_handles(node->p.other);
2643 /**
2644 \brief Select a node
2645 \param node     The node to select
2646 \param incremental   If true, add to selection, otherwise deselect others
2647 \param override   If true, always select this node, otherwise toggle selected status
2648 */
2649 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2651     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2653     if (incremental) {
2654         if (override) {
2655             if (!g_list_find(nodepath->selected, node)) {
2656                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2657             }
2658             sp_node_set_selected(node, TRUE);
2659         } else { // toggle
2660             if (node->selected) {
2661                 g_assert(g_list_find(nodepath->selected, node));
2662                 nodepath->selected = g_list_remove(nodepath->selected, node);
2663             } else {
2664                 g_assert(!g_list_find(nodepath->selected, node));
2665                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2666             }
2667             sp_node_set_selected(node, !node->selected);
2668         }
2669     } else {
2670         sp_nodepath_deselect(nodepath);
2671         nodepath->selected = g_list_prepend(nodepath->selected, node);
2672         sp_node_set_selected(node, TRUE);
2673     }
2675     sp_nodepath_update_statusbar(nodepath);
2679 /**
2680 \brief Deselect all nodes in the nodepath
2681 */
2682 void
2683 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2685     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2687     while (nodepath->selected) {
2688         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2689         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2690     }
2691     sp_nodepath_update_statusbar(nodepath);
2694 /**
2695 \brief Select or invert selection of all nodes in the nodepath
2696 */
2697 void
2698 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2700     if (!nodepath) return;
2702     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2703        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2704         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2705            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2706            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2707         }
2708     }
2711 /**
2712  * If nothing selected, does the same as sp_nodepath_select_all();
2713  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2714  * (i.e., similar to "select all in layer", with the "selected" subpaths
2715  * being treated as "layers" in the path).
2716  */
2717 void
2718 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2720     if (!nodepath) return;
2722     if (g_list_length (nodepath->selected) == 0) {
2723         sp_nodepath_select_all (nodepath, invert);
2724         return;
2725     }
2727     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2728     GSList *subpaths = NULL;
2730     for (GList *l = copy; l != NULL; l = l->next) {
2731         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2732         Inkscape::NodePath::SubPath *subpath = n->subpath;
2733         if (!g_slist_find (subpaths, subpath))
2734             subpaths = g_slist_prepend (subpaths, subpath);
2735     }
2737     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2738         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2739         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2740             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2741             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2742         }
2743     }
2745     g_slist_free (subpaths);
2746     g_list_free (copy);
2749 /**
2750  * \brief Select the node after the last selected; if none is selected,
2751  * select the first within path.
2752  */
2753 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2755     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2757    Inkscape::NodePath::Node *last = NULL;
2758     if (nodepath->selected) {
2759         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2760            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2761             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2762             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2763                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2764                 if (node->selected) {
2765                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2766                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2767                             if (spl->next) { // there's a next subpath
2768                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2769                                 last = subpath_next->first;
2770                             } else if (spl->prev) { // there's a previous subpath
2771                                 last = NULL; // to be set later to the first node of first subpath
2772                             } else {
2773                                 last = node->n.other;
2774                             }
2775                         } else {
2776                             last = node->n.other;
2777                         }
2778                     } else {
2779                         if (node->n.other) {
2780                             last = node->n.other;
2781                         } else {
2782                             if (spl->next) { // there's a next subpath
2783                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2784                                 last = subpath_next->first;
2785                             } else if (spl->prev) { // there's a previous subpath
2786                                 last = NULL; // to be set later to the first node of first subpath
2787                             } else {
2788                                 last = (Inkscape::NodePath::Node *) subpath->first;
2789                             }
2790                         }
2791                     }
2792                 }
2793             }
2794         }
2795         sp_nodepath_deselect(nodepath);
2796     }
2798     if (last) { // there's at least one more node after selected
2799         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2800     } else { // no more nodes, select the first one in first subpath
2801        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2802         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2803     }
2806 /**
2807  * \brief Select the node before the first selected; if none is selected,
2808  * select the last within path
2809  */
2810 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2812     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2814    Inkscape::NodePath::Node *last = NULL;
2815     if (nodepath->selected) {
2816         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2817            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2818             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2819                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2820                 if (node->selected) {
2821                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2822                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2823                             if (spl->prev) { // there's a prev subpath
2824                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2825                                 last = subpath_prev->last;
2826                             } else if (spl->next) { // there's a next subpath
2827                                 last = NULL; // to be set later to the last node of last subpath
2828                             } else {
2829                                 last = node->p.other;
2830                             }
2831                         } else {
2832                             last = node->p.other;
2833                         }
2834                     } else {
2835                         if (node->p.other) {
2836                             last = node->p.other;
2837                         } else {
2838                             if (spl->prev) { // there's a prev subpath
2839                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2840                                 last = subpath_prev->last;
2841                             } else if (spl->next) { // there's a next subpath
2842                                 last = NULL; // to be set later to the last node of last subpath
2843                             } else {
2844                                 last = (Inkscape::NodePath::Node *) subpath->last;
2845                             }
2846                         }
2847                     }
2848                 }
2849             }
2850         }
2851         sp_nodepath_deselect(nodepath);
2852     }
2854     if (last) { // there's at least one more node before selected
2855         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2856     } else { // no more nodes, select the last one in last subpath
2857         GList *spl = g_list_last(nodepath->subpaths);
2858        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2859         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2860     }
2863 /**
2864  * \brief Select all nodes that are within the rectangle.
2865  */
2866 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2868     if (!incremental) {
2869         sp_nodepath_deselect(nodepath);
2870     }
2872     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2873        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2874         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2875            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2877             if (b.contains(node->pos)) {
2878                 sp_nodepath_node_select(node, TRUE, TRUE);
2879             }
2880         }
2881     }
2885 void
2886 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2888     g_assert (n);
2889     g_assert (nodepath);
2890     g_assert (n->subpath->nodepath == nodepath);
2892     if (g_list_length (nodepath->selected) == 0) {
2893         if (grow > 0) {
2894             sp_nodepath_node_select(n, TRUE, TRUE);
2895         }
2896         return;
2897     }
2899     if (g_list_length (nodepath->selected) == 1) {
2900         if (grow < 0) {
2901             sp_nodepath_deselect (nodepath);
2902             return;
2903         }
2904     }
2906         double n_sel_range = 0, p_sel_range = 0;
2907             Inkscape::NodePath::Node *farthest_n_node = n;
2908             Inkscape::NodePath::Node *farthest_p_node = n;
2910         // Calculate ranges
2911         {
2912             double n_range = 0, p_range = 0;
2913             bool n_going = true, p_going = true;
2914             Inkscape::NodePath::Node *n_node = n;
2915             Inkscape::NodePath::Node *p_node = n;
2916             do {
2917                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2918                 if (n_node && n_going)
2919                     n_node = n_node->n.other;
2920                 if (n_node == NULL) {
2921                     n_going = false;
2922                 } else {
2923                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2924                     if (n_node->selected) {
2925                         n_sel_range = n_range;
2926                         farthest_n_node = n_node;
2927                     }
2928                     if (n_node == p_node) {
2929                         n_going = false;
2930                         p_going = false;
2931                     }
2932                 }
2933                 if (p_node && p_going)
2934                     p_node = p_node->p.other;
2935                 if (p_node == NULL) {
2936                     p_going = false;
2937                 } else {
2938                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2939                     if (p_node->selected) {
2940                         p_sel_range = p_range;
2941                         farthest_p_node = p_node;
2942                     }
2943                     if (p_node == n_node) {
2944                         n_going = false;
2945                         p_going = false;
2946                     }
2947                 }
2948             } while (n_going || p_going);
2949         }
2951     if (grow > 0) {
2952         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2953                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2954         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2955                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2956         }
2957     } else {
2958         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2959                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2960         } else if (farthest_p_node && farthest_p_node->selected) {
2961                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2962         }
2963     }
2966 void
2967 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2969     g_assert (n);
2970     g_assert (nodepath);
2971     g_assert (n->subpath->nodepath == nodepath);
2973     if (g_list_length (nodepath->selected) == 0) {
2974         if (grow > 0) {
2975             sp_nodepath_node_select(n, TRUE, TRUE);
2976         }
2977         return;
2978     }
2980     if (g_list_length (nodepath->selected) == 1) {
2981         if (grow < 0) {
2982             sp_nodepath_deselect (nodepath);
2983             return;
2984         }
2985     }
2987     Inkscape::NodePath::Node *farthest_selected = NULL;
2988     double farthest_dist = 0;
2990     Inkscape::NodePath::Node *closest_unselected = NULL;
2991     double closest_dist = NR_HUGE;
2993     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2994        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2995         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2996            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2997            if (node == n)
2998                continue;
2999            if (node->selected) {
3000                if (NR::L2(node->pos - n->pos) > farthest_dist) {
3001                    farthest_dist = NR::L2(node->pos - n->pos);
3002                    farthest_selected = node;
3003                }
3004            } else {
3005                if (NR::L2(node->pos - n->pos) < closest_dist) {
3006                    closest_dist = NR::L2(node->pos - n->pos);
3007                    closest_unselected = node;
3008                }
3009            }
3010         }
3011     }
3013     if (grow > 0) {
3014         if (closest_unselected) {
3015             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3016         }
3017     } else {
3018         if (farthest_selected) {
3019             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3020         }
3021     }
3025 /**
3026 \brief  Saves all nodes' and handles' current positions in their origin members
3027 */
3028 void
3029 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3031     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3032        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3033         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3034            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3035            n->origin = n->pos;
3036            n->p.origin = n->p.pos;
3037            n->n.origin = n->n.pos;
3038         }
3039     }
3042 /**
3043 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3044 */
3045 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3047     if (!nodepath->selected) {
3048         return NULL;
3049     }
3051     GList *r = NULL;
3052     guint i = 0;
3053     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3054        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3055         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3056            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3057             i++;
3058             if (node->selected) {
3059                 r = g_list_append(r, GINT_TO_POINTER(i));
3060             }
3061         }
3062     }
3063     return r;
3066 /**
3067 \brief  Restores selection by selecting nodes whose positions are in the list
3068 */
3069 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3071     sp_nodepath_deselect(nodepath);
3073     guint i = 0;
3074     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3075        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3076         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3077            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3078             i++;
3079             if (g_list_find(r, GINT_TO_POINTER(i))) {
3080                 sp_nodepath_node_select(node, TRUE, TRUE);
3081             }
3082         }
3083     }
3087 /**
3088 \brief Adjusts handle according to node type and line code.
3089 */
3090 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3092     g_assert(node);
3094    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3095    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3097    // nothing to do if we are an end node
3098     if (me->other == NULL) return;
3099     if (other->other == NULL) return;
3101     // nothing to do if we are a cusp node
3102     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3104     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3105     NRPathcode mecode;
3106     if (which_adjust == 1) {
3107         mecode = (NRPathcode)me->other->code;
3108     } else {
3109         mecode = (NRPathcode)node->code;
3110     }
3111     if (mecode == NR_LINETO) return;
3113     if (sp_node_side_is_line(node, other)) {
3114         // other is a line, and we are either smooth or symm
3115        Inkscape::NodePath::Node *othernode = other->other;
3116         double len = NR::L2(me->pos - node->pos);
3117         NR::Point delta = node->pos - othernode->pos;
3118         double linelen = NR::L2(delta);
3119         if (linelen < 1e-18)
3120             return;
3121         me->pos = node->pos + (len / linelen)*delta;
3122         return;
3123     }
3125     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3126         // symmetrize 
3127         me->pos = 2 * node->pos - other->pos;
3128         return;
3129     } else {
3130         // smoothify
3131         double len = NR::L2(me->pos - node->pos);
3132         NR::Point delta = other->pos - node->pos;
3133         double otherlen = NR::L2(delta);
3134         if (otherlen < 1e-18) return;
3135         me->pos = node->pos - (len / otherlen) * delta;
3136     }
3139 /**
3140  \brief Adjusts both handles according to node type and line code
3141  */
3142 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3144     g_assert(node);
3146     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3148     /* we are either smooth or symm */
3150     if (node->p.other == NULL) return;
3151     if (node->n.other == NULL) return;
3153     if (sp_node_side_is_line(node, &node->p)) {
3154         sp_node_adjust_handle(node, 1);
3155         return;
3156     }
3158     if (sp_node_side_is_line(node, &node->n)) {
3159         sp_node_adjust_handle(node, -1);
3160         return;
3161     }
3163     /* both are curves */
3164     NR::Point const delta( node->n.pos - node->p.pos );
3166     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3167         node->p.pos = node->pos - delta / 2;
3168         node->n.pos = node->pos + delta / 2;
3169         return;
3170     }
3172     /* We are smooth */
3173     double plen = NR::L2(node->p.pos - node->pos);
3174     if (plen < 1e-18) return;
3175     double nlen = NR::L2(node->n.pos - node->pos);
3176     if (nlen < 1e-18) return;
3177     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3178     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3181 /**
3182  * Node event callback.
3183  */
3184 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3186     gboolean ret = FALSE;
3187     switch (event->type) {
3188         case GDK_ENTER_NOTIFY:
3189             Inkscape::NodePath::Path::active_node = n;
3190             break;
3191         case GDK_LEAVE_NOTIFY:
3192             Inkscape::NodePath::Path::active_node = NULL;
3193             break;
3194         case GDK_SCROLL:
3195             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3196                 switch (event->scroll.direction) {
3197                     case GDK_SCROLL_UP:
3198                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3199                         break;
3200                     case GDK_SCROLL_DOWN:
3201                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3202                         break;
3203                     default:
3204                         break;
3205                 }
3206                 ret = TRUE;
3207             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3208                 switch (event->scroll.direction) {
3209                     case GDK_SCROLL_UP:
3210                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3211                         break;
3212                     case GDK_SCROLL_DOWN:
3213                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3214                         break;
3215                     default:
3216                         break;
3217                 }
3218                 ret = TRUE;
3219             }
3220             break;
3221         case GDK_KEY_PRESS:
3222             switch (get_group0_keyval (&event->key)) {
3223                 case GDK_space:
3224                     if (event->key.state & GDK_BUTTON1_MASK) {
3225                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3226                         stamp_repr(nodepath);
3227                         ret = TRUE;
3228                     }
3229                     break;
3230                 case GDK_Page_Up:
3231                     if (event->key.state & GDK_CONTROL_MASK) {
3232                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3233                     } else {
3234                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3235                     }
3236                     break;
3237                 case GDK_Page_Down:
3238                     if (event->key.state & GDK_CONTROL_MASK) {
3239                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3240                     } else {
3241                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3242                     }
3243                     break;
3244                 default:
3245                     break;
3246             }
3247             break;
3248         default:
3249             break;
3250     }
3252     return ret;
3255 /**
3256  * Handle keypress on node; directly called.
3257  */
3258 gboolean node_key(GdkEvent *event)
3260     Inkscape::NodePath::Path *np;
3262     // there is no way to verify nodes so set active_node to nil when deleting!!
3263     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3265     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3266         gint ret = FALSE;
3267         switch (get_group0_keyval (&event->key)) {
3268             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3269             case GDK_BackSpace:
3270                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3271                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3272                 sp_nodepath_update_repr(np, _("Delete node"));
3273                 Inkscape::NodePath::Path::active_node = NULL;
3274                 ret = TRUE;
3275                 break;
3276             case GDK_c:
3277                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3278                 ret = TRUE;
3279                 break;
3280             case GDK_s:
3281                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3282                 ret = TRUE;
3283                 break;
3284             case GDK_y:
3285                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3286                 ret = TRUE;
3287                 break;
3288             case GDK_b:
3289                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3290                 ret = TRUE;
3291                 break;
3292         }
3293         return ret;
3294     }
3295     return FALSE;
3298 /**
3299  * Mouseclick on node callback.
3300  */
3301 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3303    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3305     if (state & GDK_CONTROL_MASK) {
3306         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3308         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3309             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3310                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3311             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3312                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3313             } else {
3314                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3315             }
3316             sp_nodepath_update_repr(nodepath, _("Change node type"));
3317             sp_nodepath_update_statusbar(nodepath);
3319         } else { //ctrl+alt+click: delete node
3320             GList *node_to_delete = NULL;
3321             node_to_delete = g_list_append(node_to_delete, n);
3322             sp_node_delete_preserve(node_to_delete);
3323         }
3325     } else {
3326         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3327     }
3330 /**
3331  * Mouse grabbed node callback.
3332  */
3333 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3335    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3337     if (!n->selected) {
3338         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3339     }
3341     n->is_dragging = true;
3342     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3344     sp_nodepath_remember_origins (n->subpath->nodepath);
3347 /**
3348  * Mouse ungrabbed node callback.
3349  */
3350 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3352    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3354    n->dragging_out = NULL;
3355    n->is_dragging = false;
3356    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3358    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3361 /**
3362  * The point on a line, given by its angle, closest to the given point.
3363  * \param p  A point.
3364  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3365  * \param closest  Pointer to the point struct where the result is stored.
3366  * \todo FIXME: use dot product perhaps?
3367  */
3368 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3370     if (a == HUGE_VAL) { // vertical
3371         *closest = NR::Point(0, (*p)[NR::Y]);
3372     } else {
3373         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3374         (*closest)[NR::Y] = a * (*closest)[NR::X];
3375     }
3378 /**
3379  * Distance from the point to a line given by its angle.
3380  * \param p  A point.
3381  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3382  */
3383 static double point_line_distance(NR::Point *p, double a)
3385     NR::Point c;
3386     point_line_closest(p, a, &c);
3387     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]));
3390 /**
3391  * Callback for node "request" signal.
3392  * \todo fixme: This goes to "moved" event? (lauris)
3393  */
3394 static gboolean
3395 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3397     double yn, xn, yp, xp;
3398     double an, ap, na, pa;
3399     double d_an, d_ap, d_na, d_pa;
3400     gboolean collinear = FALSE;
3401     NR::Point c;
3402     NR::Point pr;
3404     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3406     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3408     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3409     if ( (!n->subpath->nodepath->straight_path) &&
3410          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3411            || n->dragging_out ) )
3412     {
3413        NR::Point mouse = (*p);
3415        if (!n->dragging_out) {
3416            // This is the first drag-out event; find out which handle to drag out
3417            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3418            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3420            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3421                return FALSE;
3423            Inkscape::NodePath::NodeSide *opposite;
3424            if (appr_p > appr_n) { // closer to p
3425                n->dragging_out = &n->p;
3426                opposite = &n->n;
3427                n->code = NR_CURVETO;
3428            } else if (appr_p < appr_n) { // closer to n
3429                n->dragging_out = &n->n;
3430                opposite = &n->p;
3431                n->n.other->code = NR_CURVETO;
3432            } else { // p and n nodes are the same
3433                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3434                    n->dragging_out = &n->p;
3435                    opposite = &n->n;
3436                    n->code = NR_CURVETO;
3437                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3438                    n->dragging_out = &n->n;
3439                    opposite = &n->p;
3440                    n->n.other->code = NR_CURVETO;
3441                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3442                    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);
3443                    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);
3444                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3445                        n->dragging_out = &n->n;
3446                        opposite = &n->p;
3447                        n->n.other->code = NR_CURVETO;
3448                    } else { // closer to other's n handle
3449                        n->dragging_out = &n->p;
3450                        opposite = &n->n;
3451                        n->code = NR_CURVETO;
3452                    }
3453                }
3454            }
3456            // if there's another handle, make sure the one we drag out starts parallel to it
3457            if (opposite->pos != n->pos) {
3458                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3459            }
3461            // knots might not be created yet!
3462            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3463            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3464        }
3466        // pass this on to the handle-moved callback
3467        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3468        sp_node_update_handles(n);
3469        return TRUE;
3470    }
3472     if (state & GDK_CONTROL_MASK) { // constrained motion
3474         // calculate relative distances of handles
3475         // n handle:
3476         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3477         xn = n->n.pos[NR::X] - n->pos[NR::X];
3478         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3479         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3480             if (n->n.other) { // if there is the next point
3481                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3482                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3483                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3484             }
3485         }
3486         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3487         if (yn < 0) { xn = -xn; yn = -yn; }
3489         // p handle:
3490         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3491         xp = n->p.pos[NR::X] - n->pos[NR::X];
3492         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3493         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3494             if (n->p.other) {
3495                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3496                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3497                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3498             }
3499         }
3500         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3501         if (yp < 0) { xp = -xp; yp = -yp; }
3503         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3504             // sliding on handles, only if at least one of the handles is non-vertical
3505             // (otherwise it's the same as ctrl+drag anyway)
3507             // calculate angles of the handles
3508             if (xn == 0) {
3509                 if (yn == 0) { // no handle, consider it the continuation of the other one
3510                     an = 0;
3511                     collinear = TRUE;
3512                 }
3513                 else an = 0; // vertical; set the angle to horizontal
3514             } else an = yn/xn;
3516             if (xp == 0) {
3517                 if (yp == 0) { // no handle, consider it the continuation of the other one
3518                     ap = an;
3519                 }
3520                 else ap = 0; // vertical; set the angle to horizontal
3521             } else  ap = yp/xp;
3523             if (collinear) an = ap;
3525             // angles of the perpendiculars; HUGE_VAL means vertical
3526             if (an == 0) na = HUGE_VAL; else na = -1/an;
3527             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3529             // mouse point relative to the node's original pos
3530             pr = (*p) - n->origin;
3532             // distances to the four lines (two handles and two perpendiculars)
3533             d_an = point_line_distance(&pr, an);
3534             d_na = point_line_distance(&pr, na);
3535             d_ap = point_line_distance(&pr, ap);
3536             d_pa = point_line_distance(&pr, pa);
3538             // find out which line is the closest, save its closest point in c
3539             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3540                 point_line_closest(&pr, an, &c);
3541             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3542                 point_line_closest(&pr, ap, &c);
3543             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3544                 point_line_closest(&pr, na, &c);
3545             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3546                 point_line_closest(&pr, pa, &c);
3547             }
3549             // move the node to the closest point
3550             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3551                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3552                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y], 
3553                                             true);
3555         } else {  // constraining to hor/vert
3557             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3558                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3559                                                 (*p)[NR::X] - n->pos[NR::X], 
3560                                                 n->origin[NR::Y] - n->pos[NR::Y],
3561                                                 true, 
3562                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::X]));
3563             } else { // snap to vert
3564                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3565                                                 n->origin[NR::X] - n->pos[NR::X],
3566                                                 (*p)[NR::Y] - n->pos[NR::Y],
3567                                                 true,
3568                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::Y]));
3569             }
3570         }
3571     } else { // move freely
3572         if (n->is_dragging) {
3573             if (state & GDK_MOD1_MASK) { // sculpt
3574                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3575             } else {
3576                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3577                                             (*p)[NR::X] - n->pos[NR::X],
3578                                             (*p)[NR::Y] - n->pos[NR::Y],
3579                                             (state & GDK_SHIFT_MASK) == 0);
3580             }
3581         }
3582     }
3584     n->subpath->nodepath->desktop->scroll_to_point(p);
3586     return TRUE;
3589 /**
3590  * Node handle clicked callback.
3591  */
3592 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3594    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3596     if (state & GDK_CONTROL_MASK) { // "delete" handle
3597         if (n->p.knot == knot) {
3598             n->p.pos = n->pos;
3599         } else if (n->n.knot == knot) {
3600             n->n.pos = n->pos;
3601         }
3602         sp_node_update_handles(n);
3603         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3604         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3605         sp_nodepath_update_statusbar(nodepath);
3607     } else { // just select or add to selection, depending in Shift
3608         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3609     }
3612 /**
3613  * Node handle grabbed callback.
3614  */
3615 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3617    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3619     if (!n->selected) {
3620         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3621     }
3623     // remember the origin point of the handle
3624     if (n->p.knot == knot) {
3625         n->p.origin_radial = n->p.pos - n->pos;
3626     } else if (n->n.knot == knot) {
3627         n->n.origin_radial = n->n.pos - n->pos;
3628     } else {
3629         g_assert_not_reached();
3630     }
3632     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3635 /**
3636  * Node handle ungrabbed callback.
3637  */
3638 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3640    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3642     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3643     if (n->p.knot == knot) {
3644         n->p.origin_radial.a = 0;
3645         sp_knot_set_position(knot, &n->p.pos, state);
3646     } else if (n->n.knot == knot) {
3647         n->n.origin_radial.a = 0;
3648         sp_knot_set_position(knot, &n->n.pos, state);
3649     } else {
3650         g_assert_not_reached();
3651     }
3653     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3656 /**
3657  * Node handle "request" signal callback.
3658  */
3659 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint /*state*/, gpointer data)
3661     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3663     Inkscape::NodePath::NodeSide *me, *opposite;
3664     gint which;
3665     if (n->p.knot == knot) {
3666         me = &n->p;
3667         opposite = &n->n;
3668         which = -1;
3669     } else if (n->n.knot == knot) {
3670         me = &n->n;
3671         opposite = &n->p;
3672         which = 1;
3673     } else {
3674         me = opposite = NULL;
3675         which = 0;
3676         g_assert_not_reached();
3677     }
3679     SPDesktop *desktop = n->subpath->nodepath->desktop;
3680     SnapManager &m = desktop->namedview->snap_manager;
3681     m.setup(desktop, n->subpath->nodepath->item);
3682     Inkscape::SnappedPoint s ;
3684     Inkscape::NodePath::Node *othernode = opposite->other;
3685     if (othernode) {
3686         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3687             /* We are smooth node adjacent with line */
3688             NR::Point const delta = *p - n->pos;
3689             NR::Coord const len = NR::L2(delta);
3690             Inkscape::NodePath::Node *othernode = opposite->other;
3691             NR::Point const ndelta = n->pos - othernode->pos;
3692             NR::Coord const linelen = NR::L2(ndelta);
3693             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3694                 NR::Coord const scal = dot(delta, ndelta) / linelen;
3695                 (*p) = n->pos + (scal / linelen) * ndelta;
3696             }
3697             s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta));
3698         } else {
3699             s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3700         }
3701     } else {
3702         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3703     }
3704     
3705     s.getPoint(*p);
3706     
3707     sp_node_adjust_handle(n, -which);
3709     return FALSE;
3712 /**
3713  * Node handle moved callback.
3714  */
3715 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3717    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3719    Inkscape::NodePath::NodeSide *me;
3720    Inkscape::NodePath::NodeSide *other;
3721     if (n->p.knot == knot) {
3722         me = &n->p;
3723         other = &n->n;
3724     } else if (n->n.knot == knot) {
3725         me = &n->n;
3726         other = &n->p;
3727     } else {
3728         me = NULL;
3729         other = NULL;
3730         g_assert_not_reached();
3731     }
3733     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3734     Radial rme(me->pos - n->pos);
3735     Radial rother(other->pos - n->pos);
3736     Radial rnew(*p - n->pos);
3738     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3739         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3740         /* 0 interpreted as "no snapping". */
3742         // 1. Snap to the closest PI/snaps angle, starting from zero.
3743         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3745         // 2. Snap to the original angle, its opposite and perpendiculars
3746         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3747             /* The closest PI/2 angle, starting from original angle */
3748             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3750             // Snap to the closest.
3751             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3752                        ? a_snapped
3753                        : a_ortho );
3754         }
3756         // 3. Snap to the angle of the opposite line, if any
3757         Inkscape::NodePath::Node *othernode = other->other;
3758         if (othernode) {
3759             NR::Point other_to_snap(0,0);
3760             if (sp_node_side_is_line(n, other)) {
3761                 other_to_snap = othernode->pos - n->pos;
3762             } else {
3763                 other_to_snap = other->pos - n->pos;
3764             }
3765             if (NR::L2(other_to_snap) > 1e-3) {
3766                 Radial rother_to_snap(other_to_snap);
3767                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
3768                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
3770                 // Snap to the closest.
3771                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
3772                        ? a_snapped
3773                        : a_oppo );
3774             }
3775         }
3777         rnew.a = a_snapped;
3778     }
3780     if (state & GDK_MOD1_MASK) {
3781         // lock handle length
3782         rnew.r = me->origin_radial.r;
3783     }
3785     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3786         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3787         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3788         rother.a += rnew.a - rme.a;
3789         other->pos = NR::Point(rother) + n->pos;
3790         if (other->knot) {
3791             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3792             sp_knot_moveto(other->knot, &other->pos);
3793         }
3794     }
3796     me->pos = NR::Point(rnew) + n->pos;
3797     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3799     // move knot, but without emitting the signal:
3800     // we cannot emit a "moved" signal because we're now processing it
3801     sp_knot_moveto(me->knot, &(me->pos));
3803     update_object(n->subpath->nodepath);
3805     /* status text */
3806     SPDesktop *desktop = n->subpath->nodepath->desktop;
3807     if (!desktop) return;
3808     SPEventContext *ec = desktop->event_context;
3809     if (!ec) return;
3810     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3811     if (!mc) return;
3813     double degrees = 180 / M_PI * rnew.a;
3814     if (degrees > 180) degrees -= 360;
3815     if (degrees < -180) degrees += 360;
3816     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3817         degrees = angle_to_compass (degrees);
3819     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3821     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
3822          _("<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);
3824     g_string_free(length, TRUE);
3827 /**
3828  * Node handle event callback.
3829  */
3830 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3832     gboolean ret = FALSE;
3833     switch (event->type) {
3834         case GDK_KEY_PRESS:
3835             switch (get_group0_keyval (&event->key)) {
3836                 case GDK_space:
3837                     if (event->key.state & GDK_BUTTON1_MASK) {
3838                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3839                         stamp_repr(nodepath);
3840                         ret = TRUE;
3841                     }
3842                     break;
3843                 default:
3844                     break;
3845             }
3846             break;
3847         case GDK_ENTER_NOTIFY:
3848             // we use an experimentally determined threshold that seems to work fine
3849             if (NR::L2(n->pos - knot->pos) < 0.75)
3850                 Inkscape::NodePath::Path::active_node = n;
3851             break;
3852         case GDK_LEAVE_NOTIFY:
3853             // we use an experimentally determined threshold that seems to work fine
3854             if (NR::L2(n->pos - knot->pos) < 0.75)
3855                 Inkscape::NodePath::Path::active_node = NULL;
3856             break;
3857         default:
3858             break;
3859     }
3861     return ret;
3864 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3865                                  Radial &rme, Radial &rother, gboolean const both)
3867     rme.a += angle;
3868     if ( both
3869          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3870          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3871     {
3872         rother.a += angle;
3873     }
3876 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3877                                         Radial &rme, Radial &rother, gboolean const both)
3879     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3881     gdouble r;
3882     if ( both
3883          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3884          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3885     {
3886         r = MAX(rme.r, rother.r);
3887     } else {
3888         r = rme.r;
3889     }
3891     gdouble const weird_angle = atan2(norm_angle, r);
3892 /* Bulia says norm_angle is just the visible distance that the
3893  * object's end must travel on the screen.  Left as 'angle' for want of
3894  * a better name.*/
3896     rme.a += weird_angle;
3897     if ( both
3898          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3899          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3900     {
3901         rother.a += weird_angle;
3902     }
3905 /**
3906  * Rotate one node.
3907  */
3908 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3910     Inkscape::NodePath::NodeSide *me, *other;
3911     bool both = false;
3913     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3914     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3916     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3917         me = &(n->p);
3918         other = &(n->n);
3919     } else if (!n->p.other) {
3920         me = &(n->n);
3921         other = &(n->p);
3922     } else {
3923         if (which > 0) { // right handle
3924             if (xn > xp) {
3925                 me = &(n->n);
3926                 other = &(n->p);
3927             } else {
3928                 me = &(n->p);
3929                 other = &(n->n);
3930             }
3931         } else if (which < 0){ // left handle
3932             if (xn <= xp) {
3933                 me = &(n->n);
3934                 other = &(n->p);
3935             } else {
3936                 me = &(n->p);
3937                 other = &(n->n);
3938             }
3939         } else { // both handles
3940             me = &(n->n);
3941             other = &(n->p);
3942             both = true;
3943         }
3944     }
3946     Radial rme(me->pos - n->pos);
3947     Radial rother(other->pos - n->pos);
3949     if (screen) {
3950         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3951     } else {
3952         node_rotate_one_internal (*n, angle, rme, rother, both);
3953     }
3955     me->pos = n->pos + NR::Point(rme);
3957     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3958         other->pos =  n->pos + NR::Point(rother);
3959     }
3961     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3962     // so here we just move all the knots without emitting move signals, for speed
3963     sp_node_update_handles(n, false);
3966 /**
3967  * Rotate selected nodes.
3968  */
3969 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3971     if (!nodepath || !nodepath->selected) return;
3973     if (g_list_length(nodepath->selected) == 1) {
3974        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3975         node_rotate_one (n, angle, which, screen);
3976     } else {
3977        // rotate as an object:
3979         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3980         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3981         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3982             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3983             box.expandTo (n->pos); // contain all selected nodes
3984         }
3986         gdouble rot;
3987         if (screen) {
3988             gdouble const zoom = nodepath->desktop->current_zoom();
3989             gdouble const zmove = angle / zoom;
3990             gdouble const r = NR::L2(box.max() - box.midpoint());
3991             rot = atan2(zmove, r);
3992         } else {
3993             rot = angle;
3994         }
3996         NR::Point rot_center;
3997         if (Inkscape::NodePath::Path::active_node == NULL)
3998             rot_center = box.midpoint();
3999         else
4000             rot_center = Inkscape::NodePath::Path::active_node->pos;
4002         NR::Matrix t =
4003             NR::Matrix (NR::translate(-rot_center)) *
4004             NR::Matrix (NR::rotate(rot)) *
4005             NR::Matrix (NR::translate(rot_center));
4007         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4008             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4009             n->pos *= t;
4010             n->n.pos *= t;
4011             n->p.pos *= t;
4012             sp_node_update_handles(n, false);
4013         }
4014     }
4016     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4019 /**
4020  * Scale one node.
4021  */
4022 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4024     bool both = false;
4025     Inkscape::NodePath::NodeSide *me, *other;
4027     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4028     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4030     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4031         me = &(n->p);
4032         other = &(n->n);
4033         n->code = NR_CURVETO;
4034     } else if (!n->p.other) {
4035         me = &(n->n);
4036         other = &(n->p);
4037         if (n->n.other)
4038             n->n.other->code = NR_CURVETO;
4039     } else {
4040         if (which > 0) { // right handle
4041             if (xn > xp) {
4042                 me = &(n->n);
4043                 other = &(n->p);
4044                 if (n->n.other)
4045                     n->n.other->code = NR_CURVETO;
4046             } else {
4047                 me = &(n->p);
4048                 other = &(n->n);
4049                 n->code = NR_CURVETO;
4050             }
4051         } else if (which < 0){ // left handle
4052             if (xn <= xp) {
4053                 me = &(n->n);
4054                 other = &(n->p);
4055                 if (n->n.other)
4056                     n->n.other->code = NR_CURVETO;
4057             } else {
4058                 me = &(n->p);
4059                 other = &(n->n);
4060                 n->code = NR_CURVETO;
4061             }
4062         } else { // both handles
4063             me = &(n->n);
4064             other = &(n->p);
4065             both = true;
4066             n->code = NR_CURVETO;
4067             if (n->n.other)
4068                 n->n.other->code = NR_CURVETO;
4069         }
4070     }
4072     Radial rme(me->pos - n->pos);
4073     Radial rother(other->pos - n->pos);
4075     rme.r += grow;
4076     if (rme.r < 0) rme.r = 0;
4077     if (rme.a == HUGE_VAL) {
4078         if (me->other) { // if direction is unknown, initialize it towards the next node
4079             Radial rme_next(me->other->pos - n->pos);
4080             rme.a = rme_next.a;
4081         } else { // if there's no next, initialize to 0
4082             rme.a = 0;
4083         }
4084     }
4085     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4086         rother.r += grow;
4087         if (rother.r < 0) rother.r = 0;
4088         if (rother.a == HUGE_VAL) {
4089             rother.a = rme.a + M_PI;
4090         }
4091     }
4093     me->pos = n->pos + NR::Point(rme);
4095     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4096         other->pos = n->pos + NR::Point(rother);
4097     }
4099     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4100     // so here we just move all the knots without emitting move signals, for speed
4101     sp_node_update_handles(n, false);
4104 /**
4105  * Scale selected nodes.
4106  */
4107 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4109     if (!nodepath || !nodepath->selected) return;
4111     if (g_list_length(nodepath->selected) == 1) {
4112         // scale handles of the single selected node
4113         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4114         node_scale_one (n, grow, which);
4115     } else {
4116         // scale nodes as an "object":
4118         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4119         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4120         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4121             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4122             box.expandTo (n->pos); // contain all selected nodes
4123         }
4125         double scale = (box.maxExtent() + grow)/box.maxExtent();
4127         NR::Point scale_center;
4128         if (Inkscape::NodePath::Path::active_node == NULL)
4129             scale_center = box.midpoint();
4130         else
4131             scale_center = Inkscape::NodePath::Path::active_node->pos;
4133         NR::Matrix t =
4134             NR::Matrix (NR::translate(-scale_center)) *
4135             NR::Matrix (NR::scale(scale, scale)) *
4136             NR::Matrix (NR::translate(scale_center));
4138         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4139             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4140             n->pos *= t;
4141             n->n.pos *= t;
4142             n->p.pos *= t;
4143             sp_node_update_handles(n, false);
4144         }
4145     }
4147     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4150 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4152     if (!nodepath) return;
4153     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4156 /**
4157  * Flip selected nodes horizontally/vertically.
4158  */
4159 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
4161     if (!nodepath || !nodepath->selected) return;
4163     if (g_list_length(nodepath->selected) == 1 && !center) {
4164         // flip handles of the single selected node
4165         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4166         double temp = n->p.pos[axis];
4167         n->p.pos[axis] = n->n.pos[axis];
4168         n->n.pos[axis] = temp;
4169         sp_node_update_handles(n, false);
4170     } else {
4171         // scale nodes as an "object":
4173         NR::Rect box = sp_node_selected_bbox (nodepath);
4174         if (!center) {
4175             center = box.midpoint();
4176         }
4177         NR::Matrix t =
4178             NR::Matrix (NR::translate(- *center)) *
4179             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
4180             NR::Matrix (NR::translate(*center));
4182         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4183             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4184             n->pos *= t;
4185             n->n.pos *= t;
4186             n->p.pos *= t;
4187             sp_node_update_handles(n, false);
4188         }
4189     }
4191     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4194 NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4196     g_assert (nodepath->selected);
4198     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4199     NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4200     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4201         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4202         box.expandTo (n->pos); // contain all selected nodes
4203     }
4204     return box;
4207 //-----------------------------------------------
4208 /**
4209  * Return new subpath under given nodepath.
4210  */
4211 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4213     g_assert(nodepath);
4214     g_assert(nodepath->desktop);
4216    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4218     s->nodepath = nodepath;
4219     s->closed = FALSE;
4220     s->nodes = NULL;
4221     s->first = NULL;
4222     s->last = NULL;
4224     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4225     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4226     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4228     return s;
4231 /**
4232  * Destroy nodes in subpath, then subpath itself.
4233  */
4234 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4236     g_assert(subpath);
4237     g_assert(subpath->nodepath);
4238     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4240     while (subpath->nodes) {
4241         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4242     }
4244     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4246     g_free(subpath);
4249 /**
4250  * Link head to tail in subpath.
4251  */
4252 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4254     g_assert(!sp->closed);
4255     g_assert(sp->last != sp->first);
4256     g_assert(sp->first->code == NR_MOVETO);
4258     sp->closed = TRUE;
4260     //Link the head to the tail
4261     sp->first->p.other = sp->last;
4262     sp->last->n.other  = sp->first;
4263     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4264     sp->first          = sp->last;
4266     //Remove the extra end node
4267     sp_nodepath_node_destroy(sp->last->n.other);
4270 /**
4271  * Open closed (loopy) subpath at node.
4272  */
4273 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4275     g_assert(sp->closed);
4276     g_assert(n->subpath == sp);
4277     g_assert(sp->first == sp->last);
4279     /* We create new startpoint, current node will become last one */
4281    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4282                                                 &n->pos, &n->pos, &n->n.pos);
4285     sp->closed        = FALSE;
4287     //Unlink to make a head and tail
4288     sp->first         = new_path;
4289     sp->last          = n;
4290     n->n.other        = NULL;
4291     new_path->p.other = NULL;
4294 /**
4295  * Return new node in subpath with given properties.
4296  * \param pos Position of node.
4297  * \param ppos Handle position in previous direction
4298  * \param npos Handle position in previous direction
4299  */
4300 Inkscape::NodePath::Node *
4301 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)
4303     g_assert(sp);
4304     g_assert(sp->nodepath);
4305     g_assert(sp->nodepath->desktop);
4307     if (nodechunk == NULL)
4308         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4310     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4312     n->subpath  = sp;
4314     if (type != Inkscape::NodePath::NODE_NONE) {
4315         // use the type from sodipodi:nodetypes
4316         n->type = type;
4317     } else {
4318         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4319             // points are (almost) collinear
4320             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4321                 // endnode, or a node with a retracted handle
4322                 n->type = Inkscape::NodePath::NODE_CUSP;
4323             } else {
4324                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4325             }
4326         } else {
4327             n->type = Inkscape::NodePath::NODE_CUSP;
4328         }
4329     }
4331     n->code     = code;
4332     n->selected = FALSE;
4333     n->pos      = *pos;
4334     n->p.pos    = *ppos;
4335     n->n.pos    = *npos;
4337     n->dragging_out = NULL;
4339     Inkscape::NodePath::Node *prev;
4340     if (next) {
4341         //g_assert(g_list_find(sp->nodes, next));
4342         prev = next->p.other;
4343     } else {
4344         prev = sp->last;
4345     }
4347     if (prev)
4348         prev->n.other = n;
4349     else
4350         sp->first = n;
4352     if (next)
4353         next->p.other = n;
4354     else
4355         sp->last = n;
4357     n->p.other = prev;
4358     n->n.other = next;
4360     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"));
4361     sp_knot_set_position(n->knot, pos, 0);
4363     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4364     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4365     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4366     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4367     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4368     sp_knot_update_ctrl(n->knot);
4370     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4371     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4372     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4373     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4374     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4375     sp_knot_show(n->knot);
4377     // We only create handle knots and lines on demand
4378     n->p.knot = NULL;
4379     n->p.line = NULL;
4380     n->n.knot = NULL;
4381     n->n.line = NULL;
4383     sp->nodes = g_list_prepend(sp->nodes, n);
4385     return n;
4388 /**
4389  * Destroy node and its knots, link neighbors in subpath.
4390  */
4391 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4393     g_assert(node);
4394     g_assert(node->subpath);
4395     g_assert(SP_IS_KNOT(node->knot));
4397    Inkscape::NodePath::SubPath *sp = node->subpath;
4399     if (node->selected) { // first, deselect
4400         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4401         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4402     }
4404     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4406     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4407     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4408     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4409     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4410     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4411     g_object_unref(G_OBJECT(node->knot));
4413     if (node->p.knot) {
4414         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4415         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4416         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4417         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4418         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4419         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4420         g_object_unref(G_OBJECT(node->p.knot));
4421         node->p.knot = NULL;
4422     }
4424     if (node->n.knot) {
4425         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4426         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4427         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4428         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4429         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4430         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4431         g_object_unref(G_OBJECT(node->n.knot));
4432         node->n.knot = NULL;
4433     }
4435     if (node->p.line)
4436         gtk_object_destroy(GTK_OBJECT(node->p.line));
4437     if (node->n.line)
4438         gtk_object_destroy(GTK_OBJECT(node->n.line));
4440     if (sp->nodes) { // there are others nodes on the subpath
4441         if (sp->closed) {
4442             if (sp->first == node) {
4443                 g_assert(sp->last == node);
4444                 sp->first = node->n.other;
4445                 sp->last = sp->first;
4446             }
4447             node->p.other->n.other = node->n.other;
4448             node->n.other->p.other = node->p.other;
4449         } else {
4450             if (sp->first == node) {
4451                 sp->first = node->n.other;
4452                 sp->first->code = NR_MOVETO;
4453             }
4454             if (sp->last == node) sp->last = node->p.other;
4455             if (node->p.other) node->p.other->n.other = node->n.other;
4456             if (node->n.other) node->n.other->p.other = node->p.other;
4457         }
4458     } else { // this was the last node on subpath
4459         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4460     }
4462     g_mem_chunk_free(nodechunk, node);
4465 /**
4466  * Returns one of the node's two sides.
4467  * \param which Indicates which side.
4468  * \return Pointer to previous node side if which==-1, next if which==1.
4469  */
4470 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4472     g_assert(node);
4474     switch (which) {
4475         case -1:
4476             return &node->p;
4477         case 1:
4478             return &node->n;
4479         default:
4480             break;
4481     }
4483     g_assert_not_reached();
4485     return NULL;
4488 /**
4489  * Return the other side of the node, given one of its sides.
4490  */
4491 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4493     g_assert(node);
4495     if (me == &node->p) return &node->n;
4496     if (me == &node->n) return &node->p;
4498     g_assert_not_reached();
4500     return NULL;
4503 /**
4504  * Return NRPathcode on the given side of the node.
4505  */
4506 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4508     g_assert(node);
4510     if (me == &node->p) {
4511         if (node->p.other) return (NRPathcode)node->code;
4512         return NR_MOVETO;
4513     }
4515     if (me == &node->n) {
4516         if (node->n.other) return (NRPathcode)node->n.other->code;
4517         return NR_MOVETO;
4518     }
4520     g_assert_not_reached();
4522     return NR_END;
4525 /**
4526  * Return node with the given index
4527  */
4528 Inkscape::NodePath::Node *
4529 sp_nodepath_get_node_by_index(int index)
4531     Inkscape::NodePath::Node *e = NULL;
4533     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4534     if (!nodepath) {
4535         return e;
4536     }
4538     //find segment
4539     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4541         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4542         int n = g_list_length(sp->nodes);
4543         if (sp->closed) {
4544             n++;
4545         }
4547         //if the piece belongs to this subpath grab it
4548         //otherwise move onto the next subpath
4549         if (index < n) {
4550             e = sp->first;
4551             for (int i = 0; i < index; ++i) {
4552                 e = e->n.other;
4553             }
4554             break;
4555         } else {
4556             if (sp->closed) {
4557                 index -= (n+1);
4558             } else {
4559                 index -= n;
4560             }
4561         }
4562     }
4564     return e;
4567 /**
4568  * Returns plain text meaning of node type.
4569  */
4570 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4572     unsigned retracted = 0;
4573     bool endnode = false;
4575     for (int which = -1; which <= 1; which += 2) {
4576         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4577         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4578             retracted ++;
4579         if (!side->other)
4580             endnode = true;
4581     }
4583     if (retracted == 0) {
4584         if (endnode) {
4585                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4586                 return _("end node");
4587         } else {
4588             switch (node->type) {
4589                 case Inkscape::NodePath::NODE_CUSP:
4590                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4591                     return _("cusp");
4592                 case Inkscape::NodePath::NODE_SMOOTH:
4593                     // TRANSLATORS: "smooth" is an adjective here
4594                     return _("smooth");
4595                 case Inkscape::NodePath::NODE_SYMM:
4596                     return _("symmetric");
4597             }
4598         }
4599     } else if (retracted == 1) {
4600         if (endnode) {
4601             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4602             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4603         } else {
4604             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4605         }
4606     } else {
4607         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4608     }
4610     return NULL;
4613 /**
4614  * Handles content of statusbar as long as node tool is active.
4615  */
4616 void
4617 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4619     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");
4620     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4622     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4623     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4624     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4625     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4627     SPDesktop *desktop = NULL;
4628     if (nodepath) {
4629         desktop = nodepath->desktop;
4630     } else {
4631         desktop = SP_ACTIVE_DESKTOP;
4632     }
4634     SPEventContext *ec = desktop->event_context;
4635     if (!ec) return;
4636     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4637     if (!mc) return;
4639     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4641     if (selected_nodes == 0) {
4642         Inkscape::Selection *sel = desktop->selection;
4643         if (!sel || sel->isEmpty()) {
4644             mc->setF(Inkscape::NORMAL_MESSAGE,
4645                      _("Select a single object to edit its nodes or handles."));
4646         } else {
4647             if (nodepath) {
4648             mc->setF(Inkscape::NORMAL_MESSAGE,
4649                      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.",
4650                               "<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.",
4651                               total_nodes),
4652                      total_nodes);
4653             } else {
4654                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4655                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4656                 } else {
4657                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4658                 }
4659             }
4660         }
4661     } else if (nodepath && selected_nodes == 1) {
4662         mc->setF(Inkscape::NORMAL_MESSAGE,
4663                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4664                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4665                           total_nodes),
4666                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4667     } else {
4668         if (selected_subpaths > 1) {
4669             mc->setF(Inkscape::NORMAL_MESSAGE,
4670                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4671                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4672                               total_nodes),
4673                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4674         } else {
4675             mc->setF(Inkscape::NORMAL_MESSAGE,
4676                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4677                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4678                               total_nodes),
4679                      selected_nodes, total_nodes, when_selected);
4680         }
4681     }
4684 /*
4685  * returns a *copy* of the curve of that object.
4686  */
4687 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4688     if (!object)
4689         return NULL;
4691     SPCurve *curve = NULL;
4692     if (SP_IS_PATH(object)) {
4693         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4694         curve = curve_new->copy();
4695     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4696         const gchar *svgd = object->repr->attribute(key);
4697         if (svgd) {
4698             NArtBpath *bpath = sp_svg_read_path(svgd);
4699             SPCurve *curve_new = SPCurve::new_from_bpath(bpath);
4700             if (curve_new) {
4701                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4702             } else {
4703                 g_free(bpath);
4704             }
4705         }
4706     }
4708     return curve;
4711 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4712     if (!np || !np->object || !curve)
4713         return;
4715     if (SP_IS_PATH(np->object)) {
4716         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4717             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4718         } else {
4719             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4720         }
4721     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4722         // FIXME: this writing to string and then reading from string is bound to be slow.
4723         // create a method to convert from curve directly to 2geom...
4724         gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
4725         LIVEPATHEFFECT(np->object)->lpe->setParameter(np->repr_key, svgpath);
4726         g_free(svgpath);
4728         np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4729     }
4732 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4733     np->show_helperpath = show;
4735     if (show) {
4736         SPCurve *helper_curve = np->curve->copy();
4737         helper_curve->transform(np->i2d );
4738         if (!np->helper_path) {
4739             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4740             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);
4741             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4742             sp_canvas_item_move_to_z(np->helper_path, 0);
4743             sp_canvas_item_show(np->helper_path);
4744         } else {
4745             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4746         }
4747         helper_curve->unref();
4748     } else {
4749         if (np->helper_path) {
4750             GtkObject *temp = np->helper_path;
4751             np->helper_path = NULL;
4752             gtk_object_destroy(temp);
4753         }
4754     }
4757 /* sp_nodepath_make_straight_path:
4758  *   Prevents user from curving the path by dragging a segment or activating handles etc.
4759  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
4760  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
4761  */
4762 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4763     np->straight_path = true;
4764     np->show_handles = false;
4765     g_message("add code to make the path straight.");
4766     // do sp_nodepath_convert_node_type on all nodes?
4767     // coding tip: search for this text : "Make selected segments lines"
4771 /*
4772   Local Variables:
4773   mode:c++
4774   c-file-style:"stroustrup"
4775   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4776   indent-tabs-mode:nil
4777   fill-column:99
4778   End:
4779 */
4780 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :