Code

change NR::Matrix to Geom:: for many sp_item_xxx_affine functions
[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/effect.h"
55 #include "live_effects/parameter/parameter.h"
56 #include "util/mathfns.h"
57 #include "display/snap-indicator.h"
58 #include "snapped-point.h"
60 class NR::Matrix;
62 /// \todo
63 /// evil evil evil. FIXME: conflict of two different Path classes!
64 /// There is a conflict in the namespace between two classes named Path.
65 /// #include "sp-flowtext.h"
66 /// #include "sp-flowregion.h"
68 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
69 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
70 GType sp_flowregion_get_type (void);
71 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
72 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
73 GType sp_flowtext_get_type (void);
74 // end evil workaround
76 #include "helper/stlport.h"
79 /// \todo fixme: Implement these via preferences */
81 #define NODE_FILL          0xbfbfbf00
82 #define NODE_STROKE        0x000000ff
83 #define NODE_FILL_HI       0xff000000
84 #define NODE_STROKE_HI     0x000000ff
85 #define NODE_FILL_SEL      0x0000ffff
86 #define NODE_STROKE_SEL    0x000000ff
87 #define NODE_FILL_SEL_HI   0xff000000
88 #define NODE_STROKE_SEL_HI 0x000000ff
89 #define KNOT_FILL          0xffffffff
90 #define KNOT_STROKE        0x000000ff
91 #define KNOT_FILL_HI       0xff000000
92 #define KNOT_STROKE_HI     0x000000ff
94 static GMemChunk *nodechunk = NULL;
96 /* Creation from object */
98 static NArtBpath const * subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath const *b, gchar const *t);
99 static gchar *parse_nodetypes(gchar const *types, gint length);
101 /* Object updating */
103 static void stamp_repr(Inkscape::NodePath::Path *np);
104 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
105 static gchar *create_typestr(Inkscape::NodePath::Path *np);
107 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
109 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
111 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
113 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
115 /* Adjust handle placement, if the node or the other handle is moved */
116 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
117 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
119 /* Node event callbacks */
120 static void node_clicked(SPKnot *knot, guint state, gpointer data);
121 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
122 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
123 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
125 /* Handle event callbacks */
126 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
127 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
128 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
129 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
130 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
131 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
133 /* Constructors and destructors */
135 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
136 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
137 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
138 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
139 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
140                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
141 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
143 /* Helpers */
145 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
146 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
147 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
149 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
150 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
152 // active_node indicates mouseover node
153 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
155 /**
156  * \brief Creates new nodepath from item
157  */
158 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
160     Inkscape::XML::Node *repr = object->repr;
162     /** \todo
163      * FIXME: remove this. We don't want to edit paths inside flowtext.
164      * Instead we will build our flowtext with cloned paths, so that the
165      * real paths are outside the flowtext and thus editable as usual.
166      */
167     if (SP_IS_FLOWTEXT(object)) {
168         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
169             if SP_IS_FLOWREGION(child) {
170                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
171                 if (grandchild && SP_IS_PATH(grandchild)) {
172                     object = SP_ITEM(grandchild);
173                     break;
174                 }
175             }
176         }
177     }
179     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
181     if (curve == NULL)
182         return NULL;
184     NArtBpath const *bpath = curve->get_bpath();
185     gint length = curve->get_length();
186     if (length == 0) {
187         curve->unref();
188         return NULL; // prevent crash for one-node paths
189     }
191     //Create new nodepath
192     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
193     if (!np) {
194         curve->unref();
195         return NULL;
196     }
198     // Set defaults
199     np->desktop     = desktop;
200     np->object      = object;
201     np->subpaths    = NULL;
202     np->selected    = NULL;
203     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
204     np->livarot_path = NULL;
205     np->local_change = 0;
206     np->show_handles = show_handles;
207     np->helper_path = NULL;
208     np->helperpath_rgba = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
209     np->helperpath_width = 1.0;
210     np->curve = curve->copy();
211     np->show_helperpath = prefs_get_int_attribute ("tools.nodes", "show_helperpath",  0) == 1;
212     np->straight_path = false;
213     if (IS_LIVEPATHEFFECT(object) && item) {
214         np->item = item;
215     } else {
216         np->item = SP_ITEM(object);
217     }
219     // we need to update item's transform from the repr here,
220     // because they may be out of sync when we respond
221     // to a change in repr by regenerating nodepath     --bb
222     sp_object_read_attr(SP_OBJECT(np->item), "transform");
224     np->i2d  = from_2geom(sp_item_i2d_affine(np->item));
225     np->d2i  = np->i2d.inverse();
227     np->repr = repr;
228     if (repr_key_in) { // apparantly the object is an LPEObject
229         np->repr_key = g_strdup(repr_key_in);
230         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
231         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
232         if (lpeparam) {
233             lpeparam->param_setup_nodepath(np);
234         }
235     } else {
236         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
237         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
238             np->repr_key = g_strdup("inkscape:original-d");
240             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
241             if (lpe) {
242                 lpe->setup_nodepath(np);
243             }
244         } else {
245             np->repr_key = g_strdup("d");
246         }
247     }
249     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
250     gchar *typestr = parse_nodetypes(nodetypes, length);
252     // create the subpath(s) from the bpath
253     NArtBpath const *b = bpath;
254     while (b->code != NR_END) {
255         b = subpath_from_bpath(np, b, typestr + (b - bpath));
256     }
258     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
259     np->subpaths = g_list_reverse(np->subpaths);
261     g_free(typestr);
262     curve->unref();
264     // create the livarot representation from the same item
265     sp_nodepath_ensure_livarot_path(np);
267     // Draw helper curve
268     if (np->show_helperpath) {
269         SPCurve *helper_curve = np->curve->copy();
270         helper_curve->transform(np->i2d );
271         np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(desktop), helper_curve);
272         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);
273         sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
274         sp_canvas_item_move_to_z(np->helper_path, 0);
275         sp_canvas_item_show(np->helper_path);
276         helper_curve->unref();
277     }
279     return np;
282 /**
283  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
284  */
285 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
287     if (!np)  //soft fail, like delete
288         return;
290     while (np->subpaths) {
291         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
292     }
294     //Inform the ShapeEditor that made me, if any, that I am gone.
295     if (np->shape_editor)
296         np->shape_editor->nodepath_destroyed();
298     g_assert(!np->selected);
300     if (np->livarot_path) {
301         delete np->livarot_path;
302         np->livarot_path = NULL;
303     }
305     if (np->helper_path) {
306         GtkObject *temp = np->helper_path;
307         np->helper_path = NULL;
308         gtk_object_destroy(temp);
309     }
310     if (np->curve) {
311         np->curve->unref();
312         np->curve = NULL;
313     }
315     if (np->repr_key) {
316         g_free(np->repr_key);
317         np->repr_key = NULL;
318     }
319     if (np->repr_nodetypes_key) {
320         g_free(np->repr_nodetypes_key);
321         np->repr_nodetypes_key = NULL;
322     }
324     np->desktop = NULL;
326     g_free(np);
330 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
332     if (np && np->livarot_path == NULL) {
333         SPCurve *curve = create_curve(np);
334         NArtBpath const *bpath = SP_CURVE_BPATH(curve);
335         np->livarot_path = bpath_to_Path(bpath);
337         if (np->livarot_path)
338             np->livarot_path->ConvertWithBackData(0.01);
340         curve->unref();
341     }
345 /**
346  *  Return the node count of a given NodeSubPath.
347  */
348 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
350     if (!subpath)
351         return 0;
352     gint nodeCount = g_list_length(subpath->nodes);
353     return nodeCount;
356 /**
357  *  Return the node count of a given NodePath.
358  */
359 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
361     if (!np)
362         return 0;
363     gint nodeCount = 0;
364     for (GList *item = np->subpaths ; item ; item=item->next) {
365        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
366         nodeCount += g_list_length(subpath->nodes);
367     }
368     return nodeCount;
371 /**
372  *  Return the subpath count of a given NodePath.
373  */
374 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
376     if (!np)
377         return 0;
378     return g_list_length (np->subpaths);
381 /**
382  *  Return the selected node count of a given NodePath.
383  */
384 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
386     if (!np)
387         return 0;
388     return g_list_length (np->selected);
391 /**
392  *  Return the number of subpaths where nodes are selected in a given NodePath.
393  */
394 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
396     if (!np)
397         return 0;
398     if (!np->selected)
399         return 0;
400     if (!np->selected->next)
401         return 1;
402     gint count = 0;
403     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
404         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
405         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
406             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
407             if (node->selected) {
408                 count ++;
409                 break;
410             }
411         }
412     }
413     return count;
416 /**
417  * Clean up a nodepath after editing.
418  *
419  * Currently we are deleting trivial subpaths.
420  */
421 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
423     GList *badSubPaths = NULL;
425     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
426     for (GList *l = nodepath->subpaths; l ; l=l->next) {
427        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
428        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
429             badSubPaths = g_list_append(badSubPaths, sp);
430     }
432     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
433     //also removes the subpath from nodepath->subpaths
434     for (GList *l = badSubPaths; l ; l=l->next) {
435        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
436         sp_nodepath_subpath_destroy(sp);
437     }
439     g_list_free(badSubPaths);
442 /**
443  * Create new nodepath from b, make it subpath of np.
444  * \param t The node type.
445  * \todo Fixme: t should be a proper type, rather than gchar
446  */
447 static NArtBpath const * subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath const *b, gchar const *t)
449     NR::Point ppos, pos, npos;
451     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
453     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
454     bool const closed = (b->code == NR_MOVETO);
456     pos = NR::Point(b->x3, b->y3) * np->i2d;
457     if (b[1].code == NR_CURVETO) {
458         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
459     } else {
460         npos = pos;
461     }
462     Inkscape::NodePath::Node *n;
463     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
464     g_assert(sp->first == n);
465     g_assert(sp->last  == n);
467     b++;
468     t++;
469     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
470         pos = NR::Point(b->x3, b->y3) * np->i2d;
471         if (b->code == NR_CURVETO) {
472             ppos = NR::Point(b->x2, b->y2) * np->i2d;
473         } else {
474             ppos = pos;
475         }
476         if (b[1].code == NR_CURVETO) {
477             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
478         } else {
479             npos = pos;
480         }
481         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
482         b++;
483         t++;
484     }
486     if (closed) sp_nodepath_subpath_close(sp);
488     return b;
491 /**
492  * Convert from sodipodi:nodetypes to new style type string.
493  */
494 static gchar *parse_nodetypes(gchar const *types, gint length)
496     g_assert(length > 0);
498     gchar *typestr = g_new(gchar, length + 1);
500     gint pos = 0;
502     if (types) {
503         for (gint i = 0; types[i] && ( i < length ); i++) {
504             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
505             if (types[i] != '\0') {
506                 switch (types[i]) {
507                     case 's':
508                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
509                         break;
510                     case 'z':
511                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
512                         break;
513                     case 'c':
514                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
515                         break;
516                     default:
517                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
518                         break;
519                 }
520             }
521         }
522     }
524     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
526     return typestr;
529 /**
530  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
531  * updated but repr is not (for speed). Used during curve and node drag.
532  */
533 static void update_object(Inkscape::NodePath::Path *np)
535     g_assert(np);
537     np->curve->unref();
538     np->curve = create_curve(np);
540     sp_nodepath_set_curve(np, np->curve);
542     if (np->show_helperpath) {
543         SPCurve * helper_curve = np->curve->copy();
544         helper_curve->transform(np->i2d );
545         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
546         helper_curve->unref();
547     }
550 /**
551  * Update XML path node with data from path object.
552  */
553 static void update_repr_internal(Inkscape::NodePath::Path *np)
555     g_assert(np);
557     Inkscape::XML::Node *repr = np->object->repr;
559     np->curve->unref();
560     np->curve = create_curve(np);
562     gchar *typestr = create_typestr(np);
563     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
565     // determine if path has an effect applied and write to correct "d" attribute.
566     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
567         np->local_change++;
568         repr->setAttribute(np->repr_key, svgpath);
569     }
571     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
572         np->local_change++;
573         repr->setAttribute(np->repr_nodetypes_key, typestr);
574     }
576     g_free(svgpath);
577     g_free(typestr);
579     if (np->show_helperpath) {
580         SPCurve * helper_curve = np->curve->copy();
581         helper_curve->transform(np->i2d );
582         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
583         helper_curve->unref();
584     }
585  }
587 /**
588  * Update XML path node with data from path object, commit changes forever.
589  */
590 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
592     //fixme: np can be NULL, so check before proceeding
593     g_return_if_fail(np != NULL);
595     if (np->livarot_path) {
596         delete np->livarot_path;
597         np->livarot_path = NULL;
598     }
600     update_repr_internal(np);
601     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
603     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
604                      annotation);
607 /**
608  * Update XML path node with data from path object, commit changes with undo.
609  */
610 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
612     if (np->livarot_path) {
613         delete np->livarot_path;
614         np->livarot_path = NULL;
615     }
617     update_repr_internal(np);
618     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
619                            annotation);
622 /**
623  * Make duplicate of path, replace corresponding XML node in tree, commit.
624  */
625 static void stamp_repr(Inkscape::NodePath::Path *np)
627     g_assert(np);
629     Inkscape::XML::Node *old_repr = np->object->repr;
630     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
632     // remember the position of the item
633     gint pos = old_repr->position();
634     // remember parent
635     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
637     SPCurve *curve = create_curve(np);
638     gchar *typestr = create_typestr(np);
640     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
642     new_repr->setAttribute(np->repr_key, svgpath);
643     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
645     // add the new repr to the parent
646     parent->appendChild(new_repr);
647     // move to the saved position
648     new_repr->setPosition(pos > 0 ? pos : 0);
650     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
651                      _("Stamp"));
653     Inkscape::GC::release(new_repr);
654     g_free(svgpath);
655     g_free(typestr);
656     curve->unref();
659 /**
660  * Create curve from path.
661  */
662 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
664     SPCurve *curve = new SPCurve();
666     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
667        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
668         curve->moveto(sp->first->pos * np->d2i);
669        Inkscape::NodePath::Node *n = sp->first->n.other;
670         while (n) {
671             NR::Point const end_pt = n->pos * np->d2i;
672             switch (n->code) {
673                 case NR_LINETO:
674                     curve->lineto(end_pt);
675                     break;
676                 case NR_CURVETO:
677                     curve->curveto(n->p.other->n.pos * np->d2i,
678                                      n->p.pos * np->d2i,
679                                      end_pt);
680                     break;
681                 default:
682                     g_assert_not_reached();
683                     break;
684             }
685             if (n != sp->last) {
686                 n = n->n.other;
687             } else {
688                 n = NULL;
689             }
690         }
691         if (sp->closed) {
692             curve->closepath();
693         }
694     }
696     return curve;
699 /**
700  * Convert path type string to sodipodi:nodetypes style.
701  */
702 static gchar *create_typestr(Inkscape::NodePath::Path *np)
704     gchar *typestr = g_new(gchar, 32);
705     gint len = 32;
706     gint pos = 0;
708     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
709        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
711         if (pos >= len) {
712             typestr = g_renew(gchar, typestr, len + 32);
713             len += 32;
714         }
716         typestr[pos++] = 'c';
718        Inkscape::NodePath::Node *n;
719         n = sp->first->n.other;
720         while (n) {
721             gchar code;
723             switch (n->type) {
724                 case Inkscape::NodePath::NODE_CUSP:
725                     code = 'c';
726                     break;
727                 case Inkscape::NodePath::NODE_SMOOTH:
728                     code = 's';
729                     break;
730                 case Inkscape::NodePath::NODE_SYMM:
731                     code = 'z';
732                     break;
733                 default:
734                     g_assert_not_reached();
735                     code = '\0';
736                     break;
737             }
739             if (pos >= len) {
740                 typestr = g_renew(gchar, typestr, len + 32);
741                 len += 32;
742             }
744             typestr[pos++] = code;
746             if (n != sp->last) {
747                 n = n->n.other;
748             } else {
749                 n = NULL;
750             }
751         }
752     }
754     if (pos >= len) {
755         typestr = g_renew(gchar, typestr, len + 1);
756         len += 1;
757     }
759     typestr[pos++] = '\0';
761     return typestr;
764 /**
765  * Returns current path in context. // later eliminate this function at all!
766  */
767 static Inkscape::NodePath::Path *sp_nodepath_current()
769     if (!SP_ACTIVE_DESKTOP) {
770         return NULL;
771     }
773     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
775     if (!SP_IS_NODE_CONTEXT(event_context)) {
776         return NULL;
777     }
779     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
784 /**
785  \brief Fills node and handle positions for three nodes, splitting line
786   marked by end at distance t.
787  */
788 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
790     g_assert(new_path != NULL);
791     g_assert(end      != NULL);
793     g_assert(end->p.other == new_path);
794    Inkscape::NodePath::Node *start = new_path->p.other;
795     g_assert(start);
797     if (end->code == NR_LINETO) {
798         new_path->type =Inkscape::NodePath::NODE_CUSP;
799         new_path->code = NR_LINETO;
800         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
801     } else {
802         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
803         new_path->code = NR_CURVETO;
804         gdouble s      = 1 - t;
805         for (int dim = 0; dim < 2; dim++) {
806             NR::Coord const f000 = start->pos[dim];
807             NR::Coord const f001 = start->n.pos[dim];
808             NR::Coord const f011 = end->p.pos[dim];
809             NR::Coord const f111 = end->pos[dim];
810             NR::Coord const f00t = s * f000 + t * f001;
811             NR::Coord const f01t = s * f001 + t * f011;
812             NR::Coord const f11t = s * f011 + t * f111;
813             NR::Coord const f0tt = s * f00t + t * f01t;
814             NR::Coord const f1tt = s * f01t + t * f11t;
815             NR::Coord const fttt = s * f0tt + t * f1tt;
816             start->n.pos[dim]    = f00t;
817             new_path->p.pos[dim] = f0tt;
818             new_path->pos[dim]   = fttt;
819             new_path->n.pos[dim] = f1tt;
820             end->p.pos[dim]      = f11t;
821         }
822     }
825 /**
826  * Adds new node on direct line between two nodes, activates handles of all
827  * three nodes.
828  */
829 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
831     g_assert(end);
832     g_assert(end->subpath);
833     g_assert(g_list_find(end->subpath->nodes, end));
835    Inkscape::NodePath::Node *start = end->p.other;
836     g_assert( start->n.other == end );
837    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
838                                                end,
839                                                (NRPathcode)end->code == NR_LINETO?
840                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
841                                                (NRPathcode)end->code,
842                                                &start->pos, &start->pos, &start->n.pos);
843     sp_nodepath_line_midpoint(newnode, end, t);
845     sp_node_adjust_handles(start);
846     sp_node_update_handles(start);
847     sp_node_update_handles(newnode);
848     sp_node_adjust_handles(end);
849     sp_node_update_handles(end);
851     return newnode;
854 /**
855 \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
856 */
857 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
859     g_assert(node);
860     g_assert(node->subpath);
861     g_assert(g_list_find(node->subpath->nodes, node));
863    Inkscape::NodePath::SubPath *sp = node->subpath;
864     Inkscape::NodePath::Path *np    = sp->nodepath;
866     if (sp->closed) {
867         sp_nodepath_subpath_open(sp, node);
868         return sp->first;
869     } else {
870         // no break for end nodes
871         if (node == sp->first) return NULL;
872         if (node == sp->last ) return NULL;
874         // create a new subpath
875        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
877         // duplicate the break node as start of the new subpath
878        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
880         while (node->n.other) { // copy the remaining nodes into the new subpath
881            Inkscape::NodePath::Node *n  = node->n.other;
882            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);
883             if (n->selected) {
884                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
885             }
886             sp_nodepath_node_destroy(n); // remove the point on the original subpath
887         }
889         return newnode;
890     }
893 /**
894  * Duplicate node and connect to neighbours.
895  */
896 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
898     g_assert(node);
899     g_assert(node->subpath);
900     g_assert(g_list_find(node->subpath->nodes, node));
902    Inkscape::NodePath::SubPath *sp = node->subpath;
904     NRPathcode code = (NRPathcode) node->code;
905     if (code == NR_MOVETO) { // if node is the endnode,
906         node->code = NR_LINETO; // new one is inserted before it, so change that to line
907     }
909     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
911     if (!node->n.other || !node->p.other) // if node is an endnode, select it
912         return node;
913     else
914         return newnode; // otherwise select the newly created node
917 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
919     node->p.pos = (node->pos + (node->pos - node->n.pos));
922 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
924     node->n.pos = (node->pos + (node->pos - node->p.pos));
927 /**
928  * Change line type at node, with side effects on neighbours.
929  */
930 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
932     g_assert(end);
933     g_assert(end->subpath);
934     g_assert(end->p.other);
936     if (end->code == static_cast< guint > ( code ) )
937         return;
939    Inkscape::NodePath::Node *start = end->p.other;
941     end->code = code;
943     if (code == NR_LINETO) {
944         if (start->code == NR_LINETO) {
945             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
946         }
947         if (end->n.other) {
948             if (end->n.other->code == NR_LINETO) {
949                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
950             }
951         }
952     } else {
953         NR::Point delta = end->pos - start->pos;
954         start->n.pos = start->pos + delta / 3;
955         end->p.pos = end->pos - delta / 3;
956         sp_node_adjust_handle(start, 1);
957         sp_node_adjust_handle(end, -1);
958     }
960     sp_node_update_handles(start);
961     sp_node_update_handles(end);
964 /**
965  * Change node type, and its handles accordingly.
966  */
967 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
969     g_assert(node);
970     g_assert(node->subpath);
972     if ((node->p.other != NULL) && (node->n.other != NULL)) {
973         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
974             type =Inkscape::NodePath::NODE_CUSP;
975         }
976     }
978     node->type = type;
980     if (node->type == Inkscape::NodePath::NODE_CUSP) {
981         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
982         node->knot->setSize (node->selected? 11 : 9);
983         sp_knot_update_ctrl(node->knot);
984     } else {
985         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
986         node->knot->setSize (node->selected? 9 : 7);
987         sp_knot_update_ctrl(node->knot);
988     }
990     // if one of handles is mouseovered, preserve its position
991     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
992         sp_node_adjust_handle(node, 1);
993     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
994         sp_node_adjust_handle(node, -1);
995     } else {
996         sp_node_adjust_handles(node);
997     }
999     sp_node_update_handles(node);
1001     sp_nodepath_update_statusbar(node->subpath->nodepath);
1003     return node;
1006 bool
1007 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1009         Inkscape::NodePath::Node *othernode = side->other;
1010         if (!othernode)
1011             return false;
1012         NRPathcode const code = sp_node_path_code_from_side(node, side);
1013         if (code == NR_LINETO)
1014             return true;
1015         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1016         if (&node->p == side) {
1017             other_to_me = &othernode->n;
1018         } else if (&node->n == side) {
1019             other_to_me = &othernode->p;
1020         } 
1021         if (!other_to_me)
1022             return false;
1023         bool is_line = 
1024              (NR::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1025               NR::L2(node->pos - side->pos) < 1e-6);
1026         return is_line;
1029 /**
1030  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1031  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1032  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1033  * If already cusp and set to cusp, retracts handles.
1034 */
1035 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1037     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1039 /* 
1040   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1041  
1042         if (two_handles) {
1043             // do nothing, adjust_handles called via set_node_type will line them up
1044         } else if (one_handle) {
1045             if (opposite_to_handle_is_line) {
1046                 if (lined_up) {
1047                     // already half-smooth; pull opposite handle too making it fully smooth
1048                 } else {
1049                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1050                 }
1051             } else {
1052                 // pull opposite handle in line with the existing one
1053             }
1054         } else if (no_handles) {
1055             if (both_segments_are_lines OR both_segments_are_curves) {
1056                 //pull both handles
1057             } else {
1058                 // pull the handle opposite to line segment, making node half-smooth
1059             }
1060         }
1061 */
1062         bool p_has_handle = (NR::L2(node->pos  - node->p.pos) > 1e-6);
1063         bool n_has_handle = (NR::L2(node->pos  - node->n.pos) > 1e-6);
1064         bool p_is_line = sp_node_side_is_line(node, &node->p);
1065         bool n_is_line = sp_node_side_is_line(node, &node->n);
1067         if (p_has_handle && n_has_handle) {
1068             // do nothing, adjust_handles will line them up
1069         } else if (p_has_handle || n_has_handle) {
1070             if (p_has_handle && n_is_line) {
1071                 Radial line (node->n.other->pos - node->pos);
1072                 Radial handle (node->pos - node->p.pos);
1073                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1074                     // already half-smooth; pull opposite handle too making it fully smooth
1075                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1076                 } else {
1077                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1078                 }
1079             } else if (n_has_handle && p_is_line) {
1080                 Radial line (node->p.other->pos - node->pos);
1081                 Radial handle (node->pos - node->n.pos);
1082                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1083                     // already half-smooth; pull opposite handle too making it fully smooth
1084                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1085                 } else {
1086                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1087                 }
1088             } else if (p_has_handle && node->n.other) {
1089                 // pull n handle
1090                 node->n.other->code = NR_CURVETO;
1091                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1092                     NR::L2(node->p.pos - node->pos) :
1093                     NR::L2(node->n.other->pos - node->pos) / 3;
1094                 node->n.pos = node->pos - (len / NR::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1095             } else if (n_has_handle && node->p.other) {
1096                 // pull p handle
1097                 node->code = NR_CURVETO;
1098                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1099                     NR::L2(node->n.pos - node->pos) :
1100                     NR::L2(node->p.other->pos - node->pos) / 3;
1101                 node->p.pos = node->pos - (len / NR::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1102             }
1103         } else if (!p_has_handle && !n_has_handle) {
1104             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1105                 // no handles, but both segments are either lnes or curves:
1106                 //pull both handles
1108                 // convert both to curves:
1109                 node->code = NR_CURVETO;
1110                 node->n.other->code = NR_CURVETO;
1112                 NR::Point leg_prev = node->pos - node->p.other->pos;
1113                 NR::Point leg_next = node->pos - node->n.other->pos;
1115                 double norm_leg_prev = L2(leg_prev);
1116                 double norm_leg_next = L2(leg_next);
1118                 NR::Point delta;
1119                 if (norm_leg_next > 0.0) {
1120                     delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1121                     (&delta)->normalize();
1122                 }
1124                 if (type == Inkscape::NodePath::NODE_SYMM) {
1125                     double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1126                     node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1127                     node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1128                 } else {
1129                     // length of handle is proportional to distance to adjacent node
1130                     node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1131                     node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1132                 }
1134             } else {
1135                 // pull the handle opposite to line segment, making it half-smooth
1136                 if (p_is_line && node->n.other) {
1137                     if (type != Inkscape::NodePath::NODE_SYMM) {
1138                         // pull n handle
1139                         node->n.other->code = NR_CURVETO;
1140                         double len =  NR::L2(node->n.other->pos - node->pos) / 3;
1141                         node->n.pos = node->pos + (len / NR::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1142                     }
1143                 } else if (n_is_line && node->p.other) {
1144                     if (type != Inkscape::NodePath::NODE_SYMM) {
1145                         // pull p handle
1146                         node->code = NR_CURVETO;
1147                         double len =  NR::L2(node->p.other->pos - node->pos) / 3;
1148                         node->p.pos = node->pos + (len / NR::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1149                     }
1150                 }
1151             }
1152         }
1153     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1154         // cusping a cusp: retract nodes
1155         node->p.pos = node->pos;
1156         node->n.pos = node->pos;
1157     }
1159     sp_nodepath_set_node_type (node, type);
1162 /**
1163  * Move node to point, and adjust its and neighbouring handles.
1164  */
1165 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1167     NR::Point delta = p - node->pos;
1168     node->pos = p;
1170     node->p.pos += delta;
1171     node->n.pos += delta;
1173     Inkscape::NodePath::Node *node_p = NULL;
1174     Inkscape::NodePath::Node *node_n = NULL;
1176     if (node->p.other) {
1177         if (node->code == NR_LINETO) {
1178             sp_node_adjust_handle(node, 1);
1179             sp_node_adjust_handle(node->p.other, -1);
1180             node_p = node->p.other;
1181         }
1182     }
1183     if (node->n.other) {
1184         if (node->n.other->code == NR_LINETO) {
1185             sp_node_adjust_handle(node, -1);
1186             sp_node_adjust_handle(node->n.other, 1);
1187             node_n = node->n.other;
1188         }
1189     }
1191     // this function is only called from batch movers that will update display at the end
1192     // themselves, so here we just move all the knots without emitting move signals, for speed
1193     sp_node_update_handles(node, false);
1194     if (node_n) {
1195         sp_node_update_handles(node_n, false);
1196     }
1197     if (node_p) {
1198         sp_node_update_handles(node_p, false);
1199     }
1202 /**
1203  * Call sp_node_moveto() for node selection and handle possible snapping.
1204  */
1205 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1206                                             bool const snap, bool constrained = false, 
1207                                             Inkscape::Snapper::ConstraintLine const &constraint = NR::Point())
1209     NR::Coord best = NR_HUGE;
1210     NR::Point delta(dx, dy);
1211     NR::Point best_pt = delta;
1212     Inkscape::SnappedPoint best_abs;
1213     
1214     if (snap) {    
1215         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1216          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1217          * must provide that information. */
1218           
1219         // Build a list of the unselected nodes to which the snapper should snap 
1220         std::vector<NR::Point> unselected_nodes;
1221         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1222             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1223             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1224                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1225                 if (!node->selected) {
1226                     unselected_nodes.push_back(node->pos);
1227                 }    
1228             }
1229         }        
1230         
1231         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1232         
1233         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1234             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1235             m.setup(NULL, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1236             Inkscape::SnappedPoint s;
1237             if (constrained) {
1238                 Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1239                 dedicated_constraint.setPoint(n->pos);
1240                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, dedicated_constraint);
1241             } else {
1242                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta);
1243             }            
1244             if (s.getSnapped() && (s.getDistance() < best)) {
1245                 best = s.getDistance();
1246                 best_abs = s;
1247                 best_pt = s.getPoint() - n->pos;
1248             }
1249         }
1250                         
1251         if (best_abs.getSnapped()) {
1252             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1253         } else {
1254             nodepath->desktop->snapindicator->remove_snappoint();    
1255         }
1256     }
1258     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1259         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1260         sp_node_moveto(n, n->pos + best_pt);
1261     }
1263     // do not update repr here so that node dragging is acceptably fast
1264     update_object(nodepath);
1267 /**
1268 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1269 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1270 near x = 0.
1271  */
1272 double
1273 sculpt_profile (double x, double alpha, guint profile)
1275     if (x >= 1)
1276         return 0;
1277     if (x <= 0)
1278         return 1;
1280     switch (profile) {
1281         case SCULPT_PROFILE_LINEAR:
1282         return 1 - x;
1283         case SCULPT_PROFILE_BELL:
1284         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1285         case SCULPT_PROFILE_ELLIPTIC:
1286         return sqrt(1 - x*x);
1287     }
1289     return 1;
1292 double
1293 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1295     // extremely primitive for now, don't have time to look for the real one
1296     double lower = NR::L2(b - a);
1297     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1298     return (lower + upper)/2;
1301 void
1302 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1304     n->pos = n->origin + delta;
1305     n->n.pos = n->n.origin + delta_n;
1306     n->p.pos = n->p.origin + delta_p;
1307     sp_node_adjust_handles(n);
1308     sp_node_update_handles(n, false);
1311 /**
1312  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1313  * on how far they are from the dragged node n.
1314  */
1315 static void
1316 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1318     g_assert (n);
1319     g_assert (nodepath);
1320     g_assert (n->subpath->nodepath == nodepath);
1322     double pressure = n->knot->pressure;
1323     if (pressure == 0)
1324         pressure = 0.5; // default
1325     pressure = CLAMP (pressure, 0.2, 0.8);
1327     // map pressure to alpha = 1/5 ... 5
1328     double alpha = 1 - 2 * fabs(pressure - 0.5);
1329     if (pressure > 0.5)
1330         alpha = 1/alpha;
1332     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1334     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1335         // Only one subpath has selected nodes:
1336         // use linear mode, where the distance from n to node being dragged is calculated along the path
1338         double n_sel_range = 0, p_sel_range = 0;
1339         guint n_nodes = 0, p_nodes = 0;
1340         guint n_sel_nodes = 0, p_sel_nodes = 0;
1342         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1343         {
1344             double n_range = 0, p_range = 0;
1345             bool n_going = true, p_going = true;
1346             Inkscape::NodePath::Node *n_node = n;
1347             Inkscape::NodePath::Node *p_node = n;
1348             do {
1349                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1350                 if (n_node && n_going)
1351                     n_node = n_node->n.other;
1352                 if (n_node == NULL) {
1353                     n_going = false;
1354                 } else {
1355                     n_nodes ++;
1356                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1357                     if (n_node->selected) {
1358                         n_sel_nodes ++;
1359                         n_sel_range = n_range;
1360                     }
1361                     if (n_node == p_node) {
1362                         n_going = false;
1363                         p_going = false;
1364                     }
1365                 }
1366                 if (p_node && p_going)
1367                     p_node = p_node->p.other;
1368                 if (p_node == NULL) {
1369                     p_going = false;
1370                 } else {
1371                     p_nodes ++;
1372                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1373                     if (p_node->selected) {
1374                         p_sel_nodes ++;
1375                         p_sel_range = p_range;
1376                     }
1377                     if (p_node == n_node) {
1378                         n_going = false;
1379                         p_going = false;
1380                     }
1381                 }
1382             } while (n_going || p_going);
1383         }
1385         // Second pass: actually move nodes in this subpath
1386         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1387         {
1388             double n_range = 0, p_range = 0;
1389             bool n_going = true, p_going = true;
1390             Inkscape::NodePath::Node *n_node = n;
1391             Inkscape::NodePath::Node *p_node = n;
1392             do {
1393                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1394                 if (n_node && n_going)
1395                     n_node = n_node->n.other;
1396                 if (n_node == NULL) {
1397                     n_going = false;
1398                 } else {
1399                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1400                     if (n_node->selected) {
1401                         sp_nodepath_move_node_and_handles (n_node,
1402                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1403                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1404                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1405                     }
1406                     if (n_node == p_node) {
1407                         n_going = false;
1408                         p_going = false;
1409                     }
1410                 }
1411                 if (p_node && p_going)
1412                     p_node = p_node->p.other;
1413                 if (p_node == NULL) {
1414                     p_going = false;
1415                 } else {
1416                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1417                     if (p_node->selected) {
1418                         sp_nodepath_move_node_and_handles (p_node,
1419                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1420                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1421                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1422                     }
1423                     if (p_node == n_node) {
1424                         n_going = false;
1425                         p_going = false;
1426                     }
1427                 }
1428             } while (n_going || p_going);
1429         }
1431     } else {
1432         // Multiple subpaths have selected nodes:
1433         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1434         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1435         // fix the pear-like shape when sculpting e.g. a ring
1437         // First pass: calculate range
1438         gdouble direct_range = 0;
1439         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1440             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1441             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1442                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1443                 if (node->selected) {
1444                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1445                 }
1446             }
1447         }
1449         // Second pass: actually move nodes
1450         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1451             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1452             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1453                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1454                 if (node->selected) {
1455                     if (direct_range > 1e-6) {
1456                         sp_nodepath_move_node_and_handles (node,
1457                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1458                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1459                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1460                     } else {
1461                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1462                     }
1464                 }
1465             }
1466         }
1467     }
1469     // do not update repr here so that node dragging is acceptably fast
1470     update_object(nodepath);
1474 /**
1475  * Move node selection to point, adjust its and neighbouring handles,
1476  * handle possible snapping, and commit the change with possible undo.
1477  */
1478 void
1479 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1481     if (!nodepath) return;
1483     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1485     if (dx == 0) {
1486         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1487     } else if (dy == 0) {
1488         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1489     } else {
1490         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1491     }
1494 /**
1495  * Move node selection off screen and commit the change.
1496  */
1497 void
1498 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1500     // borrowed from sp_selection_move_screen in selection-chemistry.c
1501     // we find out the current zoom factor and divide deltas by it
1502     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1504     gdouble zoom = desktop->current_zoom();
1505     gdouble zdx = dx / zoom;
1506     gdouble zdy = dy / zoom;
1508     if (!nodepath) return;
1510     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1512     if (dx == 0) {
1513         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1514     } else if (dy == 0) {
1515         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1516     } else {
1517         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1518     }
1521 /**
1522  * Move selected nodes to the absolute position given
1523  */
1524 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis)
1526     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1527         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1528         NR::Point npos(axis == NR::X ? val : n->pos[NR::X], axis == NR::Y ? val : n->pos[NR::Y]);
1529         sp_node_moveto(n, npos);
1530     }
1532     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1535 /**
1536  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1537  */
1538 NR::Maybe<NR::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1540     NR::Maybe<NR::Coord> no_coord = NR::Nothing();
1541     g_return_val_if_fail(nodepath->selected, no_coord);
1543     // determine coordinate of first selected node
1544     GList *nsel = nodepath->selected;
1545     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1546     NR::Coord coord = n->pos[axis];
1547     bool coincide = true;
1549     // compare it to the coordinates of all the other selected nodes
1550     for (GList *l = nsel->next; l != NULL; l = l->next) {
1551         n = (Inkscape::NodePath::Node *) l->data;
1552         if (n->pos[axis] != coord) {
1553             coincide = false;
1554         }
1555     }
1556     if (coincide) {
1557         return coord;
1558     } else {
1559         NR::Rect bbox = sp_node_selected_bbox(nodepath);
1560         // currently we return the coordinate of the bounding box midpoint because I don't know how
1561         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1562         return bbox.midpoint()[axis];
1563     }
1566 /** If they don't yet exist, creates knot and line for the given side of the node */
1567 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1569     if (!side->knot) {
1570         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"));
1572         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1573         side->knot->setSize (7);
1574         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1575         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1576         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1577         sp_knot_update_ctrl(side->knot);
1579         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1580         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1581         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1582         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1583         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1584         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1585     }
1587     if (!side->line) {
1588         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1589                                         SP_TYPE_CTRLLINE, NULL);
1590     }
1593 /**
1594  * Ensure the given handle of the node is visible/invisible, update its screen position
1595  */
1596 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1598     g_assert(node != NULL);
1600    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1601     NRPathcode code = sp_node_path_code_from_side(node, side);
1603     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1605     if (show_handle) {
1606         if (!side->knot) { // No handle knot at all
1607             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1608             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1609             side->knot->pos = side->pos;
1610             if (side->knot->item)
1611                 SP_CTRL(side->knot->item)->moveto(side->pos);
1612             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1613             sp_knot_show(side->knot);
1614         } else {
1615             if (side->knot->pos != side->pos) { // only if it's really moved
1616                 if (fire_move_signals) {
1617                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1618                 } else {
1619                     sp_knot_moveto(side->knot, &side->pos);
1620                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1621                 }
1622             }
1623             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1624                 sp_knot_show(side->knot);
1625             }
1626         }
1627         sp_canvas_item_show(side->line);
1628     } else {
1629         if (side->knot) {
1630             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1631                 sp_knot_hide(side->knot);
1632             }
1633         }
1634         if (side->line) {
1635             sp_canvas_item_hide(side->line);
1636         }
1637     }
1640 /**
1641  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1642  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1643  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1644  * updated; otherwise, just move the knots silently (used in batch moves).
1645  */
1646 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1648     g_assert(node != NULL);
1650     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1651         sp_knot_show(node->knot);
1652     }
1654     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1655         if (fire_move_signals)
1656             sp_knot_set_position(node->knot, &node->pos, 0);
1657         else
1658             sp_knot_moveto(node->knot, &node->pos);
1659     }
1661     gboolean show_handles = node->selected;
1662     if (node->p.other != NULL) {
1663         if (node->p.other->selected) show_handles = TRUE;
1664     }
1665     if (node->n.other != NULL) {
1666         if (node->n.other->selected) show_handles = TRUE;
1667     }
1669     if (node->subpath->nodepath->show_handles == false)
1670         show_handles = FALSE;
1672     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1673     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1676 /**
1677  * Call sp_node_update_handles() for all nodes on subpath.
1678  */
1679 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1681     g_assert(subpath != NULL);
1683     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1684         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1685     }
1688 /**
1689  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1690  */
1691 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1693     g_assert(nodepath != NULL);
1695     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1696         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1697     }
1700 void
1701 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1703     if (nodepath == NULL) return;
1705     nodepath->show_handles = show;
1706     sp_nodepath_update_handles(nodepath);
1709 /**
1710  * Adds all selected nodes in nodepath to list.
1711  */
1712 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1714     StlConv<Node *>::list(l, selected);
1715 /// \todo this adds a copying, rework when the selection becomes a stl list
1718 /**
1719  * Align selected nodes on the specified axis.
1720  */
1721 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1723     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1724         return;
1725     }
1727     if ( !nodepath->selected->next ) { // only one node selected
1728         return;
1729     }
1730    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1731     NR::Point dest(pNode->pos);
1732     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1733         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1734         if (pNode) {
1735             dest[axis] = pNode->pos[axis];
1736             sp_node_moveto(pNode, dest);
1737         }
1738     }
1740     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1743 /// Helper struct.
1744 struct NodeSort
1746    Inkscape::NodePath::Node *_node;
1747     NR::Coord _coord;
1748     /// \todo use vectorof pointers instead of calling copy ctor
1749     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1750         _node(node), _coord(node->pos[axis])
1751     {}
1753 };
1755 static bool operator<(NodeSort const &a, NodeSort const &b)
1757     return (a._coord < b._coord);
1760 /**
1761  * Distribute selected nodes on the specified axis.
1762  */
1763 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1765     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1766         return;
1767     }
1769     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1770         return;
1771     }
1773    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1774     std::vector<NodeSort> sorted;
1775     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1776         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1777         if (pNode) {
1778             NodeSort n(pNode, axis);
1779             sorted.push_back(n);
1780             //dest[axis] = pNode->pos[axis];
1781             //sp_node_moveto(pNode, dest);
1782         }
1783     }
1784     std::sort(sorted.begin(), sorted.end());
1785     unsigned int len = sorted.size();
1786     //overall bboxes span
1787     float dist = (sorted.back()._coord -
1788                   sorted.front()._coord);
1789     //new distance between each bbox
1790     float step = (dist) / (len - 1);
1791     float pos = sorted.front()._coord;
1792     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1793           it < sorted.end();
1794           it ++ )
1795     {
1796         NR::Point dest((*it)._node->pos);
1797         dest[axis] = pos;
1798         sp_node_moveto((*it)._node, dest);
1799         pos += step;
1800     }
1802     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1806 /**
1807  * Call sp_nodepath_line_add_node() for all selected segments.
1808  */
1809 void
1810 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1812     if (!nodepath) {
1813         return;
1814     }
1816     GList *nl = NULL;
1818     int n_added = 0;
1820     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1821        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1822         g_assert(t->selected);
1823         if (t->p.other && t->p.other->selected) {
1824             nl = g_list_prepend(nl, t);
1825         }
1826     }
1828     while (nl) {
1829        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1830        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1831        sp_nodepath_node_select(n, TRUE, FALSE);
1832        n_added ++;
1833        nl = g_list_remove(nl, t);
1834     }
1836     /** \todo fixme: adjust ? */
1837     sp_nodepath_update_handles(nodepath);
1839     if (n_added > 1) {
1840         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1841     } else if (n_added > 0) {
1842         sp_nodepath_update_repr(nodepath, _("Add node"));
1843     }
1845     sp_nodepath_update_statusbar(nodepath);
1848 /**
1849  * Select segment nearest to point
1850  */
1851 void
1852 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1854     if (!nodepath) {
1855         return;
1856     }
1858     sp_nodepath_ensure_livarot_path(nodepath);
1859     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1860     if (!maybe_position) {
1861         return;
1862     }
1863     Path::cut_position position = *maybe_position;
1865     //find segment to segment
1866     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1868     //fixme: this can return NULL, so check before proceeding.
1869     g_return_if_fail(e != NULL);
1871     gboolean force = FALSE;
1872     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1873         force = TRUE;
1874     }
1875     sp_nodepath_node_select(e, (gboolean) toggle, force);
1876     if (e->p.other)
1877         sp_nodepath_node_select(e->p.other, TRUE, force);
1879     sp_nodepath_update_handles(nodepath);
1881     sp_nodepath_update_statusbar(nodepath);
1884 /**
1885  * Add a node nearest to point
1886  */
1887 void
1888 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1890     if (!nodepath) {
1891         return;
1892     }
1894     sp_nodepath_ensure_livarot_path(nodepath);
1895     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1896     if (!maybe_position) {
1897         return;
1898     }
1899     Path::cut_position position = *maybe_position;
1901     //find segment to split
1902     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1904     //don't know why but t seems to flip for lines
1905     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1906         position.t = 1.0 - position.t;
1907     }
1908     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1909     sp_nodepath_node_select(n, FALSE, TRUE);
1911     /* fixme: adjust ? */
1912     sp_nodepath_update_handles(nodepath);
1914     sp_nodepath_update_repr(nodepath, _("Add node"));
1916     sp_nodepath_update_statusbar(nodepath);
1919 /*
1920  * Adjusts a segment so that t moves by a certain delta for dragging
1921  * converts lines to curves
1922  *
1923  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1924  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1925  */
1926 void
1927 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1929     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1931     //fixme: e and e->p can be NULL, so check for those before proceeding
1932     g_return_if_fail(e != NULL);
1933     g_return_if_fail(&e->p != NULL);
1935     /* feel good is an arbitrary parameter that distributes the delta between handles
1936      * if t of the drag point is less than 1/6 distance form the endpoint only
1937      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1938      */
1939     double feel_good;
1940     if (t <= 1.0 / 6.0)
1941         feel_good = 0;
1942     else if (t <= 0.5)
1943         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1944     else if (t <= 5.0 / 6.0)
1945         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1946     else
1947         feel_good = 1;
1949     //if we're dragging a line convert it to a curve
1950     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1951         sp_nodepath_set_line_type(e, NR_CURVETO);
1952     }
1954     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1955     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1956     e->p.other->n.pos += offsetcoord0;
1957     e->p.pos += offsetcoord1;
1959     // adjust handles of adjacent nodes where necessary
1960     sp_node_adjust_handle(e,1);
1961     sp_node_adjust_handle(e->p.other,-1);
1963     sp_nodepath_update_handles(e->subpath->nodepath);
1965     update_object(e->subpath->nodepath);
1967     sp_nodepath_update_statusbar(e->subpath->nodepath);
1971 /**
1972  * Call sp_nodepath_break() for all selected segments.
1973  */
1974 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
1976     if (!nodepath) return;
1978     GList *temp = NULL;
1979     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1980        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1981        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1982         if (nn == NULL) continue; // no break, no new node
1983         temp = g_list_prepend(temp, nn);
1984     }
1986     if (temp) {
1987         sp_nodepath_deselect(nodepath);
1988     }
1989     for (GList *l = temp; l != NULL; l = l->next) {
1990         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1991     }
1993     sp_nodepath_update_handles(nodepath);
1995     sp_nodepath_update_repr(nodepath, _("Break path"));
1998 /**
1999  * Duplicate the selected node(s).
2000  */
2001 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2003     if (!nodepath) {
2004         return;
2005     }
2007     GList *temp = NULL;
2008     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2009        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2010        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2011         if (nn == NULL) continue; // could not duplicate
2012         temp = g_list_prepend(temp, nn);
2013     }
2015     if (temp) {
2016         sp_nodepath_deselect(nodepath);
2017     }
2018     for (GList *l = temp; l != NULL; l = l->next) {
2019         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2020     }
2022     sp_nodepath_update_handles(nodepath);
2024     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2027 /**
2028  *  Internal function to join two nodes by merging them into one.
2029  */
2030 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2032     /* a and b are endpoints */
2034     // if one of the two nodes is mouseovered, fix its position
2035     NR::Point c;
2036     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2037         c = a->pos;
2038     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2039         c = b->pos;
2040     } else {
2041         // otherwise, move joined node to the midpoint
2042         c = (a->pos + b->pos) / 2;
2043     }
2045     if (a->subpath == b->subpath) {
2046        Inkscape::NodePath::SubPath *sp = a->subpath;
2047         sp_nodepath_subpath_close(sp);
2048         sp_node_moveto (sp->first, c);
2050         sp_nodepath_update_handles(sp->nodepath);
2051         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2052         return;
2053     }
2055     /* a and b are separate subpaths */
2056     Inkscape::NodePath::SubPath *sa = a->subpath;
2057     Inkscape::NodePath::SubPath *sb = b->subpath;
2058     NR::Point p;
2059     Inkscape::NodePath::Node *n;
2060     NRPathcode code;
2061     if (a == sa->first) {
2062         // we will now reverse sa, so that a is its last node, not first, and drop that node
2063         p = sa->first->n.pos;
2064         code = (NRPathcode)sa->first->n.other->code;
2065         // create new subpath
2066        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2067        // create a first moveto node on it
2068         n = sa->last;
2069         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2070         n = n->p.other;
2071         if (n == sa->first) n = NULL;
2072         while (n) {
2073             // copy the rest of the nodes from sa to t, going backwards
2074             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2075             n = n->p.other;
2076             if (n == sa->first) n = NULL;
2077         }
2078         // replace sa with t
2079         sp_nodepath_subpath_destroy(sa);
2080         sa = t;
2081     } else if (a == sa->last) {
2082         // a is already last, just drop it
2083         p = sa->last->p.pos;
2084         code = (NRPathcode)sa->last->code;
2085         sp_nodepath_node_destroy(sa->last);
2086     } else {
2087         code = NR_END;
2088         g_assert_not_reached();
2089     }
2091     if (b == sb->first) {
2092         // copy all nodes from b to a, forward 
2093         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2094         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2095             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2096         }
2097     } else if (b == sb->last) {
2098         // copy all nodes from b to a, backward 
2099         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2100         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2101             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2102         }
2103     } else {
2104         g_assert_not_reached();
2105     }
2106     /* and now destroy sb */
2108     sp_nodepath_subpath_destroy(sb);
2110     sp_nodepath_update_handles(sa->nodepath);
2112     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2114     sp_nodepath_update_statusbar(nodepath);
2117 /**
2118  *  Internal function to join two nodes by adding a segment between them.
2119  */
2120 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2122     if (a->subpath == b->subpath) {
2123        Inkscape::NodePath::SubPath *sp = a->subpath;
2125         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2126         sp->closed = TRUE;
2128         sp->first->p.other = sp->last;
2129         sp->last->n.other  = sp->first;
2131         sp_node_handle_mirror_p_to_n(sp->last);
2132         sp_node_handle_mirror_n_to_p(sp->first);
2134         sp->first->code = sp->last->code;
2135         sp->first       = sp->last;
2137         sp_nodepath_update_handles(sp->nodepath);
2139         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2141         return;
2142     }
2144     /* a and b are separate subpaths */
2145     Inkscape::NodePath::SubPath *sa = a->subpath;
2146     Inkscape::NodePath::SubPath *sb = b->subpath;
2148     Inkscape::NodePath::Node *n;
2149     NR::Point p;
2150     NRPathcode code;
2151     if (a == sa->first) {
2152         code = (NRPathcode) sa->first->n.other->code;
2153        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2154         n = sa->last;
2155         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2156         for (n = n->p.other; n != NULL; n = n->p.other) {
2157             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2158         }
2159         sp_nodepath_subpath_destroy(sa);
2160         sa = t;
2161     } else if (a == sa->last) {
2162         code = (NRPathcode)sa->last->code;
2163     } else {
2164         code = NR_END;
2165         g_assert_not_reached();
2166     }
2168     if (b == sb->first) {
2169         n = sb->first;
2170         sp_node_handle_mirror_p_to_n(sa->last);
2171         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2172         sp_node_handle_mirror_n_to_p(sa->last);
2173         for (n = n->n.other; n != NULL; n = n->n.other) {
2174             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2175         }
2176     } else if (b == sb->last) {
2177         n = sb->last;
2178         sp_node_handle_mirror_p_to_n(sa->last);
2179         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2180         sp_node_handle_mirror_n_to_p(sa->last);
2181         for (n = n->p.other; n != NULL; n = n->p.other) {
2182             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2183         }
2184     } else {
2185         g_assert_not_reached();
2186     }
2187     /* and now destroy sb */
2189     sp_nodepath_subpath_destroy(sb);
2191     sp_nodepath_update_handles(sa->nodepath);
2193     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2196 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2198 /**
2199  * Internal function to handle joining two nodes.
2200  */
2201 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2203     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2205     if (g_list_length(nodepath->selected) != 2) {
2206         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2207         return;
2208     }
2210     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2211     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2213     g_assert(a != b);
2214     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2215         // someone tried to join an orphan node (i.e. a single-node subpath).
2216         // this is not worth an error message, just fail silently.
2217         return;
2218     }
2220     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2221         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2222         return;
2223     }
2225     switch(mode) {
2226         case NODE_JOIN_ENDPOINTS:
2227             do_node_selected_join(nodepath, a, b);
2228             break;
2229         case NODE_JOIN_SEGMENT:
2230             do_node_selected_join_segment(nodepath, a, b);
2231             break;
2232     }
2235 /**
2236  *  Join two nodes by merging them into one.
2237  */
2238 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2240     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2243 /**
2244  *  Join two nodes by adding a segment between them.
2245  */
2246 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2248     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2251 /**
2252  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2253  */
2254 void sp_node_delete_preserve(GList *nodes_to_delete)
2256     GSList *nodepaths = NULL;
2258     while (nodes_to_delete) {
2259         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2260         Inkscape::NodePath::SubPath *sp = node->subpath;
2261         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2262         Inkscape::NodePath::Node *sample_cursor = NULL;
2263         Inkscape::NodePath::Node *sample_end = NULL;
2264         Inkscape::NodePath::Node *delete_cursor = node;
2265         bool just_delete = false;
2267         //find the start of this contiguous selection
2268         //move left to the first node that is not selected
2269         //or the start of the non-closed path
2270         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2271             delete_cursor = curr;
2272         }
2274         //just delete at the beginning of an open path
2275         if (!delete_cursor->p.other) {
2276             sample_cursor = delete_cursor;
2277             just_delete = true;
2278         } else {
2279             sample_cursor = delete_cursor->p.other;
2280         }
2282         //calculate points for each segment
2283         int rate = 5;
2284         float period = 1.0 / rate;
2285         std::vector<NR::Point> data;
2286         if (!just_delete) {
2287             data.push_back(sample_cursor->pos);
2288             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2289                 //just delete at the end of an open path
2290                 if (!sp->closed && curr == sp->last) {
2291                     just_delete = true;
2292                     break;
2293                 }
2295                 //sample points on the contiguous selected segment
2296                 NR::Point *bez;
2297                 bez = new NR::Point [4];
2298                 bez[0] = curr->pos;
2299                 bez[1] = curr->n.pos;
2300                 bez[2] = curr->n.other->p.pos;
2301                 bez[3] = curr->n.other->pos;
2302                 for (int i=1; i<rate; i++) {
2303                     gdouble t = i * period;
2304                     NR::Point p = bezier_pt(3, bez, t);
2305                     data.push_back(p);
2306                 }
2307                 data.push_back(curr->n.other->pos);
2309                 sample_end = curr->n.other;
2310                 //break if we've come full circle or hit the end of the selection
2311                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2312                     break;
2313                 }
2314             }
2315         }
2317         if (!just_delete) {
2318             //calculate the best fitting single segment and adjust the endpoints
2319             NR::Point *adata;
2320             adata = new NR::Point [data.size()];
2321             copy(data.begin(), data.end(), adata);
2323             NR::Point *bez;
2324             bez = new NR::Point [4];
2325             //would decreasing error create a better fitting approximation?
2326             gdouble error = 1.0;
2327             gint ret;
2328             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2330             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2331             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2332             //the resulting nodes behave as expected.
2333             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2334                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2335             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2336                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2338             //adjust endpoints
2339             sample_cursor->n.pos = bez[1];
2340             sample_end->p.pos = bez[2];
2341         }
2343         //destroy this contiguous selection
2344         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2345             Inkscape::NodePath::Node *temp = delete_cursor;
2346             if (delete_cursor->n.other == delete_cursor) {
2347                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2348                 delete_cursor = NULL;
2349             } else {
2350                 delete_cursor = delete_cursor->n.other;
2351             }
2352             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2353             sp_nodepath_node_destroy(temp);
2354         }
2356         sp_nodepath_update_handles(nodepath);
2358         if (!g_slist_find(nodepaths, nodepath))
2359             nodepaths = g_slist_prepend (nodepaths, nodepath);
2360     }
2362     for (GSList *i = nodepaths; i; i = i->next) {
2363         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2364         // different nodepaths will give us one undo event per nodepath
2365         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2367         // if the entire nodepath is removed, delete the selected object.
2368         if (nodepath->subpaths == NULL ||
2369             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2370             //at least 2
2371             sp_nodepath_get_node_count(nodepath) < 2) {
2372             SPDocument *document = sp_desktop_document (nodepath->desktop);
2373             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2374             //delete this nodepath's object, not the entire selection! (though at this time, this
2375             //does not matter)
2376             sp_selection_delete();
2377             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2378                               _("Delete nodes"));
2379         } else {
2380             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2381             sp_nodepath_update_statusbar(nodepath);
2382         }
2383     }
2385     g_slist_free (nodepaths);
2388 /**
2389  * Delete one or more selected nodes.
2390  */
2391 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2393     if (!nodepath) return;
2394     if (!nodepath->selected) return;
2396     /** \todo fixme: do it the right way */
2397     while (nodepath->selected) {
2398        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2399         sp_nodepath_node_destroy(node);
2400     }
2403     //clean up the nodepath (such as for trivial subpaths)
2404     sp_nodepath_cleanup(nodepath);
2406     sp_nodepath_update_handles(nodepath);
2408     // if the entire nodepath is removed, delete the selected object.
2409     if (nodepath->subpaths == NULL ||
2410         sp_nodepath_get_node_count(nodepath) < 2) {
2411         SPDocument *document = sp_desktop_document (nodepath->desktop);
2412         sp_selection_delete();
2413         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2414                           _("Delete nodes"));
2415         return;
2416     }
2418     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2420     sp_nodepath_update_statusbar(nodepath);
2423 /**
2424  * Delete one or more segments between two selected nodes.
2425  * This is the code for 'split'.
2426  */
2427 void
2428 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2430    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2431    Inkscape::NodePath::Node *curr, *next;     //Iterators
2433     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2435     if (g_list_length(nodepath->selected) != 2) {
2436         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2437                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2438         return;
2439     }
2441     //Selected nodes, not inclusive
2442    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2443    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2445     if ( ( a==b)                       ||  //same node
2446          (a->subpath  != b->subpath )  ||  //not the same path
2447          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2448          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2449     {
2450         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2451                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2452         return;
2453     }
2455     //###########################################
2456     //# BEGIN EDITS
2457     //###########################################
2458     //##################################
2459     //# CLOSED PATH
2460     //##################################
2461     if (a->subpath->closed) {
2464         gboolean reversed = FALSE;
2466         //Since we can go in a circle, we need to find the shorter distance.
2467         //  a->b or b->a
2468         start = end = NULL;
2469         int distance    = 0;
2470         int minDistance = 0;
2471         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2472             if (curr==b) {
2473                 //printf("a to b:%d\n", distance);
2474                 start = a;//go from a to b
2475                 end   = b;
2476                 minDistance = distance;
2477                 //printf("A to B :\n");
2478                 break;
2479             }
2480             distance++;
2481         }
2483         //try again, the other direction
2484         distance = 0;
2485         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2486             if (curr==a) {
2487                 //printf("b to a:%d\n", distance);
2488                 if (distance < minDistance) {
2489                     start    = b;  //we go from b to a
2490                     end      = a;
2491                     reversed = TRUE;
2492                     //printf("B to A\n");
2493                 }
2494                 break;
2495             }
2496             distance++;
2497         }
2500         //Copy everything from 'end' to 'start' to a new subpath
2501        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2502         for (curr=end ; curr ; curr=curr->n.other) {
2503             NRPathcode code = (NRPathcode) curr->code;
2504             if (curr == end)
2505                 code = NR_MOVETO;
2506             sp_nodepath_node_new(t, NULL,
2507                                  (Inkscape::NodePath::NodeType)curr->type, code,
2508                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2509             if (curr == start)
2510                 break;
2511         }
2512         sp_nodepath_subpath_destroy(a->subpath);
2515     }
2519     //##################################
2520     //# OPEN PATH
2521     //##################################
2522     else {
2524         //We need to get the direction of the list between A and B
2525         //Can we walk from a to b?
2526         start = end = NULL;
2527         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2528             if (curr==b) {
2529                 start = a;  //did it!  we go from a to b
2530                 end   = b;
2531                 //printf("A to B\n");
2532                 break;
2533             }
2534         }
2535         if (!start) {//didn't work?  let's try the other direction
2536             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2537                 if (curr==a) {
2538                     start = b;  //did it!  we go from b to a
2539                     end   = a;
2540                     //printf("B to A\n");
2541                     break;
2542                 }
2543             }
2544         }
2545         if (!start) {
2546             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2547                                                      _("Cannot find path between nodes."));
2548             return;
2549         }
2553         //Copy everything after 'end' to a new subpath
2554        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2555         for (curr=end ; curr ; curr=curr->n.other) {
2556             NRPathcode code = (NRPathcode) curr->code;
2557             if (curr == end)
2558                 code = NR_MOVETO;
2559             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2560                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2561         }
2563         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2564         for (curr = start->n.other ; curr  ; curr=next) {
2565             next = curr->n.other;
2566             sp_nodepath_node_destroy(curr);
2567         }
2569     }
2570     //###########################################
2571     //# END EDITS
2572     //###########################################
2574     //clean up the nodepath (such as for trivial subpaths)
2575     sp_nodepath_cleanup(nodepath);
2577     sp_nodepath_update_handles(nodepath);
2579     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2581     sp_nodepath_update_statusbar(nodepath);
2584 /**
2585  * Call sp_nodepath_set_line() for all selected segments.
2586  */
2587 void
2588 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2590     if (nodepath == NULL) return;
2592     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2593        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2594         g_assert(n->selected);
2595         if (n->p.other && n->p.other->selected) {
2596             sp_nodepath_set_line_type(n, code);
2597         }
2598     }
2600     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2603 /**
2604  * Call sp_nodepath_convert_node_type() for all selected nodes.
2605  */
2606 void
2607 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2609     if (nodepath == NULL) return;
2611     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2613     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2614         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2615     }
2617     sp_nodepath_update_repr(nodepath, _("Change node type"));
2620 /**
2621  * Change select status of node, update its own and neighbour handles.
2622  */
2623 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2625     node->selected = selected;
2627     if (selected) {
2628         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2629         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2630         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2631         sp_knot_update_ctrl(node->knot);
2632     } else {
2633         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2634         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2635         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2636         sp_knot_update_ctrl(node->knot);
2637     }
2639     sp_node_update_handles(node);
2640     if (node->n.other) sp_node_update_handles(node->n.other);
2641     if (node->p.other) sp_node_update_handles(node->p.other);
2644 /**
2645 \brief Select a node
2646 \param node     The node to select
2647 \param incremental   If true, add to selection, otherwise deselect others
2648 \param override   If true, always select this node, otherwise toggle selected status
2649 */
2650 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2652     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2654     if (incremental) {
2655         if (override) {
2656             if (!g_list_find(nodepath->selected, node)) {
2657                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2658             }
2659             sp_node_set_selected(node, TRUE);
2660         } else { // toggle
2661             if (node->selected) {
2662                 g_assert(g_list_find(nodepath->selected, node));
2663                 nodepath->selected = g_list_remove(nodepath->selected, node);
2664             } else {
2665                 g_assert(!g_list_find(nodepath->selected, node));
2666                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2667             }
2668             sp_node_set_selected(node, !node->selected);
2669         }
2670     } else {
2671         sp_nodepath_deselect(nodepath);
2672         nodepath->selected = g_list_prepend(nodepath->selected, node);
2673         sp_node_set_selected(node, TRUE);
2674     }
2676     sp_nodepath_update_statusbar(nodepath);
2680 /**
2681 \brief Deselect all nodes in the nodepath
2682 */
2683 void
2684 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2686     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2688     while (nodepath->selected) {
2689         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2690         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2691     }
2692     sp_nodepath_update_statusbar(nodepath);
2695 /**
2696 \brief Select or invert selection of all nodes in the nodepath
2697 */
2698 void
2699 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2701     if (!nodepath) return;
2703     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2704        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2705         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2706            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2707            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2708         }
2709     }
2712 /**
2713  * If nothing selected, does the same as sp_nodepath_select_all();
2714  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2715  * (i.e., similar to "select all in layer", with the "selected" subpaths
2716  * being treated as "layers" in the path).
2717  */
2718 void
2719 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2721     if (!nodepath) return;
2723     if (g_list_length (nodepath->selected) == 0) {
2724         sp_nodepath_select_all (nodepath, invert);
2725         return;
2726     }
2728     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2729     GSList *subpaths = NULL;
2731     for (GList *l = copy; l != NULL; l = l->next) {
2732         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2733         Inkscape::NodePath::SubPath *subpath = n->subpath;
2734         if (!g_slist_find (subpaths, subpath))
2735             subpaths = g_slist_prepend (subpaths, subpath);
2736     }
2738     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2739         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2740         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2741             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2742             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2743         }
2744     }
2746     g_slist_free (subpaths);
2747     g_list_free (copy);
2750 /**
2751  * \brief Select the node after the last selected; if none is selected,
2752  * select the first within path.
2753  */
2754 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2756     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2758    Inkscape::NodePath::Node *last = NULL;
2759     if (nodepath->selected) {
2760         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2761            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2762             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2763             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2764                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2765                 if (node->selected) {
2766                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2767                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2768                             if (spl->next) { // there's a next subpath
2769                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2770                                 last = subpath_next->first;
2771                             } else if (spl->prev) { // there's a previous subpath
2772                                 last = NULL; // to be set later to the first node of first subpath
2773                             } else {
2774                                 last = node->n.other;
2775                             }
2776                         } else {
2777                             last = node->n.other;
2778                         }
2779                     } else {
2780                         if (node->n.other) {
2781                             last = node->n.other;
2782                         } else {
2783                             if (spl->next) { // there's a next subpath
2784                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2785                                 last = subpath_next->first;
2786                             } else if (spl->prev) { // there's a previous subpath
2787                                 last = NULL; // to be set later to the first node of first subpath
2788                             } else {
2789                                 last = (Inkscape::NodePath::Node *) subpath->first;
2790                             }
2791                         }
2792                     }
2793                 }
2794             }
2795         }
2796         sp_nodepath_deselect(nodepath);
2797     }
2799     if (last) { // there's at least one more node after selected
2800         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2801     } else { // no more nodes, select the first one in first subpath
2802        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2803         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2804     }
2807 /**
2808  * \brief Select the node before the first selected; if none is selected,
2809  * select the last within path
2810  */
2811 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2813     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2815    Inkscape::NodePath::Node *last = NULL;
2816     if (nodepath->selected) {
2817         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2818            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2819             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2820                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2821                 if (node->selected) {
2822                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2823                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2824                             if (spl->prev) { // there's a prev subpath
2825                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2826                                 last = subpath_prev->last;
2827                             } else if (spl->next) { // there's a next subpath
2828                                 last = NULL; // to be set later to the last node of last subpath
2829                             } else {
2830                                 last = node->p.other;
2831                             }
2832                         } else {
2833                             last = node->p.other;
2834                         }
2835                     } else {
2836                         if (node->p.other) {
2837                             last = node->p.other;
2838                         } else {
2839                             if (spl->prev) { // there's a prev subpath
2840                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2841                                 last = subpath_prev->last;
2842                             } else if (spl->next) { // there's a next subpath
2843                                 last = NULL; // to be set later to the last node of last subpath
2844                             } else {
2845                                 last = (Inkscape::NodePath::Node *) subpath->last;
2846                             }
2847                         }
2848                     }
2849                 }
2850             }
2851         }
2852         sp_nodepath_deselect(nodepath);
2853     }
2855     if (last) { // there's at least one more node before selected
2856         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2857     } else { // no more nodes, select the last one in last subpath
2858         GList *spl = g_list_last(nodepath->subpaths);
2859        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2860         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2861     }
2864 /**
2865  * \brief Select all nodes that are within the rectangle.
2866  */
2867 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2869     if (!incremental) {
2870         sp_nodepath_deselect(nodepath);
2871     }
2873     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2874        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2875         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2876            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2878             if (b.contains(node->pos)) {
2879                 sp_nodepath_node_select(node, TRUE, TRUE);
2880             }
2881         }
2882     }
2886 void
2887 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2889     g_assert (n);
2890     g_assert (nodepath);
2891     g_assert (n->subpath->nodepath == nodepath);
2893     if (g_list_length (nodepath->selected) == 0) {
2894         if (grow > 0) {
2895             sp_nodepath_node_select(n, TRUE, TRUE);
2896         }
2897         return;
2898     }
2900     if (g_list_length (nodepath->selected) == 1) {
2901         if (grow < 0) {
2902             sp_nodepath_deselect (nodepath);
2903             return;
2904         }
2905     }
2907         double n_sel_range = 0, p_sel_range = 0;
2908             Inkscape::NodePath::Node *farthest_n_node = n;
2909             Inkscape::NodePath::Node *farthest_p_node = n;
2911         // Calculate ranges
2912         {
2913             double n_range = 0, p_range = 0;
2914             bool n_going = true, p_going = true;
2915             Inkscape::NodePath::Node *n_node = n;
2916             Inkscape::NodePath::Node *p_node = n;
2917             do {
2918                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2919                 if (n_node && n_going)
2920                     n_node = n_node->n.other;
2921                 if (n_node == NULL) {
2922                     n_going = false;
2923                 } else {
2924                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2925                     if (n_node->selected) {
2926                         n_sel_range = n_range;
2927                         farthest_n_node = n_node;
2928                     }
2929                     if (n_node == p_node) {
2930                         n_going = false;
2931                         p_going = false;
2932                     }
2933                 }
2934                 if (p_node && p_going)
2935                     p_node = p_node->p.other;
2936                 if (p_node == NULL) {
2937                     p_going = false;
2938                 } else {
2939                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2940                     if (p_node->selected) {
2941                         p_sel_range = p_range;
2942                         farthest_p_node = p_node;
2943                     }
2944                     if (p_node == n_node) {
2945                         n_going = false;
2946                         p_going = false;
2947                     }
2948                 }
2949             } while (n_going || p_going);
2950         }
2952     if (grow > 0) {
2953         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2954                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2955         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2956                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2957         }
2958     } else {
2959         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2960                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2961         } else if (farthest_p_node && farthest_p_node->selected) {
2962                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2963         }
2964     }
2967 void
2968 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2970     g_assert (n);
2971     g_assert (nodepath);
2972     g_assert (n->subpath->nodepath == nodepath);
2974     if (g_list_length (nodepath->selected) == 0) {
2975         if (grow > 0) {
2976             sp_nodepath_node_select(n, TRUE, TRUE);
2977         }
2978         return;
2979     }
2981     if (g_list_length (nodepath->selected) == 1) {
2982         if (grow < 0) {
2983             sp_nodepath_deselect (nodepath);
2984             return;
2985         }
2986     }
2988     Inkscape::NodePath::Node *farthest_selected = NULL;
2989     double farthest_dist = 0;
2991     Inkscape::NodePath::Node *closest_unselected = NULL;
2992     double closest_dist = NR_HUGE;
2994     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2995        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2996         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2997            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2998            if (node == n)
2999                continue;
3000            if (node->selected) {
3001                if (NR::L2(node->pos - n->pos) > farthest_dist) {
3002                    farthest_dist = NR::L2(node->pos - n->pos);
3003                    farthest_selected = node;
3004                }
3005            } else {
3006                if (NR::L2(node->pos - n->pos) < closest_dist) {
3007                    closest_dist = NR::L2(node->pos - n->pos);
3008                    closest_unselected = node;
3009                }
3010            }
3011         }
3012     }
3014     if (grow > 0) {
3015         if (closest_unselected) {
3016             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3017         }
3018     } else {
3019         if (farthest_selected) {
3020             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3021         }
3022     }
3026 /**
3027 \brief  Saves all nodes' and handles' current positions in their origin members
3028 */
3029 void
3030 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3032     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3033        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3034         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3035            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3036            n->origin = n->pos;
3037            n->p.origin = n->p.pos;
3038            n->n.origin = n->n.pos;
3039         }
3040     }
3043 /**
3044 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3045 */
3046 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3048     if (!nodepath->selected) {
3049         return NULL;
3050     }
3052     GList *r = NULL;
3053     guint i = 0;
3054     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3055        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3056         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3057            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3058             i++;
3059             if (node->selected) {
3060                 r = g_list_append(r, GINT_TO_POINTER(i));
3061             }
3062         }
3063     }
3064     return r;
3067 /**
3068 \brief  Restores selection by selecting nodes whose positions are in the list
3069 */
3070 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3072     sp_nodepath_deselect(nodepath);
3074     guint i = 0;
3075     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3076        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3077         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3078            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3079             i++;
3080             if (g_list_find(r, GINT_TO_POINTER(i))) {
3081                 sp_nodepath_node_select(node, TRUE, TRUE);
3082             }
3083         }
3084     }
3088 /**
3089 \brief Adjusts handle according to node type and line code.
3090 */
3091 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3093     g_assert(node);
3095    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3096    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3098    // nothing to do if we are an end node
3099     if (me->other == NULL) return;
3100     if (other->other == NULL) return;
3102     // nothing to do if we are a cusp node
3103     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3105     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3106     NRPathcode mecode;
3107     if (which_adjust == 1) {
3108         mecode = (NRPathcode)me->other->code;
3109     } else {
3110         mecode = (NRPathcode)node->code;
3111     }
3112     if (mecode == NR_LINETO) return;
3114     if (sp_node_side_is_line(node, other)) {
3115         // other is a line, and we are either smooth or symm
3116        Inkscape::NodePath::Node *othernode = other->other;
3117         double len = NR::L2(me->pos - node->pos);
3118         NR::Point delta = node->pos - othernode->pos;
3119         double linelen = NR::L2(delta);
3120         if (linelen < 1e-18)
3121             return;
3122         me->pos = node->pos + (len / linelen)*delta;
3123         return;
3124     }
3126     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3127         // symmetrize 
3128         me->pos = 2 * node->pos - other->pos;
3129         return;
3130     } else {
3131         // smoothify
3132         double len = NR::L2(me->pos - node->pos);
3133         NR::Point delta = other->pos - node->pos;
3134         double otherlen = NR::L2(delta);
3135         if (otherlen < 1e-18) return;
3136         me->pos = node->pos - (len / otherlen) * delta;
3137     }
3140 /**
3141  \brief Adjusts both handles according to node type and line code
3142  */
3143 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3145     g_assert(node);
3147     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3149     /* we are either smooth or symm */
3151     if (node->p.other == NULL) return;
3152     if (node->n.other == NULL) return;
3154     if (sp_node_side_is_line(node, &node->p)) {
3155         sp_node_adjust_handle(node, 1);
3156         return;
3157     }
3159     if (sp_node_side_is_line(node, &node->n)) {
3160         sp_node_adjust_handle(node, -1);
3161         return;
3162     }
3164     /* both are curves */
3165     NR::Point const delta( node->n.pos - node->p.pos );
3167     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3168         node->p.pos = node->pos - delta / 2;
3169         node->n.pos = node->pos + delta / 2;
3170         return;
3171     }
3173     /* We are smooth */
3174     double plen = NR::L2(node->p.pos - node->pos);
3175     if (plen < 1e-18) return;
3176     double nlen = NR::L2(node->n.pos - node->pos);
3177     if (nlen < 1e-18) return;
3178     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3179     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3182 /**
3183  * Node event callback.
3184  */
3185 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3187     gboolean ret = FALSE;
3188     switch (event->type) {
3189         case GDK_ENTER_NOTIFY:
3190             Inkscape::NodePath::Path::active_node = n;
3191             break;
3192         case GDK_LEAVE_NOTIFY:
3193             Inkscape::NodePath::Path::active_node = NULL;
3194             break;
3195         case GDK_SCROLL:
3196             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3197                 switch (event->scroll.direction) {
3198                     case GDK_SCROLL_UP:
3199                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3200                         break;
3201                     case GDK_SCROLL_DOWN:
3202                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3203                         break;
3204                     default:
3205                         break;
3206                 }
3207                 ret = TRUE;
3208             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3209                 switch (event->scroll.direction) {
3210                     case GDK_SCROLL_UP:
3211                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3212                         break;
3213                     case GDK_SCROLL_DOWN:
3214                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3215                         break;
3216                     default:
3217                         break;
3218                 }
3219                 ret = TRUE;
3220             }
3221             break;
3222         case GDK_KEY_PRESS:
3223             switch (get_group0_keyval (&event->key)) {
3224                 case GDK_space:
3225                     if (event->key.state & GDK_BUTTON1_MASK) {
3226                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3227                         stamp_repr(nodepath);
3228                         ret = TRUE;
3229                     }
3230                     break;
3231                 case GDK_Page_Up:
3232                     if (event->key.state & GDK_CONTROL_MASK) {
3233                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3234                     } else {
3235                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3236                     }
3237                     break;
3238                 case GDK_Page_Down:
3239                     if (event->key.state & GDK_CONTROL_MASK) {
3240                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3241                     } else {
3242                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3243                     }
3244                     break;
3245                 default:
3246                     break;
3247             }
3248             break;
3249         default:
3250             break;
3251     }
3253     return ret;
3256 /**
3257  * Handle keypress on node; directly called.
3258  */
3259 gboolean node_key(GdkEvent *event)
3261     Inkscape::NodePath::Path *np;
3263     // there is no way to verify nodes so set active_node to nil when deleting!!
3264     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3266     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3267         gint ret = FALSE;
3268         switch (get_group0_keyval (&event->key)) {
3269             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3270             case GDK_BackSpace:
3271                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3272                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3273                 sp_nodepath_update_repr(np, _("Delete node"));
3274                 Inkscape::NodePath::Path::active_node = NULL;
3275                 ret = TRUE;
3276                 break;
3277             case GDK_c:
3278                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3279                 ret = TRUE;
3280                 break;
3281             case GDK_s:
3282                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3283                 ret = TRUE;
3284                 break;
3285             case GDK_y:
3286                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3287                 ret = TRUE;
3288                 break;
3289             case GDK_b:
3290                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3291                 ret = TRUE;
3292                 break;
3293         }
3294         return ret;
3295     }
3296     return FALSE;
3299 /**
3300  * Mouseclick on node callback.
3301  */
3302 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3304    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3306     if (state & GDK_CONTROL_MASK) {
3307         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3309         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3310             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3311                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3312             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3313                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3314             } else {
3315                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3316             }
3317             sp_nodepath_update_repr(nodepath, _("Change node type"));
3318             sp_nodepath_update_statusbar(nodepath);
3320         } else { //ctrl+alt+click: delete node
3321             GList *node_to_delete = NULL;
3322             node_to_delete = g_list_append(node_to_delete, n);
3323             sp_node_delete_preserve(node_to_delete);
3324         }
3326     } else {
3327         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3328     }
3331 /**
3332  * Mouse grabbed node callback.
3333  */
3334 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3336    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3338     if (!n->selected) {
3339         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3340     }
3342     n->is_dragging = true;
3343     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3345     sp_nodepath_remember_origins (n->subpath->nodepath);
3348 /**
3349  * Mouse ungrabbed node callback.
3350  */
3351 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3353    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3355    n->dragging_out = NULL;
3356    n->is_dragging = false;
3357    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3359    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3362 /**
3363  * The point on a line, given by its angle, closest to the given point.
3364  * \param p  A point.
3365  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3366  * \param closest  Pointer to the point struct where the result is stored.
3367  * \todo FIXME: use dot product perhaps?
3368  */
3369 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3371     if (a == HUGE_VAL) { // vertical
3372         *closest = NR::Point(0, (*p)[NR::Y]);
3373     } else {
3374         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3375         (*closest)[NR::Y] = a * (*closest)[NR::X];
3376     }
3379 /**
3380  * Distance from the point to a line given by its angle.
3381  * \param p  A point.
3382  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3383  */
3384 static double point_line_distance(NR::Point *p, double a)
3386     NR::Point c;
3387     point_line_closest(p, a, &c);
3388     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]));
3391 /**
3392  * Callback for node "request" signal.
3393  * \todo fixme: This goes to "moved" event? (lauris)
3394  */
3395 static gboolean
3396 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3398     double yn, xn, yp, xp;
3399     double an, ap, na, pa;
3400     double d_an, d_ap, d_na, d_pa;
3401     gboolean collinear = FALSE;
3402     NR::Point c;
3403     NR::Point pr;
3405     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3407     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3409     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3410     if ( (!n->subpath->nodepath->straight_path) &&
3411          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3412            || n->dragging_out ) )
3413     {
3414        NR::Point mouse = (*p);
3416        if (!n->dragging_out) {
3417            // This is the first drag-out event; find out which handle to drag out
3418            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3419            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3421            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3422                return FALSE;
3424            Inkscape::NodePath::NodeSide *opposite;
3425            if (appr_p > appr_n) { // closer to p
3426                n->dragging_out = &n->p;
3427                opposite = &n->n;
3428                n->code = NR_CURVETO;
3429            } else if (appr_p < appr_n) { // closer to n
3430                n->dragging_out = &n->n;
3431                opposite = &n->p;
3432                n->n.other->code = NR_CURVETO;
3433            } else { // p and n nodes are the same
3434                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3435                    n->dragging_out = &n->p;
3436                    opposite = &n->n;
3437                    n->code = NR_CURVETO;
3438                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3439                    n->dragging_out = &n->n;
3440                    opposite = &n->p;
3441                    n->n.other->code = NR_CURVETO;
3442                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3443                    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);
3444                    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);
3445                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3446                        n->dragging_out = &n->n;
3447                        opposite = &n->p;
3448                        n->n.other->code = NR_CURVETO;
3449                    } else { // closer to other's n handle
3450                        n->dragging_out = &n->p;
3451                        opposite = &n->n;
3452                        n->code = NR_CURVETO;
3453                    }
3454                }
3455            }
3457            // if there's another handle, make sure the one we drag out starts parallel to it
3458            if (opposite->pos != n->pos) {
3459                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3460            }
3462            // knots might not be created yet!
3463            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3464            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3465        }
3467        // pass this on to the handle-moved callback
3468        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3469        sp_node_update_handles(n);
3470        return TRUE;
3471    }
3473     if (state & GDK_CONTROL_MASK) { // constrained motion
3475         // calculate relative distances of handles
3476         // n handle:
3477         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3478         xn = n->n.pos[NR::X] - n->pos[NR::X];
3479         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3480         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3481             if (n->n.other) { // if there is the next point
3482                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3483                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3484                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3485             }
3486         }
3487         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3488         if (yn < 0) { xn = -xn; yn = -yn; }
3490         // p handle:
3491         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3492         xp = n->p.pos[NR::X] - n->pos[NR::X];
3493         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3494         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3495             if (n->p.other) {
3496                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3497                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3498                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3499             }
3500         }
3501         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3502         if (yp < 0) { xp = -xp; yp = -yp; }
3504         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3505             // sliding on handles, only if at least one of the handles is non-vertical
3506             // (otherwise it's the same as ctrl+drag anyway)
3508             // calculate angles of the handles
3509             if (xn == 0) {
3510                 if (yn == 0) { // no handle, consider it the continuation of the other one
3511                     an = 0;
3512                     collinear = TRUE;
3513                 }
3514                 else an = 0; // vertical; set the angle to horizontal
3515             } else an = yn/xn;
3517             if (xp == 0) {
3518                 if (yp == 0) { // no handle, consider it the continuation of the other one
3519                     ap = an;
3520                 }
3521                 else ap = 0; // vertical; set the angle to horizontal
3522             } else  ap = yp/xp;
3524             if (collinear) an = ap;
3526             // angles of the perpendiculars; HUGE_VAL means vertical
3527             if (an == 0) na = HUGE_VAL; else na = -1/an;
3528             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3530             // mouse point relative to the node's original pos
3531             pr = (*p) - n->origin;
3533             // distances to the four lines (two handles and two perpendiculars)
3534             d_an = point_line_distance(&pr, an);
3535             d_na = point_line_distance(&pr, na);
3536             d_ap = point_line_distance(&pr, ap);
3537             d_pa = point_line_distance(&pr, pa);
3539             // find out which line is the closest, save its closest point in c
3540             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3541                 point_line_closest(&pr, an, &c);
3542             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3543                 point_line_closest(&pr, ap, &c);
3544             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3545                 point_line_closest(&pr, na, &c);
3546             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3547                 point_line_closest(&pr, pa, &c);
3548             }
3550             // move the node to the closest point
3551             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3552                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3553                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y], 
3554                                             true);
3556         } else {  // constraining to hor/vert
3558             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3559                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3560                                                 (*p)[NR::X] - n->pos[NR::X], 
3561                                                 n->origin[NR::Y] - n->pos[NR::Y],
3562                                                 true, 
3563                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::X]));
3564             } else { // snap to vert
3565                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3566                                                 n->origin[NR::X] - n->pos[NR::X],
3567                                                 (*p)[NR::Y] - n->pos[NR::Y],
3568                                                 true,
3569                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::Y]));
3570             }
3571         }
3572     } else { // move freely
3573         if (n->is_dragging) {
3574             if (state & GDK_MOD1_MASK) { // sculpt
3575                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3576             } else {
3577                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3578                                             (*p)[NR::X] - n->pos[NR::X],
3579                                             (*p)[NR::Y] - n->pos[NR::Y],
3580                                             (state & GDK_SHIFT_MASK) == 0);
3581             }
3582         }
3583     }
3585     n->subpath->nodepath->desktop->scroll_to_point(p);
3587     return TRUE;
3590 /**
3591  * Node handle clicked callback.
3592  */
3593 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3595    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3597     if (state & GDK_CONTROL_MASK) { // "delete" handle
3598         if (n->p.knot == knot) {
3599             n->p.pos = n->pos;
3600         } else if (n->n.knot == knot) {
3601             n->n.pos = n->pos;
3602         }
3603         sp_node_update_handles(n);
3604         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3605         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3606         sp_nodepath_update_statusbar(nodepath);
3608     } else { // just select or add to selection, depending in Shift
3609         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3610     }
3613 /**
3614  * Node handle grabbed callback.
3615  */
3616 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3618    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3620     if (!n->selected) {
3621         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3622     }
3624     // remember the origin point of the handle
3625     if (n->p.knot == knot) {
3626         n->p.origin_radial = n->p.pos - n->pos;
3627     } else if (n->n.knot == knot) {
3628         n->n.origin_radial = n->n.pos - n->pos;
3629     } else {
3630         g_assert_not_reached();
3631     }
3633     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3636 /**
3637  * Node handle ungrabbed callback.
3638  */
3639 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3641    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3643     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3644     if (n->p.knot == knot) {
3645         n->p.origin_radial.a = 0;
3646         sp_knot_set_position(knot, &n->p.pos, state);
3647     } else if (n->n.knot == knot) {
3648         n->n.origin_radial.a = 0;
3649         sp_knot_set_position(knot, &n->n.pos, state);
3650     } else {
3651         g_assert_not_reached();
3652     }
3654     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3657 /**
3658  * Node handle "request" signal callback.
3659  */
3660 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint /*state*/, gpointer data)
3662     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3664     Inkscape::NodePath::NodeSide *me, *opposite;
3665     gint which;
3666     if (n->p.knot == knot) {
3667         me = &n->p;
3668         opposite = &n->n;
3669         which = -1;
3670     } else if (n->n.knot == knot) {
3671         me = &n->n;
3672         opposite = &n->p;
3673         which = 1;
3674     } else {
3675         me = opposite = NULL;
3676         which = 0;
3677         g_assert_not_reached();
3678     }
3680     SPDesktop *desktop = n->subpath->nodepath->desktop;
3681     SnapManager &m = desktop->namedview->snap_manager;
3682     m.setup(desktop, n->subpath->nodepath->item);
3683     Inkscape::SnappedPoint s ;
3685     Inkscape::NodePath::Node *othernode = opposite->other;
3686     if (othernode) {
3687         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3688             /* We are smooth node adjacent with line */
3689             NR::Point const delta = *p - n->pos;
3690             NR::Coord const len = NR::L2(delta);
3691             Inkscape::NodePath::Node *othernode = opposite->other;
3692             NR::Point const ndelta = n->pos - othernode->pos;
3693             NR::Coord const linelen = NR::L2(ndelta);
3694             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3695                 NR::Coord const scal = dot(delta, ndelta) / linelen;
3696                 (*p) = n->pos + (scal / linelen) * ndelta;
3697             }
3698             s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta));
3699         } else {
3700             s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3701         }
3702     } else {
3703         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3704     }
3705     
3706     s.getPoint(*p);
3707     
3708     sp_node_adjust_handle(n, -which);
3710     return FALSE;
3713 /**
3714  * Node handle moved callback.
3715  */
3716 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3718    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3720    Inkscape::NodePath::NodeSide *me;
3721    Inkscape::NodePath::NodeSide *other;
3722     if (n->p.knot == knot) {
3723         me = &n->p;
3724         other = &n->n;
3725     } else if (n->n.knot == knot) {
3726         me = &n->n;
3727         other = &n->p;
3728     } else {
3729         me = NULL;
3730         other = NULL;
3731         g_assert_not_reached();
3732     }
3734     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3735     Radial rme(me->pos - n->pos);
3736     Radial rother(other->pos - n->pos);
3737     Radial rnew(*p - n->pos);
3739     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3740         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3741         /* 0 interpreted as "no snapping". */
3743         // 1. Snap to the closest PI/snaps angle, starting from zero.
3744         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3746         // 2. Snap to the original angle, its opposite and perpendiculars
3747         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3748             /* The closest PI/2 angle, starting from original angle */
3749             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3751             // Snap to the closest.
3752             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3753                        ? a_snapped
3754                        : a_ortho );
3755         }
3757         // 3. Snap to the angle of the opposite line, if any
3758         Inkscape::NodePath::Node *othernode = other->other;
3759         if (othernode) {
3760             NR::Point other_to_snap(0,0);
3761             if (sp_node_side_is_line(n, other)) {
3762                 other_to_snap = othernode->pos - n->pos;
3763             } else {
3764                 other_to_snap = other->pos - n->pos;
3765             }
3766             if (NR::L2(other_to_snap) > 1e-3) {
3767                 Radial rother_to_snap(other_to_snap);
3768                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
3769                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
3771                 // Snap to the closest.
3772                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
3773                        ? a_snapped
3774                        : a_oppo );
3775             }
3776         }
3778         rnew.a = a_snapped;
3779     }
3781     if (state & GDK_MOD1_MASK) {
3782         // lock handle length
3783         rnew.r = me->origin_radial.r;
3784     }
3786     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3787         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3788         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3789         rother.a += rnew.a - rme.a;
3790         other->pos = NR::Point(rother) + n->pos;
3791         if (other->knot) {
3792             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3793             sp_knot_moveto(other->knot, &other->pos);
3794         }
3795     }
3797     me->pos = NR::Point(rnew) + n->pos;
3798     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3800     // move knot, but without emitting the signal:
3801     // we cannot emit a "moved" signal because we're now processing it
3802     sp_knot_moveto(me->knot, &(me->pos));
3804     update_object(n->subpath->nodepath);
3806     /* status text */
3807     SPDesktop *desktop = n->subpath->nodepath->desktop;
3808     if (!desktop) return;
3809     SPEventContext *ec = desktop->event_context;
3810     if (!ec) return;
3811     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3812     if (!mc) return;
3814     double degrees = 180 / M_PI * rnew.a;
3815     if (degrees > 180) degrees -= 360;
3816     if (degrees < -180) degrees += 360;
3817     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3818         degrees = angle_to_compass (degrees);
3820     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3822     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
3823          _("<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);
3825     g_string_free(length, TRUE);
3828 /**
3829  * Node handle event callback.
3830  */
3831 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3833     gboolean ret = FALSE;
3834     switch (event->type) {
3835         case GDK_KEY_PRESS:
3836             switch (get_group0_keyval (&event->key)) {
3837                 case GDK_space:
3838                     if (event->key.state & GDK_BUTTON1_MASK) {
3839                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3840                         stamp_repr(nodepath);
3841                         ret = TRUE;
3842                     }
3843                     break;
3844                 default:
3845                     break;
3846             }
3847             break;
3848         case GDK_ENTER_NOTIFY:
3849             // we use an experimentally determined threshold that seems to work fine
3850             if (NR::L2(n->pos - knot->pos) < 0.75)
3851                 Inkscape::NodePath::Path::active_node = n;
3852             break;
3853         case GDK_LEAVE_NOTIFY:
3854             // we use an experimentally determined threshold that seems to work fine
3855             if (NR::L2(n->pos - knot->pos) < 0.75)
3856                 Inkscape::NodePath::Path::active_node = NULL;
3857             break;
3858         default:
3859             break;
3860     }
3862     return ret;
3865 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3866                                  Radial &rme, Radial &rother, gboolean const both)
3868     rme.a += angle;
3869     if ( both
3870          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3871          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3872     {
3873         rother.a += angle;
3874     }
3877 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3878                                         Radial &rme, Radial &rother, gboolean const both)
3880     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3882     gdouble r;
3883     if ( both
3884          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3885          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3886     {
3887         r = MAX(rme.r, rother.r);
3888     } else {
3889         r = rme.r;
3890     }
3892     gdouble const weird_angle = atan2(norm_angle, r);
3893 /* Bulia says norm_angle is just the visible distance that the
3894  * object's end must travel on the screen.  Left as 'angle' for want of
3895  * a better name.*/
3897     rme.a += weird_angle;
3898     if ( both
3899          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3900          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3901     {
3902         rother.a += weird_angle;
3903     }
3906 /**
3907  * Rotate one node.
3908  */
3909 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3911     Inkscape::NodePath::NodeSide *me, *other;
3912     bool both = false;
3914     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3915     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3917     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3918         me = &(n->p);
3919         other = &(n->n);
3920     } else if (!n->p.other) {
3921         me = &(n->n);
3922         other = &(n->p);
3923     } else {
3924         if (which > 0) { // right handle
3925             if (xn > xp) {
3926                 me = &(n->n);
3927                 other = &(n->p);
3928             } else {
3929                 me = &(n->p);
3930                 other = &(n->n);
3931             }
3932         } else if (which < 0){ // left handle
3933             if (xn <= xp) {
3934                 me = &(n->n);
3935                 other = &(n->p);
3936             } else {
3937                 me = &(n->p);
3938                 other = &(n->n);
3939             }
3940         } else { // both handles
3941             me = &(n->n);
3942             other = &(n->p);
3943             both = true;
3944         }
3945     }
3947     Radial rme(me->pos - n->pos);
3948     Radial rother(other->pos - n->pos);
3950     if (screen) {
3951         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3952     } else {
3953         node_rotate_one_internal (*n, angle, rme, rother, both);
3954     }
3956     me->pos = n->pos + NR::Point(rme);
3958     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3959         other->pos =  n->pos + NR::Point(rother);
3960     }
3962     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3963     // so here we just move all the knots without emitting move signals, for speed
3964     sp_node_update_handles(n, false);
3967 /**
3968  * Rotate selected nodes.
3969  */
3970 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3972     if (!nodepath || !nodepath->selected) return;
3974     if (g_list_length(nodepath->selected) == 1) {
3975        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3976         node_rotate_one (n, angle, which, screen);
3977     } else {
3978        // rotate as an object:
3980         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3981         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3982         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3983             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3984             box.expandTo (n->pos); // contain all selected nodes
3985         }
3987         gdouble rot;
3988         if (screen) {
3989             gdouble const zoom = nodepath->desktop->current_zoom();
3990             gdouble const zmove = angle / zoom;
3991             gdouble const r = NR::L2(box.max() - box.midpoint());
3992             rot = atan2(zmove, r);
3993         } else {
3994             rot = angle;
3995         }
3997         NR::Point rot_center;
3998         if (Inkscape::NodePath::Path::active_node == NULL)
3999             rot_center = box.midpoint();
4000         else
4001             rot_center = Inkscape::NodePath::Path::active_node->pos;
4003         NR::Matrix t =
4004             NR::Matrix (NR::translate(-rot_center)) *
4005             NR::Matrix (NR::rotate(rot)) *
4006             NR::Matrix (NR::translate(rot_center));
4008         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4009             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4010             n->pos *= t;
4011             n->n.pos *= t;
4012             n->p.pos *= t;
4013             sp_node_update_handles(n, false);
4014         }
4015     }
4017     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4020 /**
4021  * Scale one node.
4022  */
4023 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4025     bool both = false;
4026     Inkscape::NodePath::NodeSide *me, *other;
4028     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4029     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4031     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4032         me = &(n->p);
4033         other = &(n->n);
4034         n->code = NR_CURVETO;
4035     } else if (!n->p.other) {
4036         me = &(n->n);
4037         other = &(n->p);
4038         if (n->n.other)
4039             n->n.other->code = NR_CURVETO;
4040     } else {
4041         if (which > 0) { // right handle
4042             if (xn > xp) {
4043                 me = &(n->n);
4044                 other = &(n->p);
4045                 if (n->n.other)
4046                     n->n.other->code = NR_CURVETO;
4047             } else {
4048                 me = &(n->p);
4049                 other = &(n->n);
4050                 n->code = NR_CURVETO;
4051             }
4052         } else if (which < 0){ // left handle
4053             if (xn <= xp) {
4054                 me = &(n->n);
4055                 other = &(n->p);
4056                 if (n->n.other)
4057                     n->n.other->code = NR_CURVETO;
4058             } else {
4059                 me = &(n->p);
4060                 other = &(n->n);
4061                 n->code = NR_CURVETO;
4062             }
4063         } else { // both handles
4064             me = &(n->n);
4065             other = &(n->p);
4066             both = true;
4067             n->code = NR_CURVETO;
4068             if (n->n.other)
4069                 n->n.other->code = NR_CURVETO;
4070         }
4071     }
4073     Radial rme(me->pos - n->pos);
4074     Radial rother(other->pos - n->pos);
4076     rme.r += grow;
4077     if (rme.r < 0) rme.r = 0;
4078     if (rme.a == HUGE_VAL) {
4079         if (me->other) { // if direction is unknown, initialize it towards the next node
4080             Radial rme_next(me->other->pos - n->pos);
4081             rme.a = rme_next.a;
4082         } else { // if there's no next, initialize to 0
4083             rme.a = 0;
4084         }
4085     }
4086     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4087         rother.r += grow;
4088         if (rother.r < 0) rother.r = 0;
4089         if (rother.a == HUGE_VAL) {
4090             rother.a = rme.a + M_PI;
4091         }
4092     }
4094     me->pos = n->pos + NR::Point(rme);
4096     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4097         other->pos = n->pos + NR::Point(rother);
4098     }
4100     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4101     // so here we just move all the knots without emitting move signals, for speed
4102     sp_node_update_handles(n, false);
4105 /**
4106  * Scale selected nodes.
4107  */
4108 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4110     if (!nodepath || !nodepath->selected) return;
4112     if (g_list_length(nodepath->selected) == 1) {
4113         // scale handles of the single selected node
4114         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4115         node_scale_one (n, grow, which);
4116     } else {
4117         // scale nodes as an "object":
4119         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4120         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4121         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4122             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4123             box.expandTo (n->pos); // contain all selected nodes
4124         }
4126         double scale = (box.maxExtent() + grow)/box.maxExtent();
4128         NR::Point scale_center;
4129         if (Inkscape::NodePath::Path::active_node == NULL)
4130             scale_center = box.midpoint();
4131         else
4132             scale_center = Inkscape::NodePath::Path::active_node->pos;
4134         NR::Matrix t =
4135             NR::Matrix (NR::translate(-scale_center)) *
4136             NR::Matrix (NR::scale(scale, scale)) *
4137             NR::Matrix (NR::translate(scale_center));
4139         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4140             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4141             n->pos *= t;
4142             n->n.pos *= t;
4143             n->p.pos *= t;
4144             sp_node_update_handles(n, false);
4145         }
4146     }
4148     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4151 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4153     if (!nodepath) return;
4154     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4157 /**
4158  * Flip selected nodes horizontally/vertically.
4159  */
4160 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
4162     if (!nodepath || !nodepath->selected) return;
4164     if (g_list_length(nodepath->selected) == 1 && !center) {
4165         // flip handles of the single selected node
4166         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4167         double temp = n->p.pos[axis];
4168         n->p.pos[axis] = n->n.pos[axis];
4169         n->n.pos[axis] = temp;
4170         sp_node_update_handles(n, false);
4171     } else {
4172         // scale nodes as an "object":
4174         NR::Rect box = sp_node_selected_bbox (nodepath);
4175         if (!center) {
4176             center = box.midpoint();
4177         }
4178         NR::Matrix t =
4179             NR::Matrix (NR::translate(- *center)) *
4180             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
4181             NR::Matrix (NR::translate(*center));
4183         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4184             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4185             n->pos *= t;
4186             n->n.pos *= t;
4187             n->p.pos *= t;
4188             sp_node_update_handles(n, false);
4189         }
4190     }
4192     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4195 NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4197     g_assert (nodepath->selected);
4199     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4200     NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4201     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4202         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4203         box.expandTo (n->pos); // contain all selected nodes
4204     }
4205     return box;
4208 //-----------------------------------------------
4209 /**
4210  * Return new subpath under given nodepath.
4211  */
4212 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4214     g_assert(nodepath);
4215     g_assert(nodepath->desktop);
4217    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4219     s->nodepath = nodepath;
4220     s->closed = FALSE;
4221     s->nodes = NULL;
4222     s->first = NULL;
4223     s->last = NULL;
4225     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4226     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4227     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4229     return s;
4232 /**
4233  * Destroy nodes in subpath, then subpath itself.
4234  */
4235 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4237     g_assert(subpath);
4238     g_assert(subpath->nodepath);
4239     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4241     while (subpath->nodes) {
4242         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4243     }
4245     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4247     g_free(subpath);
4250 /**
4251  * Link head to tail in subpath.
4252  */
4253 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4255     g_assert(!sp->closed);
4256     g_assert(sp->last != sp->first);
4257     g_assert(sp->first->code == NR_MOVETO);
4259     sp->closed = TRUE;
4261     //Link the head to the tail
4262     sp->first->p.other = sp->last;
4263     sp->last->n.other  = sp->first;
4264     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4265     sp->first          = sp->last;
4267     //Remove the extra end node
4268     sp_nodepath_node_destroy(sp->last->n.other);
4271 /**
4272  * Open closed (loopy) subpath at node.
4273  */
4274 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4276     g_assert(sp->closed);
4277     g_assert(n->subpath == sp);
4278     g_assert(sp->first == sp->last);
4280     /* We create new startpoint, current node will become last one */
4282    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4283                                                 &n->pos, &n->pos, &n->n.pos);
4286     sp->closed        = FALSE;
4288     //Unlink to make a head and tail
4289     sp->first         = new_path;
4290     sp->last          = n;
4291     n->n.other        = NULL;
4292     new_path->p.other = NULL;
4295 /**
4296  * Return new node in subpath with given properties.
4297  * \param pos Position of node.
4298  * \param ppos Handle position in previous direction
4299  * \param npos Handle position in previous direction
4300  */
4301 Inkscape::NodePath::Node *
4302 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)
4304     g_assert(sp);
4305     g_assert(sp->nodepath);
4306     g_assert(sp->nodepath->desktop);
4308     if (nodechunk == NULL)
4309         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4311     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4313     n->subpath  = sp;
4315     if (type != Inkscape::NodePath::NODE_NONE) {
4316         // use the type from sodipodi:nodetypes
4317         n->type = type;
4318     } else {
4319         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4320             // points are (almost) collinear
4321             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4322                 // endnode, or a node with a retracted handle
4323                 n->type = Inkscape::NodePath::NODE_CUSP;
4324             } else {
4325                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4326             }
4327         } else {
4328             n->type = Inkscape::NodePath::NODE_CUSP;
4329         }
4330     }
4332     n->code     = code;
4333     n->selected = FALSE;
4334     n->pos      = *pos;
4335     n->p.pos    = *ppos;
4336     n->n.pos    = *npos;
4338     n->dragging_out = NULL;
4340     Inkscape::NodePath::Node *prev;
4341     if (next) {
4342         //g_assert(g_list_find(sp->nodes, next));
4343         prev = next->p.other;
4344     } else {
4345         prev = sp->last;
4346     }
4348     if (prev)
4349         prev->n.other = n;
4350     else
4351         sp->first = n;
4353     if (next)
4354         next->p.other = n;
4355     else
4356         sp->last = n;
4358     n->p.other = prev;
4359     n->n.other = next;
4361     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"));
4362     sp_knot_set_position(n->knot, pos, 0);
4364     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4365     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4366     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4367     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4368     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4369     sp_knot_update_ctrl(n->knot);
4371     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4372     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4373     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4374     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4375     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4376     sp_knot_show(n->knot);
4378     // We only create handle knots and lines on demand
4379     n->p.knot = NULL;
4380     n->p.line = NULL;
4381     n->n.knot = NULL;
4382     n->n.line = NULL;
4384     sp->nodes = g_list_prepend(sp->nodes, n);
4386     return n;
4389 /**
4390  * Destroy node and its knots, link neighbors in subpath.
4391  */
4392 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4394     g_assert(node);
4395     g_assert(node->subpath);
4396     g_assert(SP_IS_KNOT(node->knot));
4398    Inkscape::NodePath::SubPath *sp = node->subpath;
4400     if (node->selected) { // first, deselect
4401         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4402         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4403     }
4405     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4407     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4408     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4409     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4410     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4411     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4412     g_object_unref(G_OBJECT(node->knot));
4414     if (node->p.knot) {
4415         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4416         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4417         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4418         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4419         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4420         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4421         g_object_unref(G_OBJECT(node->p.knot));
4422         node->p.knot = NULL;
4423     }
4425     if (node->n.knot) {
4426         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4427         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4428         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4429         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4430         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4431         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4432         g_object_unref(G_OBJECT(node->n.knot));
4433         node->n.knot = NULL;
4434     }
4436     if (node->p.line)
4437         gtk_object_destroy(GTK_OBJECT(node->p.line));
4438     if (node->n.line)
4439         gtk_object_destroy(GTK_OBJECT(node->n.line));
4441     if (sp->nodes) { // there are others nodes on the subpath
4442         if (sp->closed) {
4443             if (sp->first == node) {
4444                 g_assert(sp->last == node);
4445                 sp->first = node->n.other;
4446                 sp->last = sp->first;
4447             }
4448             node->p.other->n.other = node->n.other;
4449             node->n.other->p.other = node->p.other;
4450         } else {
4451             if (sp->first == node) {
4452                 sp->first = node->n.other;
4453                 sp->first->code = NR_MOVETO;
4454             }
4455             if (sp->last == node) sp->last = node->p.other;
4456             if (node->p.other) node->p.other->n.other = node->n.other;
4457             if (node->n.other) node->n.other->p.other = node->p.other;
4458         }
4459     } else { // this was the last node on subpath
4460         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4461     }
4463     g_mem_chunk_free(nodechunk, node);
4466 /**
4467  * Returns one of the node's two sides.
4468  * \param which Indicates which side.
4469  * \return Pointer to previous node side if which==-1, next if which==1.
4470  */
4471 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4473     g_assert(node);
4475     switch (which) {
4476         case -1:
4477             return &node->p;
4478         case 1:
4479             return &node->n;
4480         default:
4481             break;
4482     }
4484     g_assert_not_reached();
4486     return NULL;
4489 /**
4490  * Return the other side of the node, given one of its sides.
4491  */
4492 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4494     g_assert(node);
4496     if (me == &node->p) return &node->n;
4497     if (me == &node->n) return &node->p;
4499     g_assert_not_reached();
4501     return NULL;
4504 /**
4505  * Return NRPathcode on the given side of the node.
4506  */
4507 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4509     g_assert(node);
4511     if (me == &node->p) {
4512         if (node->p.other) return (NRPathcode)node->code;
4513         return NR_MOVETO;
4514     }
4516     if (me == &node->n) {
4517         if (node->n.other) return (NRPathcode)node->n.other->code;
4518         return NR_MOVETO;
4519     }
4521     g_assert_not_reached();
4523     return NR_END;
4526 /**
4527  * Return node with the given index
4528  */
4529 Inkscape::NodePath::Node *
4530 sp_nodepath_get_node_by_index(int index)
4532     Inkscape::NodePath::Node *e = NULL;
4534     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4535     if (!nodepath) {
4536         return e;
4537     }
4539     //find segment
4540     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4542         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4543         int n = g_list_length(sp->nodes);
4544         if (sp->closed) {
4545             n++;
4546         }
4548         //if the piece belongs to this subpath grab it
4549         //otherwise move onto the next subpath
4550         if (index < n) {
4551             e = sp->first;
4552             for (int i = 0; i < index; ++i) {
4553                 e = e->n.other;
4554             }
4555             break;
4556         } else {
4557             if (sp->closed) {
4558                 index -= (n+1);
4559             } else {
4560                 index -= n;
4561             }
4562         }
4563     }
4565     return e;
4568 /**
4569  * Returns plain text meaning of node type.
4570  */
4571 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4573     unsigned retracted = 0;
4574     bool endnode = false;
4576     for (int which = -1; which <= 1; which += 2) {
4577         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4578         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4579             retracted ++;
4580         if (!side->other)
4581             endnode = true;
4582     }
4584     if (retracted == 0) {
4585         if (endnode) {
4586                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4587                 return _("end node");
4588         } else {
4589             switch (node->type) {
4590                 case Inkscape::NodePath::NODE_CUSP:
4591                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4592                     return _("cusp");
4593                 case Inkscape::NodePath::NODE_SMOOTH:
4594                     // TRANSLATORS: "smooth" is an adjective here
4595                     return _("smooth");
4596                 case Inkscape::NodePath::NODE_SYMM:
4597                     return _("symmetric");
4598             }
4599         }
4600     } else if (retracted == 1) {
4601         if (endnode) {
4602             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4603             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4604         } else {
4605             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4606         }
4607     } else {
4608         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4609     }
4611     return NULL;
4614 /**
4615  * Handles content of statusbar as long as node tool is active.
4616  */
4617 void
4618 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4620     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");
4621     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4623     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4624     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4625     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4626     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4628     SPDesktop *desktop = NULL;
4629     if (nodepath) {
4630         desktop = nodepath->desktop;
4631     } else {
4632         desktop = SP_ACTIVE_DESKTOP;
4633     }
4635     SPEventContext *ec = desktop->event_context;
4636     if (!ec) return;
4637     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4638     if (!mc) return;
4640     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4642     if (selected_nodes == 0) {
4643         Inkscape::Selection *sel = desktop->selection;
4644         if (!sel || sel->isEmpty()) {
4645             mc->setF(Inkscape::NORMAL_MESSAGE,
4646                      _("Select a single object to edit its nodes or handles."));
4647         } else {
4648             if (nodepath) {
4649             mc->setF(Inkscape::NORMAL_MESSAGE,
4650                      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.",
4651                               "<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.",
4652                               total_nodes),
4653                      total_nodes);
4654             } else {
4655                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4656                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4657                 } else {
4658                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4659                 }
4660             }
4661         }
4662     } else if (nodepath && selected_nodes == 1) {
4663         mc->setF(Inkscape::NORMAL_MESSAGE,
4664                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4665                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4666                           total_nodes),
4667                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4668     } else {
4669         if (selected_subpaths > 1) {
4670             mc->setF(Inkscape::NORMAL_MESSAGE,
4671                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4672                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4673                               total_nodes),
4674                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4675         } else {
4676             mc->setF(Inkscape::NORMAL_MESSAGE,
4677                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4678                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4679                               total_nodes),
4680                      selected_nodes, total_nodes, when_selected);
4681         }
4682     }
4685 /*
4686  * returns a *copy* of the curve of that object.
4687  */
4688 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4689     if (!object)
4690         return NULL;
4692     SPCurve *curve = NULL;
4693     if (SP_IS_PATH(object)) {
4694         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4695         curve = curve_new->copy();
4696     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4697         const gchar *svgd = object->repr->attribute(key);
4698         if (svgd) {
4699             NArtBpath *bpath = sp_svg_read_path(svgd);
4700             SPCurve *curve_new = SPCurve::new_from_bpath(bpath);
4701             if (curve_new) {
4702                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4703             } else {
4704                 g_free(bpath);
4705             }
4706         }
4707     }
4709     return curve;
4712 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4713     if (!np || !np->object || !curve)
4714         return;
4716     if (SP_IS_PATH(np->object)) {
4717         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4718             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4719         } else {
4720             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4721         }
4722     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4723         // FIXME: this writing to string and then reading from string is bound to be slow.
4724         // create a method to convert from curve directly to 2geom...
4725         gchar *svgpath = sp_svg_write_path( np->curve->get_pathvector() );
4726         LIVEPATHEFFECT(np->object)->lpe->setParameter(np->repr_key, svgpath);
4727         g_free(svgpath);
4729         np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4730     }
4733 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4734     np->show_helperpath = show;
4736     if (show) {
4737         SPCurve *helper_curve = np->curve->copy();
4738         helper_curve->transform(np->i2d );
4739         if (!np->helper_path) {
4740             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4741             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);
4742             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4743             sp_canvas_item_move_to_z(np->helper_path, 0);
4744             sp_canvas_item_show(np->helper_path);
4745         } else {
4746             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4747         }
4748         helper_curve->unref();
4749     } else {
4750         if (np->helper_path) {
4751             GtkObject *temp = np->helper_path;
4752             np->helper_path = NULL;
4753             gtk_object_destroy(temp);
4754         }
4755     }
4758 /* sp_nodepath_make_straight_path:
4759  *   Prevents user from curving the path by dragging a segment or activating handles etc.
4760  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
4761  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
4762  */
4763 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4764     np->straight_path = true;
4765     np->show_handles = false;
4766     g_message("add code to make the path straight.");
4767     // do sp_nodepath_convert_node_type on all nodes?
4768     // coding tip: search for this text : "Make selected segments lines"
4772 /*
4773   Local Variables:
4774   mode:c++
4775   c-file-style:"stroustrup"
4776   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4777   indent-tabs-mode:nil
4778   fill-column:99
4779   End:
4780 */
4781 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :