Code

apply patch by Stefaan, fixes bug #168589
[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 static void sp_nodepath_draw_helper_curve(Inkscape::NodePath::Path *np, SPDesktop *desktop) {
156     // Draw helper curve
157     if (np->show_helperpath) {
158         SPCurve *helper_curve = np->curve->copy();
159         helper_curve->transform(np->i2d );
160         np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(desktop), helper_curve);
161         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);
162         sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
163         sp_canvas_item_move_to_z(np->helper_path, 0);
164         sp_canvas_item_show(np->helper_path);
165         helper_curve->unref();
166     }
169 /**
170  * \brief Creates new nodepath from item
171  */
172 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
174     Inkscape::XML::Node *repr = object->repr;
176     /** \todo
177      * FIXME: remove this. We don't want to edit paths inside flowtext.
178      * Instead we will build our flowtext with cloned paths, so that the
179      * real paths are outside the flowtext and thus editable as usual.
180      */
181     if (SP_IS_FLOWTEXT(object)) {
182         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
183             if SP_IS_FLOWREGION(child) {
184                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
185                 if (grandchild && SP_IS_PATH(grandchild)) {
186                     object = SP_ITEM(grandchild);
187                     break;
188                 }
189             }
190         }
191     }
193     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
195     if (curve == NULL)
196         return NULL;
198     NArtBpath const *bpath = curve->get_bpath();
199     gint length = curve->get_length();
200     if (length == 0) {
201         curve->unref();
202         return NULL; // prevent crash for one-node paths
203     }
205     //Create new nodepath
206     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
207     if (!np) {
208         curve->unref();
209         return NULL;
210     }
212     // Set defaults
213     np->desktop     = desktop;
214     np->object      = object;
215     np->subpaths    = NULL;
216     np->selected    = NULL;
217     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
218     np->livarot_path = NULL;
219     np->local_change = 0;
220     np->show_handles = show_handles;
221     np->helper_path = NULL;
222     np->helperpath_rgba = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
223     np->helperpath_width = 1.0;
224     np->curve = curve->copy();
225     np->show_helperpath = (prefs_get_int_attribute ("tools.nodes", "show_helperpath",  0) == 1);
226     if (SP_IS_LPE_ITEM(object)) {
227         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
228         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
229             np->show_helperpath = true;
230         }            
231     }
232     np->straight_path = false;
233     if (IS_LIVEPATHEFFECT(object) && item) {
234         np->item = item;
235     } else {
236         np->item = SP_ITEM(object);
237     }
239     // we need to update item's transform from the repr here,
240     // because they may be out of sync when we respond
241     // to a change in repr by regenerating nodepath     --bb
242     sp_object_read_attr(SP_OBJECT(np->item), "transform");
244     np->i2d  = from_2geom(sp_item_i2d_affine(np->item));
245     np->d2i  = np->i2d.inverse();
247     np->repr = repr;
248     if (repr_key_in) { // apparantly the object is an LPEObject
249         np->repr_key = g_strdup(repr_key_in);
250         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
251         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
252         if (lpeparam) {
253             lpeparam->param_setup_nodepath(np);
254         }
255     } else {
256         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
257         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
258             np->repr_key = g_strdup("inkscape:original-d");
260             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
261             if (lpe) {
262                 lpe->setup_nodepath(np);
263             }
264         } else {
265             np->repr_key = g_strdup("d");
266         }
267     }
269     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
270     gchar *typestr = parse_nodetypes(nodetypes, length);
272     // create the subpath(s) from the bpath
273     NArtBpath const *b = bpath;
274     while (b->code != NR_END) {
275         b = subpath_from_bpath(np, b, typestr + (b - bpath));
276     }
278     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
279     np->subpaths = g_list_reverse(np->subpaths);
281     g_free(typestr);
282     curve->unref();
284     // create the livarot representation from the same item
285     sp_nodepath_ensure_livarot_path(np);
287     sp_nodepath_draw_helper_curve(np, desktop);
289     return np;
292 /**
293  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
294  */
295 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
297     if (!np)  //soft fail, like delete
298         return;
300     while (np->subpaths) {
301         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
302     }
304     //Inform the ShapeEditor that made me, if any, that I am gone.
305     if (np->shape_editor)
306         np->shape_editor->nodepath_destroyed();
308     g_assert(!np->selected);
310     if (np->livarot_path) {
311         delete np->livarot_path;
312         np->livarot_path = NULL;
313     }
315     if (np->helper_path) {
316         GtkObject *temp = np->helper_path;
317         np->helper_path = NULL;
318         gtk_object_destroy(temp);
319     }
320     if (np->curve) {
321         np->curve->unref();
322         np->curve = NULL;
323     }
325     if (np->repr_key) {
326         g_free(np->repr_key);
327         np->repr_key = NULL;
328     }
329     if (np->repr_nodetypes_key) {
330         g_free(np->repr_nodetypes_key);
331         np->repr_nodetypes_key = NULL;
332     }
334     np->desktop = NULL;
336     g_free(np);
340 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
342     if (np && np->livarot_path == NULL) {
343         SPCurve *curve = create_curve(np);
344         np->livarot_path = new Path;
345         np->livarot_path->LoadPathVector(curve->get_pathvector());
347         if (np->livarot_path)
348             np->livarot_path->ConvertWithBackData(0.01);
350         curve->unref();
351     }
355 /**
356  *  Return the node count of a given NodeSubPath.
357  */
358 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
360     if (!subpath)
361         return 0;
362     gint nodeCount = g_list_length(subpath->nodes);
363     return nodeCount;
366 /**
367  *  Return the node count of a given NodePath.
368  */
369 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
371     if (!np)
372         return 0;
373     gint nodeCount = 0;
374     for (GList *item = np->subpaths ; item ; item=item->next) {
375        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
376         nodeCount += g_list_length(subpath->nodes);
377     }
378     return nodeCount;
381 /**
382  *  Return the subpath count of a given NodePath.
383  */
384 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
386     if (!np)
387         return 0;
388     return g_list_length (np->subpaths);
391 /**
392  *  Return the selected node count of a given NodePath.
393  */
394 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
396     if (!np)
397         return 0;
398     return g_list_length (np->selected);
401 /**
402  *  Return the number of subpaths where nodes are selected in a given NodePath.
403  */
404 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
406     if (!np)
407         return 0;
408     if (!np->selected)
409         return 0;
410     if (!np->selected->next)
411         return 1;
412     gint count = 0;
413     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
414         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
415         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
416             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
417             if (node->selected) {
418                 count ++;
419                 break;
420             }
421         }
422     }
423     return count;
426 /**
427  * Clean up a nodepath after editing.
428  *
429  * Currently we are deleting trivial subpaths.
430  */
431 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
433     GList *badSubPaths = NULL;
435     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
436     for (GList *l = nodepath->subpaths; l ; l=l->next) {
437        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
438        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
439             badSubPaths = g_list_append(badSubPaths, sp);
440     }
442     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
443     //also removes the subpath from nodepath->subpaths
444     for (GList *l = badSubPaths; l ; l=l->next) {
445        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
446         sp_nodepath_subpath_destroy(sp);
447     }
449     g_list_free(badSubPaths);
452 /**
453  * Create new nodepath from b, make it subpath of np.
454  * \param t The node type.
455  * \todo Fixme: t should be a proper type, rather than gchar
456  */
457 static NArtBpath const * subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath const *b, gchar const *t)
459     NR::Point ppos, pos, npos;
461     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
463     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
464     bool const closed = (b->code == NR_MOVETO);
466     pos = NR::Point(b->x3, b->y3) * np->i2d;
467     if (b[1].code == NR_CURVETO) {
468         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
469     } else {
470         npos = pos;
471     }
472     Inkscape::NodePath::Node *n;
473     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
474     g_assert(sp->first == n);
475     g_assert(sp->last  == n);
477     b++;
478     t++;
479     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
480         pos = NR::Point(b->x3, b->y3) * np->i2d;
481         if (b->code == NR_CURVETO) {
482             ppos = NR::Point(b->x2, b->y2) * np->i2d;
483         } else {
484             ppos = pos;
485         }
486         if (b[1].code == NR_CURVETO) {
487             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
488         } else {
489             npos = pos;
490         }
491         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
492         b++;
493         t++;
494     }
496     if (closed) sp_nodepath_subpath_close(sp);
498     return b;
501 /**
502  * Convert from sodipodi:nodetypes to new style type string.
503  */
504 static gchar *parse_nodetypes(gchar const *types, gint length)
506     g_assert(length > 0);
508     gchar *typestr = g_new(gchar, length + 1);
510     gint pos = 0;
512     if (types) {
513         for (gint i = 0; types[i] && ( i < length ); i++) {
514             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
515             if (types[i] != '\0') {
516                 switch (types[i]) {
517                     case 's':
518                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
519                         break;
520                     case 'z':
521                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
522                         break;
523                     case 'c':
524                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
525                         break;
526                     default:
527                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
528                         break;
529                 }
530             }
531         }
532     }
534     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
536     return typestr;
539 /**
540  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
541  * updated but repr is not (for speed). Used during curve and node drag.
542  */
543 static void update_object(Inkscape::NodePath::Path *np)
545     g_assert(np);
547     np->curve->unref();
548     np->curve = create_curve(np);
550     sp_nodepath_set_curve(np, np->curve);
552     if (np->show_helperpath) {
553         SPCurve * helper_curve = np->curve->copy();
554         helper_curve->transform(np->i2d );
555         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
556         helper_curve->unref();
557     }
560 /**
561  * Update XML path node with data from path object.
562  */
563 static void update_repr_internal(Inkscape::NodePath::Path *np)
565     g_assert(np);
567     Inkscape::XML::Node *repr = np->object->repr;
569     np->curve->unref();
570     np->curve = create_curve(np);
572     gchar *typestr = create_typestr(np);
573     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
575     // determine if path has an effect applied and write to correct "d" attribute.
576     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
577         np->local_change++;
578         repr->setAttribute(np->repr_key, svgpath);
579     }
581     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
582         np->local_change++;
583         repr->setAttribute(np->repr_nodetypes_key, typestr);
584     }
586     g_free(svgpath);
587     g_free(typestr);
589     if (np->show_helperpath) {
590         SPCurve * helper_curve = np->curve->copy();
591         helper_curve->transform(np->i2d );
592         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
593         helper_curve->unref();
594     }
595  }
597 /**
598  * Update XML path node with data from path object, commit changes forever.
599  */
600 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
602     //fixme: np can be NULL, so check before proceeding
603     g_return_if_fail(np != NULL);
605     if (np->livarot_path) {
606         delete np->livarot_path;
607         np->livarot_path = NULL;
608     }
610     update_repr_internal(np);
611     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
613     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
614                      annotation);
617 /**
618  * Update XML path node with data from path object, commit changes with undo.
619  */
620 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
622     if (np->livarot_path) {
623         delete np->livarot_path;
624         np->livarot_path = NULL;
625     }
627     update_repr_internal(np);
628     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
629                            annotation);
632 /**
633  * Make duplicate of path, replace corresponding XML node in tree, commit.
634  */
635 static void stamp_repr(Inkscape::NodePath::Path *np)
637     g_assert(np);
639     Inkscape::XML::Node *old_repr = np->object->repr;
640     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
642     // remember the position of the item
643     gint pos = old_repr->position();
644     // remember parent
645     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
647     SPCurve *curve = create_curve(np);
648     gchar *typestr = create_typestr(np);
650     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
652     new_repr->setAttribute(np->repr_key, svgpath);
653     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
655     // add the new repr to the parent
656     parent->appendChild(new_repr);
657     // move to the saved position
658     new_repr->setPosition(pos > 0 ? pos : 0);
660     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
661                      _("Stamp"));
663     Inkscape::GC::release(new_repr);
664     g_free(svgpath);
665     g_free(typestr);
666     curve->unref();
669 /**
670  * Create curve from path.
671  */
672 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
674     SPCurve *curve = new SPCurve();
676     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
677        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
678         curve->moveto(sp->first->pos * np->d2i);
679        Inkscape::NodePath::Node *n = sp->first->n.other;
680         while (n) {
681             NR::Point const end_pt = n->pos * np->d2i;
682             switch (n->code) {
683                 case NR_LINETO:
684                     curve->lineto(end_pt);
685                     break;
686                 case NR_CURVETO:
687                     curve->curveto(n->p.other->n.pos * np->d2i,
688                                      n->p.pos * np->d2i,
689                                      end_pt);
690                     break;
691                 default:
692                     g_assert_not_reached();
693                     break;
694             }
695             if (n != sp->last) {
696                 n = n->n.other;
697             } else {
698                 n = NULL;
699             }
700         }
701         if (sp->closed) {
702             curve->closepath();
703         }
704     }
706     return curve;
709 /**
710  * Convert path type string to sodipodi:nodetypes style.
711  */
712 static gchar *create_typestr(Inkscape::NodePath::Path *np)
714     gchar *typestr = g_new(gchar, 32);
715     gint len = 32;
716     gint pos = 0;
718     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
719        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
721         if (pos >= len) {
722             typestr = g_renew(gchar, typestr, len + 32);
723             len += 32;
724         }
726         typestr[pos++] = 'c';
728        Inkscape::NodePath::Node *n;
729         n = sp->first->n.other;
730         while (n) {
731             gchar code;
733             switch (n->type) {
734                 case Inkscape::NodePath::NODE_CUSP:
735                     code = 'c';
736                     break;
737                 case Inkscape::NodePath::NODE_SMOOTH:
738                     code = 's';
739                     break;
740                 case Inkscape::NodePath::NODE_SYMM:
741                     code = 'z';
742                     break;
743                 default:
744                     g_assert_not_reached();
745                     code = '\0';
746                     break;
747             }
749             if (pos >= len) {
750                 typestr = g_renew(gchar, typestr, len + 32);
751                 len += 32;
752             }
754             typestr[pos++] = code;
756             if (n != sp->last) {
757                 n = n->n.other;
758             } else {
759                 n = NULL;
760             }
761         }
762     }
764     if (pos >= len) {
765         typestr = g_renew(gchar, typestr, len + 1);
766         len += 1;
767     }
769     typestr[pos++] = '\0';
771     return typestr;
774 /**
775  * Returns current path in context. // later eliminate this function at all!
776  */
777 static Inkscape::NodePath::Path *sp_nodepath_current()
779     if (!SP_ACTIVE_DESKTOP) {
780         return NULL;
781     }
783     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
785     if (!SP_IS_NODE_CONTEXT(event_context)) {
786         return NULL;
787     }
789     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
794 /**
795  \brief Fills node and handle positions for three nodes, splitting line
796   marked by end at distance t.
797  */
798 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
800     g_assert(new_path != NULL);
801     g_assert(end      != NULL);
803     g_assert(end->p.other == new_path);
804    Inkscape::NodePath::Node *start = new_path->p.other;
805     g_assert(start);
807     if (end->code == NR_LINETO) {
808         new_path->type =Inkscape::NodePath::NODE_CUSP;
809         new_path->code = NR_LINETO;
810         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
811     } else {
812         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
813         new_path->code = NR_CURVETO;
814         gdouble s      = 1 - t;
815         for (int dim = 0; dim < 2; dim++) {
816             NR::Coord const f000 = start->pos[dim];
817             NR::Coord const f001 = start->n.pos[dim];
818             NR::Coord const f011 = end->p.pos[dim];
819             NR::Coord const f111 = end->pos[dim];
820             NR::Coord const f00t = s * f000 + t * f001;
821             NR::Coord const f01t = s * f001 + t * f011;
822             NR::Coord const f11t = s * f011 + t * f111;
823             NR::Coord const f0tt = s * f00t + t * f01t;
824             NR::Coord const f1tt = s * f01t + t * f11t;
825             NR::Coord const fttt = s * f0tt + t * f1tt;
826             start->n.pos[dim]    = f00t;
827             new_path->p.pos[dim] = f0tt;
828             new_path->pos[dim]   = fttt;
829             new_path->n.pos[dim] = f1tt;
830             end->p.pos[dim]      = f11t;
831         }
832     }
835 /**
836  * Adds new node on direct line between two nodes, activates handles of all
837  * three nodes.
838  */
839 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
841     g_assert(end);
842     g_assert(end->subpath);
843     g_assert(g_list_find(end->subpath->nodes, end));
845    Inkscape::NodePath::Node *start = end->p.other;
846     g_assert( start->n.other == end );
847    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
848                                                end,
849                                                (NRPathcode)end->code == NR_LINETO?
850                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
851                                                (NRPathcode)end->code,
852                                                &start->pos, &start->pos, &start->n.pos);
853     sp_nodepath_line_midpoint(newnode, end, t);
855     sp_node_adjust_handles(start);
856     sp_node_update_handles(start);
857     sp_node_update_handles(newnode);
858     sp_node_adjust_handles(end);
859     sp_node_update_handles(end);
861     return newnode;
864 /**
865 \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
866 */
867 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
869     g_assert(node);
870     g_assert(node->subpath);
871     g_assert(g_list_find(node->subpath->nodes, node));
873    Inkscape::NodePath::SubPath *sp = node->subpath;
874     Inkscape::NodePath::Path *np    = sp->nodepath;
876     if (sp->closed) {
877         sp_nodepath_subpath_open(sp, node);
878         return sp->first;
879     } else {
880         // no break for end nodes
881         if (node == sp->first) return NULL;
882         if (node == sp->last ) return NULL;
884         // create a new subpath
885        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
887         // duplicate the break node as start of the new subpath
888         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
890         // attach rest of curve to new node
891         g_assert(node->n.other);
892         newnode->n.other = node->n.other; node->n.other = NULL;
893         newnode->n.other->p.other = newnode;
894         newsubpath->last = sp->last;
895         sp->last = node;
896         node = newnode;
897         while (node->n.other) {
898             node = node->n.other;
899             node->subpath = newsubpath;
900             sp->nodes = g_list_remove(sp->nodes, node);
901             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
902         }
905         return newnode;
906     }
909 /**
910  * Duplicate node and connect to neighbours.
911  */
912 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
914     g_assert(node);
915     g_assert(node->subpath);
916     g_assert(g_list_find(node->subpath->nodes, node));
918    Inkscape::NodePath::SubPath *sp = node->subpath;
920     NRPathcode code = (NRPathcode) node->code;
921     if (code == NR_MOVETO) { // if node is the endnode,
922         node->code = NR_LINETO; // new one is inserted before it, so change that to line
923     }
925     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
927     if (!node->n.other || !node->p.other) // if node is an endnode, select it
928         return node;
929     else
930         return newnode; // otherwise select the newly created node
933 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
935     node->p.pos = (node->pos + (node->pos - node->n.pos));
938 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
940     node->n.pos = (node->pos + (node->pos - node->p.pos));
943 /**
944  * Change line type at node, with side effects on neighbours.
945  */
946 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
948     g_assert(end);
949     g_assert(end->subpath);
950     g_assert(end->p.other);
952     if (end->code == static_cast< guint > ( code ) )
953         return;
955    Inkscape::NodePath::Node *start = end->p.other;
957     end->code = code;
959     if (code == NR_LINETO) {
960         if (start->code == NR_LINETO) {
961             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
962         }
963         if (end->n.other) {
964             if (end->n.other->code == NR_LINETO) {
965                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
966             }
967         }
968     } else {
969         NR::Point delta = end->pos - start->pos;
970         start->n.pos = start->pos + delta / 3;
971         end->p.pos = end->pos - delta / 3;
972         sp_node_adjust_handle(start, 1);
973         sp_node_adjust_handle(end, -1);
974     }
976     sp_node_update_handles(start);
977     sp_node_update_handles(end);
980 /**
981  * Change node type, and its handles accordingly.
982  */
983 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
985     g_assert(node);
986     g_assert(node->subpath);
988     if ((node->p.other != NULL) && (node->n.other != NULL)) {
989         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
990             type =Inkscape::NodePath::NODE_CUSP;
991         }
992     }
994     node->type = type;
996     if (node->type == Inkscape::NodePath::NODE_CUSP) {
997         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
998         node->knot->setSize (node->selected? 11 : 9);
999         sp_knot_update_ctrl(node->knot);
1000     } else {
1001         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1002         node->knot->setSize (node->selected? 9 : 7);
1003         sp_knot_update_ctrl(node->knot);
1004     }
1006     // if one of handles is mouseovered, preserve its position
1007     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1008         sp_node_adjust_handle(node, 1);
1009     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1010         sp_node_adjust_handle(node, -1);
1011     } else {
1012         sp_node_adjust_handles(node);
1013     }
1015     sp_node_update_handles(node);
1017     sp_nodepath_update_statusbar(node->subpath->nodepath);
1019     return node;
1022 bool
1023 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1025         Inkscape::NodePath::Node *othernode = side->other;
1026         if (!othernode)
1027             return false;
1028         NRPathcode const code = sp_node_path_code_from_side(node, side);
1029         if (code == NR_LINETO)
1030             return true;
1031         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1032         if (&node->p == side) {
1033             other_to_me = &othernode->n;
1034         } else if (&node->n == side) {
1035             other_to_me = &othernode->p;
1036         } 
1037         if (!other_to_me)
1038             return false;
1039         bool is_line = 
1040              (NR::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1041               NR::L2(node->pos - side->pos) < 1e-6);
1042         return is_line;
1045 /**
1046  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1047  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1048  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1049  * If already cusp and set to cusp, retracts handles.
1050 */
1051 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1053     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1055 /* 
1056   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1057  
1058         if (two_handles) {
1059             // do nothing, adjust_handles called via set_node_type will line them up
1060         } else if (one_handle) {
1061             if (opposite_to_handle_is_line) {
1062                 if (lined_up) {
1063                     // already half-smooth; pull opposite handle too making it fully smooth
1064                 } else {
1065                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1066                 }
1067             } else {
1068                 // pull opposite handle in line with the existing one
1069             }
1070         } else if (no_handles) {
1071             if (both_segments_are_lines OR both_segments_are_curves) {
1072                 //pull both handles
1073             } else {
1074                 // pull the handle opposite to line segment, making node half-smooth
1075             }
1076         }
1077 */
1078         bool p_has_handle = (NR::L2(node->pos  - node->p.pos) > 1e-6);
1079         bool n_has_handle = (NR::L2(node->pos  - node->n.pos) > 1e-6);
1080         bool p_is_line = sp_node_side_is_line(node, &node->p);
1081         bool n_is_line = sp_node_side_is_line(node, &node->n);
1083         if (p_has_handle && n_has_handle) {
1084             // do nothing, adjust_handles will line them up
1085         } else if (p_has_handle || n_has_handle) {
1086             if (p_has_handle && n_is_line) {
1087                 Radial line (node->n.other->pos - node->pos);
1088                 Radial handle (node->pos - node->p.pos);
1089                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1090                     // already half-smooth; pull opposite handle too making it fully smooth
1091                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1092                 } else {
1093                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1094                 }
1095             } else if (n_has_handle && p_is_line) {
1096                 Radial line (node->p.other->pos - node->pos);
1097                 Radial handle (node->pos - node->n.pos);
1098                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1099                     // already half-smooth; pull opposite handle too making it fully smooth
1100                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1101                 } else {
1102                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1103                 }
1104             } else if (p_has_handle && node->n.other) {
1105                 // pull n handle
1106                 node->n.other->code = NR_CURVETO;
1107                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1108                     NR::L2(node->p.pos - node->pos) :
1109                     NR::L2(node->n.other->pos - node->pos) / 3;
1110                 node->n.pos = node->pos - (len / NR::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1111             } else if (n_has_handle && node->p.other) {
1112                 // pull p handle
1113                 node->code = NR_CURVETO;
1114                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1115                     NR::L2(node->n.pos - node->pos) :
1116                     NR::L2(node->p.other->pos - node->pos) / 3;
1117                 node->p.pos = node->pos - (len / NR::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1118             }
1119         } else if (!p_has_handle && !n_has_handle) {
1120             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1121                 // no handles, but both segments are either lnes or curves:
1122                 //pull both handles
1124                 // convert both to curves:
1125                 node->code = NR_CURVETO;
1126                 node->n.other->code = NR_CURVETO;
1128                 NR::Point leg_prev = node->pos - node->p.other->pos;
1129                 NR::Point leg_next = node->pos - node->n.other->pos;
1131                 double norm_leg_prev = L2(leg_prev);
1132                 double norm_leg_next = L2(leg_next);
1134                 NR::Point delta;
1135                 if (norm_leg_next > 0.0) {
1136                     delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1137                     (&delta)->normalize();
1138                 }
1140                 if (type == Inkscape::NodePath::NODE_SYMM) {
1141                     double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1142                     node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1143                     node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1144                 } else {
1145                     // length of handle is proportional to distance to adjacent node
1146                     node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1147                     node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1148                 }
1150             } else {
1151                 // pull the handle opposite to line segment, making it half-smooth
1152                 if (p_is_line && node->n.other) {
1153                     if (type != Inkscape::NodePath::NODE_SYMM) {
1154                         // pull n handle
1155                         node->n.other->code = NR_CURVETO;
1156                         double len =  NR::L2(node->n.other->pos - node->pos) / 3;
1157                         node->n.pos = node->pos + (len / NR::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1158                     }
1159                 } else if (n_is_line && node->p.other) {
1160                     if (type != Inkscape::NodePath::NODE_SYMM) {
1161                         // pull p handle
1162                         node->code = NR_CURVETO;
1163                         double len =  NR::L2(node->p.other->pos - node->pos) / 3;
1164                         node->p.pos = node->pos + (len / NR::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1165                     }
1166                 }
1167             }
1168         }
1169     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1170         // cusping a cusp: retract nodes
1171         node->p.pos = node->pos;
1172         node->n.pos = node->pos;
1173     }
1175     sp_nodepath_set_node_type (node, type);
1178 /**
1179  * Move node to point, and adjust its and neighbouring handles.
1180  */
1181 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1183     NR::Point delta = p - node->pos;
1184     node->pos = p;
1186     node->p.pos += delta;
1187     node->n.pos += delta;
1189     Inkscape::NodePath::Node *node_p = NULL;
1190     Inkscape::NodePath::Node *node_n = NULL;
1192     if (node->p.other) {
1193         if (node->code == NR_LINETO) {
1194             sp_node_adjust_handle(node, 1);
1195             sp_node_adjust_handle(node->p.other, -1);
1196             node_p = node->p.other;
1197         }
1198     }
1199     if (node->n.other) {
1200         if (node->n.other->code == NR_LINETO) {
1201             sp_node_adjust_handle(node, -1);
1202             sp_node_adjust_handle(node->n.other, 1);
1203             node_n = node->n.other;
1204         }
1205     }
1207     // this function is only called from batch movers that will update display at the end
1208     // themselves, so here we just move all the knots without emitting move signals, for speed
1209     sp_node_update_handles(node, false);
1210     if (node_n) {
1211         sp_node_update_handles(node_n, false);
1212     }
1213     if (node_p) {
1214         sp_node_update_handles(node_p, false);
1215     }
1218 /**
1219  * Call sp_node_moveto() for node selection and handle possible snapping.
1220  */
1221 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1222                                             bool const snap, bool constrained = false, 
1223                                             Inkscape::Snapper::ConstraintLine const &constraint = NR::Point())
1225     NR::Coord best = NR_HUGE;
1226     NR::Point delta(dx, dy);
1227     NR::Point best_pt = delta;
1228     Inkscape::SnappedPoint best_abs;
1229     
1230     if (snap) {    
1231         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1232          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1233          * must provide that information. */
1234           
1235         // Build a list of the unselected nodes to which the snapper should snap 
1236         std::vector<NR::Point> unselected_nodes;
1237         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1238             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1239             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1240                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1241                 if (!node->selected) {
1242                     unselected_nodes.push_back(node->pos);
1243                 }    
1244             }
1245         }        
1246         
1247         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1248         
1249         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1250             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1251             m.setup(NULL, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1252             Inkscape::SnappedPoint s;
1253             if (constrained) {
1254                 Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1255                 dedicated_constraint.setPoint(n->pos);
1256                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, dedicated_constraint);
1257             } else {
1258                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta);
1259             }            
1260             if (s.getSnapped() && (s.getDistance() < best)) {
1261                 best = s.getDistance();
1262                 best_abs = s;
1263                 best_pt = s.getPoint() - n->pos;
1264             }
1265         }
1266                         
1267         if (best_abs.getSnapped()) {
1268             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1269         } else {
1270             nodepath->desktop->snapindicator->remove_snappoint();    
1271         }
1272     }
1274     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1275         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1276         sp_node_moveto(n, n->pos + best_pt);
1277     }
1279     // do not update repr here so that node dragging is acceptably fast
1280     update_object(nodepath);
1283 /**
1284 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1285 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1286 near x = 0.
1287  */
1288 double
1289 sculpt_profile (double x, double alpha, guint profile)
1291     if (x >= 1)
1292         return 0;
1293     if (x <= 0)
1294         return 1;
1296     switch (profile) {
1297         case SCULPT_PROFILE_LINEAR:
1298         return 1 - x;
1299         case SCULPT_PROFILE_BELL:
1300         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1301         case SCULPT_PROFILE_ELLIPTIC:
1302         return sqrt(1 - x*x);
1303     }
1305     return 1;
1308 double
1309 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1311     // extremely primitive for now, don't have time to look for the real one
1312     double lower = NR::L2(b - a);
1313     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1314     return (lower + upper)/2;
1317 void
1318 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1320     n->pos = n->origin + delta;
1321     n->n.pos = n->n.origin + delta_n;
1322     n->p.pos = n->p.origin + delta_p;
1323     sp_node_adjust_handles(n);
1324     sp_node_update_handles(n, false);
1327 /**
1328  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1329  * on how far they are from the dragged node n.
1330  */
1331 static void
1332 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1334     g_assert (n);
1335     g_assert (nodepath);
1336     g_assert (n->subpath->nodepath == nodepath);
1338     double pressure = n->knot->pressure;
1339     if (pressure == 0)
1340         pressure = 0.5; // default
1341     pressure = CLAMP (pressure, 0.2, 0.8);
1343     // map pressure to alpha = 1/5 ... 5
1344     double alpha = 1 - 2 * fabs(pressure - 0.5);
1345     if (pressure > 0.5)
1346         alpha = 1/alpha;
1348     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1350     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1351         // Only one subpath has selected nodes:
1352         // use linear mode, where the distance from n to node being dragged is calculated along the path
1354         double n_sel_range = 0, p_sel_range = 0;
1355         guint n_nodes = 0, p_nodes = 0;
1356         guint n_sel_nodes = 0, p_sel_nodes = 0;
1358         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1359         {
1360             double n_range = 0, p_range = 0;
1361             bool n_going = true, p_going = true;
1362             Inkscape::NodePath::Node *n_node = n;
1363             Inkscape::NodePath::Node *p_node = n;
1364             do {
1365                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1366                 if (n_node && n_going)
1367                     n_node = n_node->n.other;
1368                 if (n_node == NULL) {
1369                     n_going = false;
1370                 } else {
1371                     n_nodes ++;
1372                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1373                     if (n_node->selected) {
1374                         n_sel_nodes ++;
1375                         n_sel_range = n_range;
1376                     }
1377                     if (n_node == p_node) {
1378                         n_going = false;
1379                         p_going = false;
1380                     }
1381                 }
1382                 if (p_node && p_going)
1383                     p_node = p_node->p.other;
1384                 if (p_node == NULL) {
1385                     p_going = false;
1386                 } else {
1387                     p_nodes ++;
1388                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1389                     if (p_node->selected) {
1390                         p_sel_nodes ++;
1391                         p_sel_range = p_range;
1392                     }
1393                     if (p_node == n_node) {
1394                         n_going = false;
1395                         p_going = false;
1396                     }
1397                 }
1398             } while (n_going || p_going);
1399         }
1401         // Second pass: actually move nodes in this subpath
1402         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1403         {
1404             double n_range = 0, p_range = 0;
1405             bool n_going = true, p_going = true;
1406             Inkscape::NodePath::Node *n_node = n;
1407             Inkscape::NodePath::Node *p_node = n;
1408             do {
1409                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1410                 if (n_node && n_going)
1411                     n_node = n_node->n.other;
1412                 if (n_node == NULL) {
1413                     n_going = false;
1414                 } else {
1415                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1416                     if (n_node->selected) {
1417                         sp_nodepath_move_node_and_handles (n_node,
1418                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1419                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1420                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1421                     }
1422                     if (n_node == p_node) {
1423                         n_going = false;
1424                         p_going = false;
1425                     }
1426                 }
1427                 if (p_node && p_going)
1428                     p_node = p_node->p.other;
1429                 if (p_node == NULL) {
1430                     p_going = false;
1431                 } else {
1432                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1433                     if (p_node->selected) {
1434                         sp_nodepath_move_node_and_handles (p_node,
1435                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1436                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1437                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1438                     }
1439                     if (p_node == n_node) {
1440                         n_going = false;
1441                         p_going = false;
1442                     }
1443                 }
1444             } while (n_going || p_going);
1445         }
1447     } else {
1448         // Multiple subpaths have selected nodes:
1449         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1450         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1451         // fix the pear-like shape when sculpting e.g. a ring
1453         // First pass: calculate range
1454         gdouble direct_range = 0;
1455         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1456             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1457             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1458                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1459                 if (node->selected) {
1460                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1461                 }
1462             }
1463         }
1465         // Second pass: actually move nodes
1466         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1467             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1468             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1469                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1470                 if (node->selected) {
1471                     if (direct_range > 1e-6) {
1472                         sp_nodepath_move_node_and_handles (node,
1473                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1474                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1475                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1476                     } else {
1477                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1478                     }
1480                 }
1481             }
1482         }
1483     }
1485     // do not update repr here so that node dragging is acceptably fast
1486     update_object(nodepath);
1490 /**
1491  * Move node selection to point, adjust its and neighbouring handles,
1492  * handle possible snapping, and commit the change with possible undo.
1493  */
1494 void
1495 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1497     if (!nodepath) return;
1499     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1501     if (dx == 0) {
1502         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1503     } else if (dy == 0) {
1504         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1505     } else {
1506         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1507     }
1510 /**
1511  * Move node selection off screen and commit the change.
1512  */
1513 void
1514 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1516     // borrowed from sp_selection_move_screen in selection-chemistry.c
1517     // we find out the current zoom factor and divide deltas by it
1518     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1520     gdouble zoom = desktop->current_zoom();
1521     gdouble zdx = dx / zoom;
1522     gdouble zdy = dy / zoom;
1524     if (!nodepath) return;
1526     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1528     if (dx == 0) {
1529         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1530     } else if (dy == 0) {
1531         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1532     } else {
1533         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1534     }
1537 /**
1538  * Move selected nodes to the absolute position given
1539  */
1540 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis)
1542     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1543         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1544         NR::Point npos(axis == NR::X ? val : n->pos[NR::X], axis == NR::Y ? val : n->pos[NR::Y]);
1545         sp_node_moveto(n, npos);
1546     }
1548     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1551 /**
1552  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1553  */
1554 NR::Maybe<NR::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1556     NR::Maybe<NR::Coord> no_coord = NR::Nothing();
1557     g_return_val_if_fail(nodepath->selected, no_coord);
1559     // determine coordinate of first selected node
1560     GList *nsel = nodepath->selected;
1561     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1562     NR::Coord coord = n->pos[axis];
1563     bool coincide = true;
1565     // compare it to the coordinates of all the other selected nodes
1566     for (GList *l = nsel->next; l != NULL; l = l->next) {
1567         n = (Inkscape::NodePath::Node *) l->data;
1568         if (n->pos[axis] != coord) {
1569             coincide = false;
1570         }
1571     }
1572     if (coincide) {
1573         return coord;
1574     } else {
1575         NR::Rect bbox = sp_node_selected_bbox(nodepath);
1576         // currently we return the coordinate of the bounding box midpoint because I don't know how
1577         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1578         return bbox.midpoint()[axis];
1579     }
1582 /** If they don't yet exist, creates knot and line for the given side of the node */
1583 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1585     if (!side->knot) {
1586         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"));
1588         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1589         side->knot->setSize (7);
1590         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1591         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1592         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1593         sp_knot_update_ctrl(side->knot);
1595         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1596         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1597         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1598         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1599         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1600         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1601     }
1603     if (!side->line) {
1604         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1605                                         SP_TYPE_CTRLLINE, NULL);
1606     }
1609 /**
1610  * Ensure the given handle of the node is visible/invisible, update its screen position
1611  */
1612 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1614     g_assert(node != NULL);
1616    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1617     NRPathcode code = sp_node_path_code_from_side(node, side);
1619     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1621     if (show_handle) {
1622         if (!side->knot) { // No handle knot at all
1623             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1624             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1625             side->knot->pos = side->pos;
1626             if (side->knot->item)
1627                 SP_CTRL(side->knot->item)->moveto(side->pos);
1628             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1629             sp_knot_show(side->knot);
1630         } else {
1631             if (side->knot->pos != side->pos) { // only if it's really moved
1632                 if (fire_move_signals) {
1633                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1634                 } else {
1635                     sp_knot_moveto(side->knot, &side->pos);
1636                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1637                 }
1638             }
1639             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1640                 sp_knot_show(side->knot);
1641             }
1642         }
1643         sp_canvas_item_show(side->line);
1644     } else {
1645         if (side->knot) {
1646             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1647                 sp_knot_hide(side->knot);
1648             }
1649         }
1650         if (side->line) {
1651             sp_canvas_item_hide(side->line);
1652         }
1653     }
1656 /**
1657  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1658  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1659  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1660  * updated; otherwise, just move the knots silently (used in batch moves).
1661  */
1662 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1664     g_assert(node != NULL);
1666     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1667         sp_knot_show(node->knot);
1668     }
1670     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1671         if (fire_move_signals)
1672             sp_knot_set_position(node->knot, &node->pos, 0);
1673         else
1674             sp_knot_moveto(node->knot, &node->pos);
1675     }
1677     gboolean show_handles = node->selected;
1678     if (node->p.other != NULL) {
1679         if (node->p.other->selected) show_handles = TRUE;
1680     }
1681     if (node->n.other != NULL) {
1682         if (node->n.other->selected) show_handles = TRUE;
1683     }
1685     if (node->subpath->nodepath->show_handles == false)
1686         show_handles = FALSE;
1688     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1689     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1692 /**
1693  * Call sp_node_update_handles() for all nodes on subpath.
1694  */
1695 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1697     g_assert(subpath != NULL);
1699     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1700         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1701     }
1704 /**
1705  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1706  */
1707 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1709     g_assert(nodepath != NULL);
1711     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1712         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1713     }
1716 void
1717 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1719     if (nodepath == NULL) return;
1721     nodepath->show_handles = show;
1722     sp_nodepath_update_handles(nodepath);
1725 /**
1726  * Adds all selected nodes in nodepath to list.
1727  */
1728 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1730     StlConv<Node *>::list(l, selected);
1731 /// \todo this adds a copying, rework when the selection becomes a stl list
1734 /**
1735  * Align selected nodes on the specified axis.
1736  */
1737 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1739     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1740         return;
1741     }
1743     if ( !nodepath->selected->next ) { // only one node selected
1744         return;
1745     }
1746    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1747     NR::Point dest(pNode->pos);
1748     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1749         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1750         if (pNode) {
1751             dest[axis] = pNode->pos[axis];
1752             sp_node_moveto(pNode, dest);
1753         }
1754     }
1756     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1759 /// Helper struct.
1760 struct NodeSort
1762    Inkscape::NodePath::Node *_node;
1763     NR::Coord _coord;
1764     /// \todo use vectorof pointers instead of calling copy ctor
1765     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1766         _node(node), _coord(node->pos[axis])
1767     {}
1769 };
1771 static bool operator<(NodeSort const &a, NodeSort const &b)
1773     return (a._coord < b._coord);
1776 /**
1777  * Distribute selected nodes on the specified axis.
1778  */
1779 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1781     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1782         return;
1783     }
1785     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1786         return;
1787     }
1789    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1790     std::vector<NodeSort> sorted;
1791     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1792         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1793         if (pNode) {
1794             NodeSort n(pNode, axis);
1795             sorted.push_back(n);
1796             //dest[axis] = pNode->pos[axis];
1797             //sp_node_moveto(pNode, dest);
1798         }
1799     }
1800     std::sort(sorted.begin(), sorted.end());
1801     unsigned int len = sorted.size();
1802     //overall bboxes span
1803     float dist = (sorted.back()._coord -
1804                   sorted.front()._coord);
1805     //new distance between each bbox
1806     float step = (dist) / (len - 1);
1807     float pos = sorted.front()._coord;
1808     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1809           it < sorted.end();
1810           it ++ )
1811     {
1812         NR::Point dest((*it)._node->pos);
1813         dest[axis] = pos;
1814         sp_node_moveto((*it)._node, dest);
1815         pos += step;
1816     }
1818     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1822 /**
1823  * Call sp_nodepath_line_add_node() for all selected segments.
1824  */
1825 void
1826 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1828     if (!nodepath) {
1829         return;
1830     }
1832     GList *nl = NULL;
1834     int n_added = 0;
1836     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1837        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1838         g_assert(t->selected);
1839         if (t->p.other && t->p.other->selected) {
1840             nl = g_list_prepend(nl, t);
1841         }
1842     }
1844     while (nl) {
1845        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1846        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1847        sp_nodepath_node_select(n, TRUE, FALSE);
1848        n_added ++;
1849        nl = g_list_remove(nl, t);
1850     }
1852     /** \todo fixme: adjust ? */
1853     sp_nodepath_update_handles(nodepath);
1855     if (n_added > 1) {
1856         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1857     } else if (n_added > 0) {
1858         sp_nodepath_update_repr(nodepath, _("Add node"));
1859     }
1861     sp_nodepath_update_statusbar(nodepath);
1864 /**
1865  * Select segment nearest to point
1866  */
1867 void
1868 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1870     if (!nodepath) {
1871         return;
1872     }
1874     sp_nodepath_ensure_livarot_path(nodepath);
1875     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1876     if (!maybe_position) {
1877         return;
1878     }
1879     Path::cut_position position = *maybe_position;
1881     //find segment to segment
1882     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1884     //fixme: this can return NULL, so check before proceeding.
1885     g_return_if_fail(e != NULL);
1887     gboolean force = FALSE;
1888     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1889         force = TRUE;
1890     }
1891     sp_nodepath_node_select(e, (gboolean) toggle, force);
1892     if (e->p.other)
1893         sp_nodepath_node_select(e->p.other, TRUE, force);
1895     sp_nodepath_update_handles(nodepath);
1897     sp_nodepath_update_statusbar(nodepath);
1900 /**
1901  * Add a node nearest to point
1902  */
1903 void
1904 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1906     if (!nodepath) {
1907         return;
1908     }
1910     sp_nodepath_ensure_livarot_path(nodepath);
1911     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1912     if (!maybe_position) {
1913         return;
1914     }
1915     Path::cut_position position = *maybe_position;
1917     //find segment to split
1918     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1920     //don't know why but t seems to flip for lines
1921     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1922         position.t = 1.0 - position.t;
1923     }
1924     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1925     sp_nodepath_node_select(n, FALSE, TRUE);
1927     /* fixme: adjust ? */
1928     sp_nodepath_update_handles(nodepath);
1930     sp_nodepath_update_repr(nodepath, _("Add node"));
1932     sp_nodepath_update_statusbar(nodepath);
1935 /*
1936  * Adjusts a segment so that t moves by a certain delta for dragging
1937  * converts lines to curves
1938  *
1939  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1940  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1941  */
1942 void
1943 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1945     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1947     //fixme: e and e->p can be NULL, so check for those before proceeding
1948     g_return_if_fail(e != NULL);
1949     g_return_if_fail(&e->p != NULL);
1951     /* feel good is an arbitrary parameter that distributes the delta between handles
1952      * if t of the drag point is less than 1/6 distance form the endpoint only
1953      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1954      */
1955     double feel_good;
1956     if (t <= 1.0 / 6.0)
1957         feel_good = 0;
1958     else if (t <= 0.5)
1959         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1960     else if (t <= 5.0 / 6.0)
1961         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1962     else
1963         feel_good = 1;
1965     //if we're dragging a line convert it to a curve
1966     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1967         sp_nodepath_set_line_type(e, NR_CURVETO);
1968     }
1970     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1971     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1972     e->p.other->n.pos += offsetcoord0;
1973     e->p.pos += offsetcoord1;
1975     // adjust handles of adjacent nodes where necessary
1976     sp_node_adjust_handle(e,1);
1977     sp_node_adjust_handle(e->p.other,-1);
1979     sp_nodepath_update_handles(e->subpath->nodepath);
1981     update_object(e->subpath->nodepath);
1983     sp_nodepath_update_statusbar(e->subpath->nodepath);
1987 /**
1988  * Call sp_nodepath_break() for all selected segments.
1989  */
1990 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
1992     if (!nodepath) return;
1994     GList *tempin = g_list_copy(nodepath->selected);
1995     GList *temp = NULL;
1996     for (GList *l = tempin; l != NULL; l = l->next) {
1997        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1998        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1999         if (nn == NULL) continue; // no break, no new node
2000         temp = g_list_prepend(temp, nn);
2001     }
2002     g_list_free(tempin);
2004     if (temp) {
2005         sp_nodepath_deselect(nodepath);
2006     }
2007     for (GList *l = temp; l != NULL; l = l->next) {
2008         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2009     }
2011     sp_nodepath_update_handles(nodepath);
2013     sp_nodepath_update_repr(nodepath, _("Break path"));
2016 /**
2017  * Duplicate the selected node(s).
2018  */
2019 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2021     if (!nodepath) {
2022         return;
2023     }
2025     GList *temp = NULL;
2026     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2027        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2028        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2029         if (nn == NULL) continue; // could not duplicate
2030         temp = g_list_prepend(temp, nn);
2031     }
2033     if (temp) {
2034         sp_nodepath_deselect(nodepath);
2035     }
2036     for (GList *l = temp; l != NULL; l = l->next) {
2037         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2038     }
2040     sp_nodepath_update_handles(nodepath);
2042     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2045 /**
2046  *  Internal function to join two nodes by merging them into one.
2047  */
2048 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2050     /* a and b are endpoints */
2052     // if one of the two nodes is mouseovered, fix its position
2053     NR::Point c;
2054     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2055         c = a->pos;
2056     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2057         c = b->pos;
2058     } else {
2059         // otherwise, move joined node to the midpoint
2060         c = (a->pos + b->pos) / 2;
2061     }
2063     if (a->subpath == b->subpath) {
2064        Inkscape::NodePath::SubPath *sp = a->subpath;
2065         sp_nodepath_subpath_close(sp);
2066         sp_node_moveto (sp->first, c);
2068         sp_nodepath_update_handles(sp->nodepath);
2069         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2070         return;
2071     }
2073     /* a and b are separate subpaths */
2074     Inkscape::NodePath::SubPath *sa = a->subpath;
2075     Inkscape::NodePath::SubPath *sb = b->subpath;
2076     NR::Point p;
2077     Inkscape::NodePath::Node *n;
2078     NRPathcode code;
2079     if (a == sa->first) {
2080         // we will now reverse sa, so that a is its last node, not first, and drop that node
2081         p = sa->first->n.pos;
2082         code = (NRPathcode)sa->first->n.other->code;
2083         // create new subpath
2084        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2085        // create a first moveto node on it
2086         n = sa->last;
2087         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2088         n = n->p.other;
2089         if (n == sa->first) n = NULL;
2090         while (n) {
2091             // copy the rest of the nodes from sa to t, going backwards
2092             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2093             n = n->p.other;
2094             if (n == sa->first) n = NULL;
2095         }
2096         // replace sa with t
2097         sp_nodepath_subpath_destroy(sa);
2098         sa = t;
2099     } else if (a == sa->last) {
2100         // a is already last, just drop it
2101         p = sa->last->p.pos;
2102         code = (NRPathcode)sa->last->code;
2103         sp_nodepath_node_destroy(sa->last);
2104     } else {
2105         code = NR_END;
2106         g_assert_not_reached();
2107     }
2109     if (b == sb->first) {
2110         // copy all nodes from b to a, forward 
2111         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2112         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2113             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2114         }
2115     } else if (b == sb->last) {
2116         // copy all nodes from b to a, backward 
2117         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2118         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2119             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2120         }
2121     } else {
2122         g_assert_not_reached();
2123     }
2124     /* and now destroy sb */
2126     sp_nodepath_subpath_destroy(sb);
2128     sp_nodepath_update_handles(sa->nodepath);
2130     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2132     sp_nodepath_update_statusbar(nodepath);
2135 /**
2136  *  Internal function to join two nodes by adding a segment between them.
2137  */
2138 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2140     if (a->subpath == b->subpath) {
2141        Inkscape::NodePath::SubPath *sp = a->subpath;
2143         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2144         sp->closed = TRUE;
2146         sp->first->p.other = sp->last;
2147         sp->last->n.other  = sp->first;
2149         sp_node_handle_mirror_p_to_n(sp->last);
2150         sp_node_handle_mirror_n_to_p(sp->first);
2152         sp->first->code = sp->last->code;
2153         sp->first       = sp->last;
2155         sp_nodepath_update_handles(sp->nodepath);
2157         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2159         return;
2160     }
2162     /* a and b are separate subpaths */
2163     Inkscape::NodePath::SubPath *sa = a->subpath;
2164     Inkscape::NodePath::SubPath *sb = b->subpath;
2166     Inkscape::NodePath::Node *n;
2167     NR::Point p;
2168     NRPathcode code;
2169     if (a == sa->first) {
2170         code = (NRPathcode) sa->first->n.other->code;
2171        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2172         n = sa->last;
2173         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2174         for (n = n->p.other; n != NULL; n = n->p.other) {
2175             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2176         }
2177         sp_nodepath_subpath_destroy(sa);
2178         sa = t;
2179     } else if (a == sa->last) {
2180         code = (NRPathcode)sa->last->code;
2181     } else {
2182         code = NR_END;
2183         g_assert_not_reached();
2184     }
2186     if (b == sb->first) {
2187         n = sb->first;
2188         sp_node_handle_mirror_p_to_n(sa->last);
2189         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2190         sp_node_handle_mirror_n_to_p(sa->last);
2191         for (n = n->n.other; n != NULL; n = n->n.other) {
2192             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2193         }
2194     } else if (b == sb->last) {
2195         n = sb->last;
2196         sp_node_handle_mirror_p_to_n(sa->last);
2197         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2198         sp_node_handle_mirror_n_to_p(sa->last);
2199         for (n = n->p.other; n != NULL; n = n->p.other) {
2200             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2201         }
2202     } else {
2203         g_assert_not_reached();
2204     }
2205     /* and now destroy sb */
2207     sp_nodepath_subpath_destroy(sb);
2209     sp_nodepath_update_handles(sa->nodepath);
2211     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2214 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2216 /**
2217  * Internal function to handle joining two nodes.
2218  */
2219 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2221     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2223     if (g_list_length(nodepath->selected) != 2) {
2224         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2225         return;
2226     }
2228     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2229     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2231     g_assert(a != b);
2232     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2233         // someone tried to join an orphan node (i.e. a single-node subpath).
2234         // this is not worth an error message, just fail silently.
2235         return;
2236     }
2238     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2239         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2240         return;
2241     }
2243     switch(mode) {
2244         case NODE_JOIN_ENDPOINTS:
2245             do_node_selected_join(nodepath, a, b);
2246             break;
2247         case NODE_JOIN_SEGMENT:
2248             do_node_selected_join_segment(nodepath, a, b);
2249             break;
2250     }
2253 /**
2254  *  Join two nodes by merging them into one.
2255  */
2256 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2258     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2261 /**
2262  *  Join two nodes by adding a segment between them.
2263  */
2264 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2266     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2269 /**
2270  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2271  */
2272 void sp_node_delete_preserve(GList *nodes_to_delete)
2274     GSList *nodepaths = NULL;
2276     while (nodes_to_delete) {
2277         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2278         Inkscape::NodePath::SubPath *sp = node->subpath;
2279         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2280         Inkscape::NodePath::Node *sample_cursor = NULL;
2281         Inkscape::NodePath::Node *sample_end = NULL;
2282         Inkscape::NodePath::Node *delete_cursor = node;
2283         bool just_delete = false;
2285         //find the start of this contiguous selection
2286         //move left to the first node that is not selected
2287         //or the start of the non-closed path
2288         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2289             delete_cursor = curr;
2290         }
2292         //just delete at the beginning of an open path
2293         if (!delete_cursor->p.other) {
2294             sample_cursor = delete_cursor;
2295             just_delete = true;
2296         } else {
2297             sample_cursor = delete_cursor->p.other;
2298         }
2300         //calculate points for each segment
2301         int rate = 5;
2302         float period = 1.0 / rate;
2303         std::vector<NR::Point> data;
2304         if (!just_delete) {
2305             data.push_back(sample_cursor->pos);
2306             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2307                 //just delete at the end of an open path
2308                 if (!sp->closed && curr == sp->last) {
2309                     just_delete = true;
2310                     break;
2311                 }
2313                 //sample points on the contiguous selected segment
2314                 NR::Point *bez;
2315                 bez = new NR::Point [4];
2316                 bez[0] = curr->pos;
2317                 bez[1] = curr->n.pos;
2318                 bez[2] = curr->n.other->p.pos;
2319                 bez[3] = curr->n.other->pos;
2320                 for (int i=1; i<rate; i++) {
2321                     gdouble t = i * period;
2322                     NR::Point p = bezier_pt(3, bez, t);
2323                     data.push_back(p);
2324                 }
2325                 data.push_back(curr->n.other->pos);
2327                 sample_end = curr->n.other;
2328                 //break if we've come full circle or hit the end of the selection
2329                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2330                     break;
2331                 }
2332             }
2333         }
2335         if (!just_delete) {
2336             //calculate the best fitting single segment and adjust the endpoints
2337             NR::Point *adata;
2338             adata = new NR::Point [data.size()];
2339             copy(data.begin(), data.end(), adata);
2341             NR::Point *bez;
2342             bez = new NR::Point [4];
2343             //would decreasing error create a better fitting approximation?
2344             gdouble error = 1.0;
2345             gint ret;
2346             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2348             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2349             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2350             //the resulting nodes behave as expected.
2351             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2352                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2353             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2354                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2356             //adjust endpoints
2357             sample_cursor->n.pos = bez[1];
2358             sample_end->p.pos = bez[2];
2359         }
2361         //destroy this contiguous selection
2362         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2363             Inkscape::NodePath::Node *temp = delete_cursor;
2364             if (delete_cursor->n.other == delete_cursor) {
2365                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2366                 delete_cursor = NULL;
2367             } else {
2368                 delete_cursor = delete_cursor->n.other;
2369             }
2370             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2371             sp_nodepath_node_destroy(temp);
2372         }
2374         sp_nodepath_update_handles(nodepath);
2376         if (!g_slist_find(nodepaths, nodepath))
2377             nodepaths = g_slist_prepend (nodepaths, nodepath);
2378     }
2380     for (GSList *i = nodepaths; i; i = i->next) {
2381         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2382         // different nodepaths will give us one undo event per nodepath
2383         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2385         // if the entire nodepath is removed, delete the selected object.
2386         if (nodepath->subpaths == NULL ||
2387             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2388             //at least 2
2389             sp_nodepath_get_node_count(nodepath) < 2) {
2390             SPDocument *document = sp_desktop_document (nodepath->desktop);
2391             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2392             //delete this nodepath's object, not the entire selection! (though at this time, this
2393             //does not matter)
2394             sp_selection_delete();
2395             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2396                               _("Delete nodes"));
2397         } else {
2398             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2399             sp_nodepath_update_statusbar(nodepath);
2400         }
2401     }
2403     g_slist_free (nodepaths);
2406 /**
2407  * Delete one or more selected nodes.
2408  */
2409 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2411     if (!nodepath) return;
2412     if (!nodepath->selected) return;
2414     /** \todo fixme: do it the right way */
2415     while (nodepath->selected) {
2416        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2417         sp_nodepath_node_destroy(node);
2418     }
2421     //clean up the nodepath (such as for trivial subpaths)
2422     sp_nodepath_cleanup(nodepath);
2424     sp_nodepath_update_handles(nodepath);
2426     // if the entire nodepath is removed, delete the selected object.
2427     if (nodepath->subpaths == NULL ||
2428         sp_nodepath_get_node_count(nodepath) < 2) {
2429         SPDocument *document = sp_desktop_document (nodepath->desktop);
2430         sp_selection_delete();
2431         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2432                           _("Delete nodes"));
2433         return;
2434     }
2436     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2438     sp_nodepath_update_statusbar(nodepath);
2441 /**
2442  * Delete one or more segments between two selected nodes.
2443  * This is the code for 'split'.
2444  */
2445 void
2446 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2448    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2449    Inkscape::NodePath::Node *curr, *next;     //Iterators
2451     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2453     if (g_list_length(nodepath->selected) != 2) {
2454         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2455                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2456         return;
2457     }
2459     //Selected nodes, not inclusive
2460    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2461    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2463     if ( ( a==b)                       ||  //same node
2464          (a->subpath  != b->subpath )  ||  //not the same path
2465          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2466          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2467     {
2468         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2469                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2470         return;
2471     }
2473     //###########################################
2474     //# BEGIN EDITS
2475     //###########################################
2476     //##################################
2477     //# CLOSED PATH
2478     //##################################
2479     if (a->subpath->closed) {
2482         gboolean reversed = FALSE;
2484         //Since we can go in a circle, we need to find the shorter distance.
2485         //  a->b or b->a
2486         start = end = NULL;
2487         int distance    = 0;
2488         int minDistance = 0;
2489         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2490             if (curr==b) {
2491                 //printf("a to b:%d\n", distance);
2492                 start = a;//go from a to b
2493                 end   = b;
2494                 minDistance = distance;
2495                 //printf("A to B :\n");
2496                 break;
2497             }
2498             distance++;
2499         }
2501         //try again, the other direction
2502         distance = 0;
2503         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2504             if (curr==a) {
2505                 //printf("b to a:%d\n", distance);
2506                 if (distance < minDistance) {
2507                     start    = b;  //we go from b to a
2508                     end      = a;
2509                     reversed = TRUE;
2510                     //printf("B to A\n");
2511                 }
2512                 break;
2513             }
2514             distance++;
2515         }
2518         //Copy everything from 'end' to 'start' to a new subpath
2519        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2520         for (curr=end ; curr ; curr=curr->n.other) {
2521             NRPathcode code = (NRPathcode) curr->code;
2522             if (curr == end)
2523                 code = NR_MOVETO;
2524             sp_nodepath_node_new(t, NULL,
2525                                  (Inkscape::NodePath::NodeType)curr->type, code,
2526                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2527             if (curr == start)
2528                 break;
2529         }
2530         sp_nodepath_subpath_destroy(a->subpath);
2533     }
2537     //##################################
2538     //# OPEN PATH
2539     //##################################
2540     else {
2542         //We need to get the direction of the list between A and B
2543         //Can we walk from a to b?
2544         start = end = NULL;
2545         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2546             if (curr==b) {
2547                 start = a;  //did it!  we go from a to b
2548                 end   = b;
2549                 //printf("A to B\n");
2550                 break;
2551             }
2552         }
2553         if (!start) {//didn't work?  let's try the other direction
2554             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2555                 if (curr==a) {
2556                     start = b;  //did it!  we go from b to a
2557                     end   = a;
2558                     //printf("B to A\n");
2559                     break;
2560                 }
2561             }
2562         }
2563         if (!start) {
2564             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2565                                                      _("Cannot find path between nodes."));
2566             return;
2567         }
2571         //Copy everything after 'end' to a new subpath
2572        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2573         for (curr=end ; curr ; curr=curr->n.other) {
2574             NRPathcode code = (NRPathcode) curr->code;
2575             if (curr == end)
2576                 code = NR_MOVETO;
2577             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2578                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2579         }
2581         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2582         for (curr = start->n.other ; curr  ; curr=next) {
2583             next = curr->n.other;
2584             sp_nodepath_node_destroy(curr);
2585         }
2587     }
2588     //###########################################
2589     //# END EDITS
2590     //###########################################
2592     //clean up the nodepath (such as for trivial subpaths)
2593     sp_nodepath_cleanup(nodepath);
2595     sp_nodepath_update_handles(nodepath);
2597     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2599     sp_nodepath_update_statusbar(nodepath);
2602 /**
2603  * Call sp_nodepath_set_line() for all selected segments.
2604  */
2605 void
2606 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2608     if (nodepath == NULL) return;
2610     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2611        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2612         g_assert(n->selected);
2613         if (n->p.other && n->p.other->selected) {
2614             sp_nodepath_set_line_type(n, code);
2615         }
2616     }
2618     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2621 /**
2622  * Call sp_nodepath_convert_node_type() for all selected nodes.
2623  */
2624 void
2625 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2627     if (nodepath == NULL) return;
2629     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2631     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2632         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2633     }
2635     sp_nodepath_update_repr(nodepath, _("Change node type"));
2638 /**
2639  * Change select status of node, update its own and neighbour handles.
2640  */
2641 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2643     node->selected = selected;
2645     if (selected) {
2646         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2647         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2648         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2649         sp_knot_update_ctrl(node->knot);
2650     } else {
2651         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2652         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2653         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2654         sp_knot_update_ctrl(node->knot);
2655     }
2657     sp_node_update_handles(node);
2658     if (node->n.other) sp_node_update_handles(node->n.other);
2659     if (node->p.other) sp_node_update_handles(node->p.other);
2662 /**
2663 \brief Select a node
2664 \param node     The node to select
2665 \param incremental   If true, add to selection, otherwise deselect others
2666 \param override   If true, always select this node, otherwise toggle selected status
2667 */
2668 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2670     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2672     if (incremental) {
2673         if (override) {
2674             if (!g_list_find(nodepath->selected, node)) {
2675                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2676             }
2677             sp_node_set_selected(node, TRUE);
2678         } else { // toggle
2679             if (node->selected) {
2680                 g_assert(g_list_find(nodepath->selected, node));
2681                 nodepath->selected = g_list_remove(nodepath->selected, node);
2682             } else {
2683                 g_assert(!g_list_find(nodepath->selected, node));
2684                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2685             }
2686             sp_node_set_selected(node, !node->selected);
2687         }
2688     } else {
2689         sp_nodepath_deselect(nodepath);
2690         nodepath->selected = g_list_prepend(nodepath->selected, node);
2691         sp_node_set_selected(node, TRUE);
2692     }
2694     sp_nodepath_update_statusbar(nodepath);
2698 /**
2699 \brief Deselect all nodes in the nodepath
2700 */
2701 void
2702 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2704     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2706     while (nodepath->selected) {
2707         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2708         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2709     }
2710     sp_nodepath_update_statusbar(nodepath);
2713 /**
2714 \brief Select or invert selection of all nodes in the nodepath
2715 */
2716 void
2717 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2719     if (!nodepath) return;
2721     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2722        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2723         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2724            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2725            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2726         }
2727     }
2730 /**
2731  * If nothing selected, does the same as sp_nodepath_select_all();
2732  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2733  * (i.e., similar to "select all in layer", with the "selected" subpaths
2734  * being treated as "layers" in the path).
2735  */
2736 void
2737 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2739     if (!nodepath) return;
2741     if (g_list_length (nodepath->selected) == 0) {
2742         sp_nodepath_select_all (nodepath, invert);
2743         return;
2744     }
2746     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2747     GSList *subpaths = NULL;
2749     for (GList *l = copy; l != NULL; l = l->next) {
2750         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2751         Inkscape::NodePath::SubPath *subpath = n->subpath;
2752         if (!g_slist_find (subpaths, subpath))
2753             subpaths = g_slist_prepend (subpaths, subpath);
2754     }
2756     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2757         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2758         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2759             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2760             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2761         }
2762     }
2764     g_slist_free (subpaths);
2765     g_list_free (copy);
2768 /**
2769  * \brief Select the node after the last selected; if none is selected,
2770  * select the first within path.
2771  */
2772 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2774     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2776    Inkscape::NodePath::Node *last = NULL;
2777     if (nodepath->selected) {
2778         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2779            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2780             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2781             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2782                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2783                 if (node->selected) {
2784                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2785                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2786                             if (spl->next) { // there's a next subpath
2787                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2788                                 last = subpath_next->first;
2789                             } else if (spl->prev) { // there's a previous subpath
2790                                 last = NULL; // to be set later to the first node of first subpath
2791                             } else {
2792                                 last = node->n.other;
2793                             }
2794                         } else {
2795                             last = node->n.other;
2796                         }
2797                     } else {
2798                         if (node->n.other) {
2799                             last = node->n.other;
2800                         } else {
2801                             if (spl->next) { // there's a next subpath
2802                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2803                                 last = subpath_next->first;
2804                             } else if (spl->prev) { // there's a previous subpath
2805                                 last = NULL; // to be set later to the first node of first subpath
2806                             } else {
2807                                 last = (Inkscape::NodePath::Node *) subpath->first;
2808                             }
2809                         }
2810                     }
2811                 }
2812             }
2813         }
2814         sp_nodepath_deselect(nodepath);
2815     }
2817     if (last) { // there's at least one more node after selected
2818         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2819     } else { // no more nodes, select the first one in first subpath
2820        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2821         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2822     }
2825 /**
2826  * \brief Select the node before the first selected; if none is selected,
2827  * select the last within path
2828  */
2829 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2831     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2833    Inkscape::NodePath::Node *last = NULL;
2834     if (nodepath->selected) {
2835         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2836            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2837             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2838                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2839                 if (node->selected) {
2840                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2841                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2842                             if (spl->prev) { // there's a prev subpath
2843                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2844                                 last = subpath_prev->last;
2845                             } else if (spl->next) { // there's a next subpath
2846                                 last = NULL; // to be set later to the last node of last subpath
2847                             } else {
2848                                 last = node->p.other;
2849                             }
2850                         } else {
2851                             last = node->p.other;
2852                         }
2853                     } else {
2854                         if (node->p.other) {
2855                             last = node->p.other;
2856                         } else {
2857                             if (spl->prev) { // there's a prev subpath
2858                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2859                                 last = subpath_prev->last;
2860                             } else if (spl->next) { // there's a next subpath
2861                                 last = NULL; // to be set later to the last node of last subpath
2862                             } else {
2863                                 last = (Inkscape::NodePath::Node *) subpath->last;
2864                             }
2865                         }
2866                     }
2867                 }
2868             }
2869         }
2870         sp_nodepath_deselect(nodepath);
2871     }
2873     if (last) { // there's at least one more node before selected
2874         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2875     } else { // no more nodes, select the last one in last subpath
2876         GList *spl = g_list_last(nodepath->subpaths);
2877        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2878         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2879     }
2882 /**
2883  * \brief Select all nodes that are within the rectangle.
2884  */
2885 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2887     if (!incremental) {
2888         sp_nodepath_deselect(nodepath);
2889     }
2891     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2892        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2893         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2894            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2896             if (b.contains(node->pos)) {
2897                 sp_nodepath_node_select(node, TRUE, TRUE);
2898             }
2899         }
2900     }
2904 void
2905 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2907     g_assert (n);
2908     g_assert (nodepath);
2909     g_assert (n->subpath->nodepath == nodepath);
2911     if (g_list_length (nodepath->selected) == 0) {
2912         if (grow > 0) {
2913             sp_nodepath_node_select(n, TRUE, TRUE);
2914         }
2915         return;
2916     }
2918     if (g_list_length (nodepath->selected) == 1) {
2919         if (grow < 0) {
2920             sp_nodepath_deselect (nodepath);
2921             return;
2922         }
2923     }
2925         double n_sel_range = 0, p_sel_range = 0;
2926             Inkscape::NodePath::Node *farthest_n_node = n;
2927             Inkscape::NodePath::Node *farthest_p_node = n;
2929         // Calculate ranges
2930         {
2931             double n_range = 0, p_range = 0;
2932             bool n_going = true, p_going = true;
2933             Inkscape::NodePath::Node *n_node = n;
2934             Inkscape::NodePath::Node *p_node = n;
2935             do {
2936                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2937                 if (n_node && n_going)
2938                     n_node = n_node->n.other;
2939                 if (n_node == NULL) {
2940                     n_going = false;
2941                 } else {
2942                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2943                     if (n_node->selected) {
2944                         n_sel_range = n_range;
2945                         farthest_n_node = n_node;
2946                     }
2947                     if (n_node == p_node) {
2948                         n_going = false;
2949                         p_going = false;
2950                     }
2951                 }
2952                 if (p_node && p_going)
2953                     p_node = p_node->p.other;
2954                 if (p_node == NULL) {
2955                     p_going = false;
2956                 } else {
2957                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2958                     if (p_node->selected) {
2959                         p_sel_range = p_range;
2960                         farthest_p_node = p_node;
2961                     }
2962                     if (p_node == n_node) {
2963                         n_going = false;
2964                         p_going = false;
2965                     }
2966                 }
2967             } while (n_going || p_going);
2968         }
2970     if (grow > 0) {
2971         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2972                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2973         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2974                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2975         }
2976     } else {
2977         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2978                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2979         } else if (farthest_p_node && farthest_p_node->selected) {
2980                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2981         }
2982     }
2985 void
2986 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2988     g_assert (n);
2989     g_assert (nodepath);
2990     g_assert (n->subpath->nodepath == nodepath);
2992     if (g_list_length (nodepath->selected) == 0) {
2993         if (grow > 0) {
2994             sp_nodepath_node_select(n, TRUE, TRUE);
2995         }
2996         return;
2997     }
2999     if (g_list_length (nodepath->selected) == 1) {
3000         if (grow < 0) {
3001             sp_nodepath_deselect (nodepath);
3002             return;
3003         }
3004     }
3006     Inkscape::NodePath::Node *farthest_selected = NULL;
3007     double farthest_dist = 0;
3009     Inkscape::NodePath::Node *closest_unselected = NULL;
3010     double closest_dist = NR_HUGE;
3012     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3013        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3014         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3015            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3016            if (node == n)
3017                continue;
3018            if (node->selected) {
3019                if (NR::L2(node->pos - n->pos) > farthest_dist) {
3020                    farthest_dist = NR::L2(node->pos - n->pos);
3021                    farthest_selected = node;
3022                }
3023            } else {
3024                if (NR::L2(node->pos - n->pos) < closest_dist) {
3025                    closest_dist = NR::L2(node->pos - n->pos);
3026                    closest_unselected = node;
3027                }
3028            }
3029         }
3030     }
3032     if (grow > 0) {
3033         if (closest_unselected) {
3034             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3035         }
3036     } else {
3037         if (farthest_selected) {
3038             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3039         }
3040     }
3044 /**
3045 \brief  Saves all nodes' and handles' current positions in their origin members
3046 */
3047 void
3048 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3050     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3051        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3052         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3053            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3054            n->origin = n->pos;
3055            n->p.origin = n->p.pos;
3056            n->n.origin = n->n.pos;
3057         }
3058     }
3061 /**
3062 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3063 */
3064 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3066     if (!nodepath->selected) {
3067         return NULL;
3068     }
3070     GList *r = NULL;
3071     guint i = 0;
3072     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3073        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3074         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3075            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3076             i++;
3077             if (node->selected) {
3078                 r = g_list_append(r, GINT_TO_POINTER(i));
3079             }
3080         }
3081     }
3082     return r;
3085 /**
3086 \brief  Restores selection by selecting nodes whose positions are in the list
3087 */
3088 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3090     sp_nodepath_deselect(nodepath);
3092     guint i = 0;
3093     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3094        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3095         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3096            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3097             i++;
3098             if (g_list_find(r, GINT_TO_POINTER(i))) {
3099                 sp_nodepath_node_select(node, TRUE, TRUE);
3100             }
3101         }
3102     }
3106 /**
3107 \brief Adjusts handle according to node type and line code.
3108 */
3109 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3111     g_assert(node);
3113    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3114    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3116    // nothing to do if we are an end node
3117     if (me->other == NULL) return;
3118     if (other->other == NULL) return;
3120     // nothing to do if we are a cusp node
3121     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3123     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3124     NRPathcode mecode;
3125     if (which_adjust == 1) {
3126         mecode = (NRPathcode)me->other->code;
3127     } else {
3128         mecode = (NRPathcode)node->code;
3129     }
3130     if (mecode == NR_LINETO) return;
3132     if (sp_node_side_is_line(node, other)) {
3133         // other is a line, and we are either smooth or symm
3134        Inkscape::NodePath::Node *othernode = other->other;
3135         double len = NR::L2(me->pos - node->pos);
3136         NR::Point delta = node->pos - othernode->pos;
3137         double linelen = NR::L2(delta);
3138         if (linelen < 1e-18)
3139             return;
3140         me->pos = node->pos + (len / linelen)*delta;
3141         return;
3142     }
3144     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3145         // symmetrize 
3146         me->pos = 2 * node->pos - other->pos;
3147         return;
3148     } else {
3149         // smoothify
3150         double len = NR::L2(me->pos - node->pos);
3151         NR::Point delta = other->pos - node->pos;
3152         double otherlen = NR::L2(delta);
3153         if (otherlen < 1e-18) return;
3154         me->pos = node->pos - (len / otherlen) * delta;
3155     }
3158 /**
3159  \brief Adjusts both handles according to node type and line code
3160  */
3161 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3163     g_assert(node);
3165     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3167     /* we are either smooth or symm */
3169     if (node->p.other == NULL) return;
3170     if (node->n.other == NULL) return;
3172     if (sp_node_side_is_line(node, &node->p)) {
3173         sp_node_adjust_handle(node, 1);
3174         return;
3175     }
3177     if (sp_node_side_is_line(node, &node->n)) {
3178         sp_node_adjust_handle(node, -1);
3179         return;
3180     }
3182     /* both are curves */
3183     NR::Point const delta( node->n.pos - node->p.pos );
3185     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3186         node->p.pos = node->pos - delta / 2;
3187         node->n.pos = node->pos + delta / 2;
3188         return;
3189     }
3191     /* We are smooth */
3192     double plen = NR::L2(node->p.pos - node->pos);
3193     if (plen < 1e-18) return;
3194     double nlen = NR::L2(node->n.pos - node->pos);
3195     if (nlen < 1e-18) return;
3196     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3197     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3200 /**
3201  * Node event callback.
3202  */
3203 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3205     gboolean ret = FALSE;
3206     switch (event->type) {
3207         case GDK_ENTER_NOTIFY:
3208             Inkscape::NodePath::Path::active_node = n;
3209             break;
3210         case GDK_LEAVE_NOTIFY:
3211             Inkscape::NodePath::Path::active_node = NULL;
3212             break;
3213         case GDK_SCROLL:
3214             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3215                 switch (event->scroll.direction) {
3216                     case GDK_SCROLL_UP:
3217                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3218                         break;
3219                     case GDK_SCROLL_DOWN:
3220                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3221                         break;
3222                     default:
3223                         break;
3224                 }
3225                 ret = TRUE;
3226             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3227                 switch (event->scroll.direction) {
3228                     case GDK_SCROLL_UP:
3229                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3230                         break;
3231                     case GDK_SCROLL_DOWN:
3232                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3233                         break;
3234                     default:
3235                         break;
3236                 }
3237                 ret = TRUE;
3238             }
3239             break;
3240         case GDK_KEY_PRESS:
3241             switch (get_group0_keyval (&event->key)) {
3242                 case GDK_space:
3243                     if (event->key.state & GDK_BUTTON1_MASK) {
3244                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3245                         stamp_repr(nodepath);
3246                         ret = TRUE;
3247                     }
3248                     break;
3249                 case GDK_Page_Up:
3250                     if (event->key.state & GDK_CONTROL_MASK) {
3251                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3252                     } else {
3253                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3254                     }
3255                     break;
3256                 case GDK_Page_Down:
3257                     if (event->key.state & GDK_CONTROL_MASK) {
3258                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3259                     } else {
3260                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3261                     }
3262                     break;
3263                 default:
3264                     break;
3265             }
3266             break;
3267         default:
3268             break;
3269     }
3271     return ret;
3274 /**
3275  * Handle keypress on node; directly called.
3276  */
3277 gboolean node_key(GdkEvent *event)
3279     Inkscape::NodePath::Path *np;
3281     // there is no way to verify nodes so set active_node to nil when deleting!!
3282     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3284     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3285         gint ret = FALSE;
3286         switch (get_group0_keyval (&event->key)) {
3287             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3288             case GDK_BackSpace:
3289                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3290                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3291                 sp_nodepath_update_repr(np, _("Delete node"));
3292                 Inkscape::NodePath::Path::active_node = NULL;
3293                 ret = TRUE;
3294                 break;
3295             case GDK_c:
3296                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3297                 ret = TRUE;
3298                 break;
3299             case GDK_s:
3300                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3301                 ret = TRUE;
3302                 break;
3303             case GDK_y:
3304                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3305                 ret = TRUE;
3306                 break;
3307             case GDK_b:
3308                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3309                 ret = TRUE;
3310                 break;
3311         }
3312         return ret;
3313     }
3314     return FALSE;
3317 /**
3318  * Mouseclick on node callback.
3319  */
3320 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3322    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3324     if (state & GDK_CONTROL_MASK) {
3325         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3327         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3328             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3329                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3330             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3331                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3332             } else {
3333                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3334             }
3335             sp_nodepath_update_repr(nodepath, _("Change node type"));
3336             sp_nodepath_update_statusbar(nodepath);
3338         } else { //ctrl+alt+click: delete node
3339             GList *node_to_delete = NULL;
3340             node_to_delete = g_list_append(node_to_delete, n);
3341             sp_node_delete_preserve(node_to_delete);
3342         }
3344     } else {
3345         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3346     }
3349 /**
3350  * Mouse grabbed node callback.
3351  */
3352 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3354    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3356     if (!n->selected) {
3357         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3358     }
3360     n->is_dragging = true;
3361     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3363     sp_nodepath_remember_origins (n->subpath->nodepath);
3366 /**
3367  * Mouse ungrabbed node callback.
3368  */
3369 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3371    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3373    n->dragging_out = NULL;
3374    n->is_dragging = false;
3375    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3377    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3380 /**
3381  * The point on a line, given by its angle, closest to the given point.
3382  * \param p  A point.
3383  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3384  * \param closest  Pointer to the point struct where the result is stored.
3385  * \todo FIXME: use dot product perhaps?
3386  */
3387 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3389     if (a == HUGE_VAL) { // vertical
3390         *closest = NR::Point(0, (*p)[NR::Y]);
3391     } else {
3392         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3393         (*closest)[NR::Y] = a * (*closest)[NR::X];
3394     }
3397 /**
3398  * Distance from the point to a line given by its angle.
3399  * \param p  A point.
3400  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3401  */
3402 static double point_line_distance(NR::Point *p, double a)
3404     NR::Point c;
3405     point_line_closest(p, a, &c);
3406     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]));
3409 /**
3410  * Callback for node "request" signal.
3411  * \todo fixme: This goes to "moved" event? (lauris)
3412  */
3413 static gboolean
3414 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3416     double yn, xn, yp, xp;
3417     double an, ap, na, pa;
3418     double d_an, d_ap, d_na, d_pa;
3419     gboolean collinear = FALSE;
3420     NR::Point c;
3421     NR::Point pr;
3423     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3425     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3427     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3428     if ( (!n->subpath->nodepath->straight_path) &&
3429          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3430            || n->dragging_out ) )
3431     {
3432        NR::Point mouse = (*p);
3434        if (!n->dragging_out) {
3435            // This is the first drag-out event; find out which handle to drag out
3436            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3437            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3439            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3440                return FALSE;
3442            Inkscape::NodePath::NodeSide *opposite;
3443            if (appr_p > appr_n) { // closer to p
3444                n->dragging_out = &n->p;
3445                opposite = &n->n;
3446                n->code = NR_CURVETO;
3447            } else if (appr_p < appr_n) { // closer to n
3448                n->dragging_out = &n->n;
3449                opposite = &n->p;
3450                n->n.other->code = NR_CURVETO;
3451            } else { // p and n nodes are the same
3452                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3453                    n->dragging_out = &n->p;
3454                    opposite = &n->n;
3455                    n->code = NR_CURVETO;
3456                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3457                    n->dragging_out = &n->n;
3458                    opposite = &n->p;
3459                    n->n.other->code = NR_CURVETO;
3460                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3461                    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);
3462                    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);
3463                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3464                        n->dragging_out = &n->n;
3465                        opposite = &n->p;
3466                        n->n.other->code = NR_CURVETO;
3467                    } else { // closer to other's n handle
3468                        n->dragging_out = &n->p;
3469                        opposite = &n->n;
3470                        n->code = NR_CURVETO;
3471                    }
3472                }
3473            }
3475            // if there's another handle, make sure the one we drag out starts parallel to it
3476            if (opposite->pos != n->pos) {
3477                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3478            }
3480            // knots might not be created yet!
3481            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3482            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3483        }
3485        // pass this on to the handle-moved callback
3486        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3487        sp_node_update_handles(n);
3488        return TRUE;
3489    }
3491     if (state & GDK_CONTROL_MASK) { // constrained motion
3493         // calculate relative distances of handles
3494         // n handle:
3495         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3496         xn = n->n.pos[NR::X] - n->pos[NR::X];
3497         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3498         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3499             if (n->n.other) { // if there is the next point
3500                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3501                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3502                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3503             }
3504         }
3505         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3506         if (yn < 0) { xn = -xn; yn = -yn; }
3508         // p handle:
3509         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3510         xp = n->p.pos[NR::X] - n->pos[NR::X];
3511         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3512         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3513             if (n->p.other) {
3514                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3515                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3516                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3517             }
3518         }
3519         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3520         if (yp < 0) { xp = -xp; yp = -yp; }
3522         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3523             // sliding on handles, only if at least one of the handles is non-vertical
3524             // (otherwise it's the same as ctrl+drag anyway)
3526             // calculate angles of the handles
3527             if (xn == 0) {
3528                 if (yn == 0) { // no handle, consider it the continuation of the other one
3529                     an = 0;
3530                     collinear = TRUE;
3531                 }
3532                 else an = 0; // vertical; set the angle to horizontal
3533             } else an = yn/xn;
3535             if (xp == 0) {
3536                 if (yp == 0) { // no handle, consider it the continuation of the other one
3537                     ap = an;
3538                 }
3539                 else ap = 0; // vertical; set the angle to horizontal
3540             } else  ap = yp/xp;
3542             if (collinear) an = ap;
3544             // angles of the perpendiculars; HUGE_VAL means vertical
3545             if (an == 0) na = HUGE_VAL; else na = -1/an;
3546             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3548             // mouse point relative to the node's original pos
3549             pr = (*p) - n->origin;
3551             // distances to the four lines (two handles and two perpendiculars)
3552             d_an = point_line_distance(&pr, an);
3553             d_na = point_line_distance(&pr, na);
3554             d_ap = point_line_distance(&pr, ap);
3555             d_pa = point_line_distance(&pr, pa);
3557             // find out which line is the closest, save its closest point in c
3558             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3559                 point_line_closest(&pr, an, &c);
3560             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3561                 point_line_closest(&pr, ap, &c);
3562             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3563                 point_line_closest(&pr, na, &c);
3564             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3565                 point_line_closest(&pr, pa, &c);
3566             }
3568             // move the node to the closest point
3569             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3570                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3571                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y], 
3572                                             true);
3574         } else {  // constraining to hor/vert
3576             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3577                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3578                                                 (*p)[NR::X] - n->pos[NR::X], 
3579                                                 n->origin[NR::Y] - n->pos[NR::Y],
3580                                                 true, 
3581                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::X]));
3582             } else { // snap to vert
3583                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3584                                                 n->origin[NR::X] - n->pos[NR::X],
3585                                                 (*p)[NR::Y] - n->pos[NR::Y],
3586                                                 true,
3587                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::Y]));
3588             }
3589         }
3590     } else { // move freely
3591         if (n->is_dragging) {
3592             if (state & GDK_MOD1_MASK) { // sculpt
3593                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3594             } else {
3595                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3596                                             (*p)[NR::X] - n->pos[NR::X],
3597                                             (*p)[NR::Y] - n->pos[NR::Y],
3598                                             (state & GDK_SHIFT_MASK) == 0);
3599             }
3600         }
3601     }
3603     n->subpath->nodepath->desktop->scroll_to_point(p);
3605     return TRUE;
3608 /**
3609  * Node handle clicked callback.
3610  */
3611 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3613    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3615     if (state & GDK_CONTROL_MASK) { // "delete" handle
3616         if (n->p.knot == knot) {
3617             n->p.pos = n->pos;
3618         } else if (n->n.knot == knot) {
3619             n->n.pos = n->pos;
3620         }
3621         sp_node_update_handles(n);
3622         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3623         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3624         sp_nodepath_update_statusbar(nodepath);
3626     } else { // just select or add to selection, depending in Shift
3627         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3628     }
3631 /**
3632  * Node handle grabbed callback.
3633  */
3634 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3636    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3638     if (!n->selected) {
3639         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3640     }
3642     // remember the origin point of the handle
3643     if (n->p.knot == knot) {
3644         n->p.origin_radial = n->p.pos - n->pos;
3645     } else if (n->n.knot == knot) {
3646         n->n.origin_radial = n->n.pos - n->pos;
3647     } else {
3648         g_assert_not_reached();
3649     }
3651     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3654 /**
3655  * Node handle ungrabbed callback.
3656  */
3657 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3659    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3661     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3662     if (n->p.knot == knot) {
3663         n->p.origin_radial.a = 0;
3664         sp_knot_set_position(knot, &n->p.pos, state);
3665     } else if (n->n.knot == knot) {
3666         n->n.origin_radial.a = 0;
3667         sp_knot_set_position(knot, &n->n.pos, state);
3668     } else {
3669         g_assert_not_reached();
3670     }
3672     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3675 /**
3676  * Node handle "request" signal callback.
3677  */
3678 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint /*state*/, gpointer data)
3680     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3682     Inkscape::NodePath::NodeSide *me, *opposite;
3683     gint which;
3684     if (n->p.knot == knot) {
3685         me = &n->p;
3686         opposite = &n->n;
3687         which = -1;
3688     } else if (n->n.knot == knot) {
3689         me = &n->n;
3690         opposite = &n->p;
3691         which = 1;
3692     } else {
3693         me = opposite = NULL;
3694         which = 0;
3695         g_assert_not_reached();
3696     }
3698     SPDesktop *desktop = n->subpath->nodepath->desktop;
3699     SnapManager &m = desktop->namedview->snap_manager;
3700     m.setup(desktop, n->subpath->nodepath->item);
3701     Inkscape::SnappedPoint s ;
3703     Inkscape::NodePath::Node *othernode = opposite->other;
3704     if (othernode) {
3705         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3706             /* We are smooth node adjacent with line */
3707             NR::Point const delta = *p - n->pos;
3708             NR::Coord const len = NR::L2(delta);
3709             Inkscape::NodePath::Node *othernode = opposite->other;
3710             NR::Point const ndelta = n->pos - othernode->pos;
3711             NR::Coord const linelen = NR::L2(ndelta);
3712             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3713                 NR::Coord const scal = dot(delta, ndelta) / linelen;
3714                 (*p) = n->pos + (scal / linelen) * ndelta;
3715             }
3716             s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta));
3717         } else {
3718             s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3719         }
3720     } else {
3721         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3722     }
3723     
3724     s.getPoint(*p);
3725     
3726     sp_node_adjust_handle(n, -which);
3728     return FALSE;
3731 /**
3732  * Node handle moved callback.
3733  */
3734 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3736    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3738    Inkscape::NodePath::NodeSide *me;
3739    Inkscape::NodePath::NodeSide *other;
3740     if (n->p.knot == knot) {
3741         me = &n->p;
3742         other = &n->n;
3743     } else if (n->n.knot == knot) {
3744         me = &n->n;
3745         other = &n->p;
3746     } else {
3747         me = NULL;
3748         other = NULL;
3749         g_assert_not_reached();
3750     }
3752     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3753     Radial rme(me->pos - n->pos);
3754     Radial rother(other->pos - n->pos);
3755     Radial rnew(*p - n->pos);
3757     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3758         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3759         /* 0 interpreted as "no snapping". */
3761         // 1. Snap to the closest PI/snaps angle, starting from zero.
3762         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3764         // 2. Snap to the original angle, its opposite and perpendiculars
3765         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3766             /* The closest PI/2 angle, starting from original angle */
3767             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3769             // Snap to the closest.
3770             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3771                        ? a_snapped
3772                        : a_ortho );
3773         }
3775         // 3. Snap to the angle of the opposite line, if any
3776         Inkscape::NodePath::Node *othernode = other->other;
3777         if (othernode) {
3778             NR::Point other_to_snap(0,0);
3779             if (sp_node_side_is_line(n, other)) {
3780                 other_to_snap = othernode->pos - n->pos;
3781             } else {
3782                 other_to_snap = other->pos - n->pos;
3783             }
3784             if (NR::L2(other_to_snap) > 1e-3) {
3785                 Radial rother_to_snap(other_to_snap);
3786                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
3787                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
3789                 // Snap to the closest.
3790                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
3791                        ? a_snapped
3792                        : a_oppo );
3793             }
3794         }
3796         rnew.a = a_snapped;
3797     }
3799     if (state & GDK_MOD1_MASK) {
3800         // lock handle length
3801         rnew.r = me->origin_radial.r;
3802     }
3804     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3805         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3806         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3807         rother.a += rnew.a - rme.a;
3808         other->pos = NR::Point(rother) + n->pos;
3809         if (other->knot) {
3810             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3811             sp_knot_moveto(other->knot, &other->pos);
3812         }
3813     }
3815     me->pos = NR::Point(rnew) + n->pos;
3816     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3818     // move knot, but without emitting the signal:
3819     // we cannot emit a "moved" signal because we're now processing it
3820     sp_knot_moveto(me->knot, &(me->pos));
3822     update_object(n->subpath->nodepath);
3824     /* status text */
3825     SPDesktop *desktop = n->subpath->nodepath->desktop;
3826     if (!desktop) return;
3827     SPEventContext *ec = desktop->event_context;
3828     if (!ec) return;
3829     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3830     if (!mc) return;
3832     double degrees = 180 / M_PI * rnew.a;
3833     if (degrees > 180) degrees -= 360;
3834     if (degrees < -180) degrees += 360;
3835     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3836         degrees = angle_to_compass (degrees);
3838     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3840     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
3841          _("<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);
3843     g_string_free(length, TRUE);
3846 /**
3847  * Node handle event callback.
3848  */
3849 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3851     gboolean ret = FALSE;
3852     switch (event->type) {
3853         case GDK_KEY_PRESS:
3854             switch (get_group0_keyval (&event->key)) {
3855                 case GDK_space:
3856                     if (event->key.state & GDK_BUTTON1_MASK) {
3857                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3858                         stamp_repr(nodepath);
3859                         ret = TRUE;
3860                     }
3861                     break;
3862                 default:
3863                     break;
3864             }
3865             break;
3866         case GDK_ENTER_NOTIFY:
3867             // we use an experimentally determined threshold that seems to work fine
3868             if (NR::L2(n->pos - knot->pos) < 0.75)
3869                 Inkscape::NodePath::Path::active_node = n;
3870             break;
3871         case GDK_LEAVE_NOTIFY:
3872             // we use an experimentally determined threshold that seems to work fine
3873             if (NR::L2(n->pos - knot->pos) < 0.75)
3874                 Inkscape::NodePath::Path::active_node = NULL;
3875             break;
3876         default:
3877             break;
3878     }
3880     return ret;
3883 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3884                                  Radial &rme, Radial &rother, gboolean const both)
3886     rme.a += angle;
3887     if ( both
3888          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3889          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3890     {
3891         rother.a += angle;
3892     }
3895 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3896                                         Radial &rme, Radial &rother, gboolean const both)
3898     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3900     gdouble r;
3901     if ( both
3902          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3903          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3904     {
3905         r = MAX(rme.r, rother.r);
3906     } else {
3907         r = rme.r;
3908     }
3910     gdouble const weird_angle = atan2(norm_angle, r);
3911 /* Bulia says norm_angle is just the visible distance that the
3912  * object's end must travel on the screen.  Left as 'angle' for want of
3913  * a better name.*/
3915     rme.a += weird_angle;
3916     if ( both
3917          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3918          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3919     {
3920         rother.a += weird_angle;
3921     }
3924 /**
3925  * Rotate one node.
3926  */
3927 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3929     Inkscape::NodePath::NodeSide *me, *other;
3930     bool both = false;
3932     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3933     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3935     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3936         me = &(n->p);
3937         other = &(n->n);
3938     } else if (!n->p.other) {
3939         me = &(n->n);
3940         other = &(n->p);
3941     } else {
3942         if (which > 0) { // right handle
3943             if (xn > xp) {
3944                 me = &(n->n);
3945                 other = &(n->p);
3946             } else {
3947                 me = &(n->p);
3948                 other = &(n->n);
3949             }
3950         } else if (which < 0){ // left handle
3951             if (xn <= xp) {
3952                 me = &(n->n);
3953                 other = &(n->p);
3954             } else {
3955                 me = &(n->p);
3956                 other = &(n->n);
3957             }
3958         } else { // both handles
3959             me = &(n->n);
3960             other = &(n->p);
3961             both = true;
3962         }
3963     }
3965     Radial rme(me->pos - n->pos);
3966     Radial rother(other->pos - n->pos);
3968     if (screen) {
3969         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3970     } else {
3971         node_rotate_one_internal (*n, angle, rme, rother, both);
3972     }
3974     me->pos = n->pos + NR::Point(rme);
3976     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3977         other->pos =  n->pos + NR::Point(rother);
3978     }
3980     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3981     // so here we just move all the knots without emitting move signals, for speed
3982     sp_node_update_handles(n, false);
3985 /**
3986  * Rotate selected nodes.
3987  */
3988 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3990     if (!nodepath || !nodepath->selected) return;
3992     if (g_list_length(nodepath->selected) == 1) {
3993        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3994         node_rotate_one (n, angle, which, screen);
3995     } else {
3996        // rotate as an object:
3998         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3999         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4000         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4001             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4002             box.expandTo (n->pos); // contain all selected nodes
4003         }
4005         gdouble rot;
4006         if (screen) {
4007             gdouble const zoom = nodepath->desktop->current_zoom();
4008             gdouble const zmove = angle / zoom;
4009             gdouble const r = NR::L2(box.max() - box.midpoint());
4010             rot = atan2(zmove, r);
4011         } else {
4012             rot = angle;
4013         }
4015         NR::Point rot_center;
4016         if (Inkscape::NodePath::Path::active_node == NULL)
4017             rot_center = box.midpoint();
4018         else
4019             rot_center = Inkscape::NodePath::Path::active_node->pos;
4021         NR::Matrix t =
4022             NR::Matrix (NR::translate(-rot_center)) *
4023             NR::Matrix (NR::rotate(rot)) *
4024             NR::Matrix (NR::translate(rot_center));
4026         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4027             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4028             n->pos *= t;
4029             n->n.pos *= t;
4030             n->p.pos *= t;
4031             sp_node_update_handles(n, false);
4032         }
4033     }
4035     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4038 /**
4039  * Scale one node.
4040  */
4041 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4043     bool both = false;
4044     Inkscape::NodePath::NodeSide *me, *other;
4046     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4047     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4049     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4050         me = &(n->p);
4051         other = &(n->n);
4052         n->code = NR_CURVETO;
4053     } else if (!n->p.other) {
4054         me = &(n->n);
4055         other = &(n->p);
4056         if (n->n.other)
4057             n->n.other->code = NR_CURVETO;
4058     } else {
4059         if (which > 0) { // right handle
4060             if (xn > xp) {
4061                 me = &(n->n);
4062                 other = &(n->p);
4063                 if (n->n.other)
4064                     n->n.other->code = NR_CURVETO;
4065             } else {
4066                 me = &(n->p);
4067                 other = &(n->n);
4068                 n->code = NR_CURVETO;
4069             }
4070         } else if (which < 0){ // left handle
4071             if (xn <= xp) {
4072                 me = &(n->n);
4073                 other = &(n->p);
4074                 if (n->n.other)
4075                     n->n.other->code = NR_CURVETO;
4076             } else {
4077                 me = &(n->p);
4078                 other = &(n->n);
4079                 n->code = NR_CURVETO;
4080             }
4081         } else { // both handles
4082             me = &(n->n);
4083             other = &(n->p);
4084             both = true;
4085             n->code = NR_CURVETO;
4086             if (n->n.other)
4087                 n->n.other->code = NR_CURVETO;
4088         }
4089     }
4091     Radial rme(me->pos - n->pos);
4092     Radial rother(other->pos - n->pos);
4094     rme.r += grow;
4095     if (rme.r < 0) rme.r = 0;
4096     if (rme.a == HUGE_VAL) {
4097         if (me->other) { // if direction is unknown, initialize it towards the next node
4098             Radial rme_next(me->other->pos - n->pos);
4099             rme.a = rme_next.a;
4100         } else { // if there's no next, initialize to 0
4101             rme.a = 0;
4102         }
4103     }
4104     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4105         rother.r += grow;
4106         if (rother.r < 0) rother.r = 0;
4107         if (rother.a == HUGE_VAL) {
4108             rother.a = rme.a + M_PI;
4109         }
4110     }
4112     me->pos = n->pos + NR::Point(rme);
4114     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4115         other->pos = n->pos + NR::Point(rother);
4116     }
4118     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4119     // so here we just move all the knots without emitting move signals, for speed
4120     sp_node_update_handles(n, false);
4123 /**
4124  * Scale selected nodes.
4125  */
4126 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4128     if (!nodepath || !nodepath->selected) return;
4130     if (g_list_length(nodepath->selected) == 1) {
4131         // scale handles of the single selected node
4132         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4133         node_scale_one (n, grow, which);
4134     } else {
4135         // scale nodes as an "object":
4137         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4138         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4139         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4140             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4141             box.expandTo (n->pos); // contain all selected nodes
4142         }
4144         double scale = (box.maxExtent() + grow)/box.maxExtent();
4146         NR::Point scale_center;
4147         if (Inkscape::NodePath::Path::active_node == NULL)
4148             scale_center = box.midpoint();
4149         else
4150             scale_center = Inkscape::NodePath::Path::active_node->pos;
4152         NR::Matrix t =
4153             NR::Matrix (NR::translate(-scale_center)) *
4154             NR::Matrix (NR::scale(scale, scale)) *
4155             NR::Matrix (NR::translate(scale_center));
4157         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4158             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4159             n->pos *= t;
4160             n->n.pos *= t;
4161             n->p.pos *= t;
4162             sp_node_update_handles(n, false);
4163         }
4164     }
4166     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4169 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4171     if (!nodepath) return;
4172     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4175 /**
4176  * Flip selected nodes horizontally/vertically.
4177  */
4178 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
4180     if (!nodepath || !nodepath->selected) return;
4182     if (g_list_length(nodepath->selected) == 1 && !center) {
4183         // flip handles of the single selected node
4184         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4185         double temp = n->p.pos[axis];
4186         n->p.pos[axis] = n->n.pos[axis];
4187         n->n.pos[axis] = temp;
4188         sp_node_update_handles(n, false);
4189     } else {
4190         // scale nodes as an "object":
4192         NR::Rect box = sp_node_selected_bbox (nodepath);
4193         if (!center) {
4194             center = box.midpoint();
4195         }
4196         NR::Matrix t =
4197             NR::Matrix (NR::translate(- *center)) *
4198             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
4199             NR::Matrix (NR::translate(*center));
4201         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4202             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4203             n->pos *= t;
4204             n->n.pos *= t;
4205             n->p.pos *= t;
4206             sp_node_update_handles(n, false);
4207         }
4208     }
4210     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4213 NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4215     g_assert (nodepath->selected);
4217     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4218     NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4219     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4220         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4221         box.expandTo (n->pos); // contain all selected nodes
4222     }
4223     return box;
4226 //-----------------------------------------------
4227 /**
4228  * Return new subpath under given nodepath.
4229  */
4230 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4232     g_assert(nodepath);
4233     g_assert(nodepath->desktop);
4235    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4237     s->nodepath = nodepath;
4238     s->closed = FALSE;
4239     s->nodes = NULL;
4240     s->first = NULL;
4241     s->last = NULL;
4243     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4244     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4245     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4247     return s;
4250 /**
4251  * Destroy nodes in subpath, then subpath itself.
4252  */
4253 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4255     g_assert(subpath);
4256     g_assert(subpath->nodepath);
4257     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4259     while (subpath->nodes) {
4260         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4261     }
4263     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4265     g_free(subpath);
4268 /**
4269  * Link head to tail in subpath.
4270  */
4271 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4273     g_assert(!sp->closed);
4274     g_assert(sp->last != sp->first);
4275     g_assert(sp->first->code == NR_MOVETO);
4277     sp->closed = TRUE;
4279     //Link the head to the tail
4280     sp->first->p.other = sp->last;
4281     sp->last->n.other  = sp->first;
4282     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4283     sp->first          = sp->last;
4285     //Remove the extra end node
4286     sp_nodepath_node_destroy(sp->last->n.other);
4289 /**
4290  * Open closed (loopy) subpath at node.
4291  */
4292 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4294     g_assert(sp->closed);
4295     g_assert(n->subpath == sp);
4296     g_assert(sp->first == sp->last);
4298     /* We create new startpoint, current node will become last one */
4300    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4301                                                 &n->pos, &n->pos, &n->n.pos);
4304     sp->closed        = FALSE;
4306     //Unlink to make a head and tail
4307     sp->first         = new_path;
4308     sp->last          = n;
4309     n->n.other        = NULL;
4310     new_path->p.other = NULL;
4313 /**
4314  * Return new node in subpath with given properties.
4315  * \param pos Position of node.
4316  * \param ppos Handle position in previous direction
4317  * \param npos Handle position in previous direction
4318  */
4319 Inkscape::NodePath::Node *
4320 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)
4322     g_assert(sp);
4323     g_assert(sp->nodepath);
4324     g_assert(sp->nodepath->desktop);
4326     if (nodechunk == NULL)
4327         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4329     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4331     n->subpath  = sp;
4333     if (type != Inkscape::NodePath::NODE_NONE) {
4334         // use the type from sodipodi:nodetypes
4335         n->type = type;
4336     } else {
4337         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4338             // points are (almost) collinear
4339             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4340                 // endnode, or a node with a retracted handle
4341                 n->type = Inkscape::NodePath::NODE_CUSP;
4342             } else {
4343                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4344             }
4345         } else {
4346             n->type = Inkscape::NodePath::NODE_CUSP;
4347         }
4348     }
4350     n->code     = code;
4351     n->selected = FALSE;
4352     n->pos      = *pos;
4353     n->p.pos    = *ppos;
4354     n->n.pos    = *npos;
4356     n->dragging_out = NULL;
4358     Inkscape::NodePath::Node *prev;
4359     if (next) {
4360         //g_assert(g_list_find(sp->nodes, next));
4361         prev = next->p.other;
4362     } else {
4363         prev = sp->last;
4364     }
4366     if (prev)
4367         prev->n.other = n;
4368     else
4369         sp->first = n;
4371     if (next)
4372         next->p.other = n;
4373     else
4374         sp->last = n;
4376     n->p.other = prev;
4377     n->n.other = next;
4379     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"));
4380     sp_knot_set_position(n->knot, pos, 0);
4382     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4383     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4384     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4385     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4386     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4387     sp_knot_update_ctrl(n->knot);
4389     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4390     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4391     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4392     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4393     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4394     sp_knot_show(n->knot);
4396     // We only create handle knots and lines on demand
4397     n->p.knot = NULL;
4398     n->p.line = NULL;
4399     n->n.knot = NULL;
4400     n->n.line = NULL;
4402     sp->nodes = g_list_prepend(sp->nodes, n);
4404     return n;
4407 /**
4408  * Destroy node and its knots, link neighbors in subpath.
4409  */
4410 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4412     g_assert(node);
4413     g_assert(node->subpath);
4414     g_assert(SP_IS_KNOT(node->knot));
4416    Inkscape::NodePath::SubPath *sp = node->subpath;
4418     if (node->selected) { // first, deselect
4419         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4420         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4421     }
4423     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4425     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4426     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4427     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4428     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4429     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4430     g_object_unref(G_OBJECT(node->knot));
4432     if (node->p.knot) {
4433         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4434         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4435         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4436         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4437         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4438         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4439         g_object_unref(G_OBJECT(node->p.knot));
4440         node->p.knot = NULL;
4441     }
4443     if (node->n.knot) {
4444         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4445         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4446         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4447         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4448         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4449         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4450         g_object_unref(G_OBJECT(node->n.knot));
4451         node->n.knot = NULL;
4452     }
4454     if (node->p.line)
4455         gtk_object_destroy(GTK_OBJECT(node->p.line));
4456     if (node->n.line)
4457         gtk_object_destroy(GTK_OBJECT(node->n.line));
4459     if (sp->nodes) { // there are others nodes on the subpath
4460         if (sp->closed) {
4461             if (sp->first == node) {
4462                 g_assert(sp->last == node);
4463                 sp->first = node->n.other;
4464                 sp->last = sp->first;
4465             }
4466             node->p.other->n.other = node->n.other;
4467             node->n.other->p.other = node->p.other;
4468         } else {
4469             if (sp->first == node) {
4470                 sp->first = node->n.other;
4471                 sp->first->code = NR_MOVETO;
4472             }
4473             if (sp->last == node) sp->last = node->p.other;
4474             if (node->p.other) node->p.other->n.other = node->n.other;
4475             if (node->n.other) node->n.other->p.other = node->p.other;
4476         }
4477     } else { // this was the last node on subpath
4478         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4479     }
4481     g_mem_chunk_free(nodechunk, node);
4484 /**
4485  * Returns one of the node's two sides.
4486  * \param which Indicates which side.
4487  * \return Pointer to previous node side if which==-1, next if which==1.
4488  */
4489 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4491     g_assert(node);
4493     switch (which) {
4494         case -1:
4495             return &node->p;
4496         case 1:
4497             return &node->n;
4498         default:
4499             break;
4500     }
4502     g_assert_not_reached();
4504     return NULL;
4507 /**
4508  * Return the other side of the node, given one of its sides.
4509  */
4510 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4512     g_assert(node);
4514     if (me == &node->p) return &node->n;
4515     if (me == &node->n) return &node->p;
4517     g_assert_not_reached();
4519     return NULL;
4522 /**
4523  * Return NRPathcode on the given side of the node.
4524  */
4525 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4527     g_assert(node);
4529     if (me == &node->p) {
4530         if (node->p.other) return (NRPathcode)node->code;
4531         return NR_MOVETO;
4532     }
4534     if (me == &node->n) {
4535         if (node->n.other) return (NRPathcode)node->n.other->code;
4536         return NR_MOVETO;
4537     }
4539     g_assert_not_reached();
4541     return NR_END;
4544 /**
4545  * Return node with the given index
4546  */
4547 Inkscape::NodePath::Node *
4548 sp_nodepath_get_node_by_index(int index)
4550     Inkscape::NodePath::Node *e = NULL;
4552     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4553     if (!nodepath) {
4554         return e;
4555     }
4557     //find segment
4558     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4560         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4561         int n = g_list_length(sp->nodes);
4562         if (sp->closed) {
4563             n++;
4564         }
4566         //if the piece belongs to this subpath grab it
4567         //otherwise move onto the next subpath
4568         if (index < n) {
4569             e = sp->first;
4570             for (int i = 0; i < index; ++i) {
4571                 e = e->n.other;
4572             }
4573             break;
4574         } else {
4575             if (sp->closed) {
4576                 index -= (n+1);
4577             } else {
4578                 index -= n;
4579             }
4580         }
4581     }
4583     return e;
4586 /**
4587  * Returns plain text meaning of node type.
4588  */
4589 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4591     unsigned retracted = 0;
4592     bool endnode = false;
4594     for (int which = -1; which <= 1; which += 2) {
4595         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4596         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4597             retracted ++;
4598         if (!side->other)
4599             endnode = true;
4600     }
4602     if (retracted == 0) {
4603         if (endnode) {
4604                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4605                 return _("end node");
4606         } else {
4607             switch (node->type) {
4608                 case Inkscape::NodePath::NODE_CUSP:
4609                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4610                     return _("cusp");
4611                 case Inkscape::NodePath::NODE_SMOOTH:
4612                     // TRANSLATORS: "smooth" is an adjective here
4613                     return _("smooth");
4614                 case Inkscape::NodePath::NODE_SYMM:
4615                     return _("symmetric");
4616             }
4617         }
4618     } else if (retracted == 1) {
4619         if (endnode) {
4620             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4621             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4622         } else {
4623             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4624         }
4625     } else {
4626         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4627     }
4629     return NULL;
4632 /**
4633  * Handles content of statusbar as long as node tool is active.
4634  */
4635 void
4636 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4638     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");
4639     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4641     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4642     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4643     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4644     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4646     SPDesktop *desktop = NULL;
4647     if (nodepath) {
4648         desktop = nodepath->desktop;
4649     } else {
4650         desktop = SP_ACTIVE_DESKTOP;
4651     }
4653     SPEventContext *ec = desktop->event_context;
4654     if (!ec) return;
4655     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4656     if (!mc) return;
4658     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4660     if (selected_nodes == 0) {
4661         Inkscape::Selection *sel = desktop->selection;
4662         if (!sel || sel->isEmpty()) {
4663             mc->setF(Inkscape::NORMAL_MESSAGE,
4664                      _("Select a single object to edit its nodes or handles."));
4665         } else {
4666             if (nodepath) {
4667             mc->setF(Inkscape::NORMAL_MESSAGE,
4668                      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.",
4669                               "<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.",
4670                               total_nodes),
4671                      total_nodes);
4672             } else {
4673                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4674                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4675                 } else {
4676                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4677                 }
4678             }
4679         }
4680     } else if (nodepath && selected_nodes == 1) {
4681         mc->setF(Inkscape::NORMAL_MESSAGE,
4682                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4683                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4684                           total_nodes),
4685                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4686     } else {
4687         if (selected_subpaths > 1) {
4688             mc->setF(Inkscape::NORMAL_MESSAGE,
4689                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4690                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4691                               total_nodes),
4692                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4693         } else {
4694             mc->setF(Inkscape::NORMAL_MESSAGE,
4695                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4696                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4697                               total_nodes),
4698                      selected_nodes, total_nodes, when_selected);
4699         }
4700     }
4703 /*
4704  * returns a *copy* of the curve of that object.
4705  */
4706 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4707     if (!object)
4708         return NULL;
4710     SPCurve *curve = NULL;
4711     if (SP_IS_PATH(object)) {
4712         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4713         curve = curve_new->copy();
4714     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4715         const gchar *svgd = object->repr->attribute(key);
4716         if (svgd) {
4717             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4718             SPCurve *curve_new = new SPCurve(pv);
4719             if (curve_new) {
4720                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4721             }
4722         }
4723     }
4725     return curve;
4728 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4729     if (!np || !np->object || !curve)
4730         return;
4732     if (SP_IS_PATH(np->object)) {
4733         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4734             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4735         } else {
4736             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4737         }
4738     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4739         // FIXME: this writing to string and then reading from string is bound to be slow.
4740         // create a method to convert from curve directly to 2geom...
4741         gchar *svgpath = sp_svg_write_path( np->curve->get_pathvector() );
4742         LIVEPATHEFFECT(np->object)->lpe->setParameter(np->repr_key, svgpath);
4743         g_free(svgpath);
4745         np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4746     }
4749 SPCanvasItem *
4750 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) {
4751     SPCurve *flash_curve = curve->copy();
4752     Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity();
4753     flash_curve->transform(i2d);
4754     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4755     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4756     // unless we also flash the nodes...
4757     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4758     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4759     sp_canvas_item_show(canvasitem);
4760     flash_curve->unref();
4761     return canvasitem;
4764 SPCanvasItem *
4765 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) {
4766     return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path),
4767                                            prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff));
4770 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4771     np->show_helperpath = show;
4773     if (show) {
4774         SPCurve *helper_curve = np->curve->copy();
4775         helper_curve->transform(np->i2d );
4776         if (!np->helper_path) {
4777             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4778             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);
4779             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4780             sp_canvas_item_move_to_z(np->helper_path, 0);
4781             sp_canvas_item_show(np->helper_path);
4782         } else {
4783             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4784         }
4785         helper_curve->unref();
4786     } else {
4787         if (np->helper_path) {
4788             GtkObject *temp = np->helper_path;
4789             np->helper_path = NULL;
4790             gtk_object_destroy(temp);
4791         }
4792     }
4795 /* sp_nodepath_make_straight_path:
4796  *   Prevents user from curving the path by dragging a segment or activating handles etc.
4797  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
4798  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
4799  */
4800 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4801     np->straight_path = true;
4802     np->show_handles = false;
4803     g_message("add code to make the path straight.");
4804     // do sp_nodepath_convert_node_type on all nodes?
4805     // coding tip: search for this text : "Make selected segments lines"
4809 /*
4810   Local Variables:
4811   mode:c++
4812   c-file-style:"stroustrup"
4813   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4814   indent-tabs-mode:nil
4815   fill-column:99
4816   End:
4817 */
4818 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :