Code

improve spcurve::second_point and document its behavior
[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         while (node->n.other) { // copy the remaining nodes into the new subpath
891            Inkscape::NodePath::Node *n  = node->n.other;
892            Inkscape::NodePath::Node *nn = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
893             if (n->selected) {
894                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
895             }
896             sp_nodepath_node_destroy(n); // remove the point on the original subpath
897         }
899         return newnode;
900     }
903 /**
904  * Duplicate node and connect to neighbours.
905  */
906 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
908     g_assert(node);
909     g_assert(node->subpath);
910     g_assert(g_list_find(node->subpath->nodes, node));
912    Inkscape::NodePath::SubPath *sp = node->subpath;
914     NRPathcode code = (NRPathcode) node->code;
915     if (code == NR_MOVETO) { // if node is the endnode,
916         node->code = NR_LINETO; // new one is inserted before it, so change that to line
917     }
919     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
921     if (!node->n.other || !node->p.other) // if node is an endnode, select it
922         return node;
923     else
924         return newnode; // otherwise select the newly created node
927 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
929     node->p.pos = (node->pos + (node->pos - node->n.pos));
932 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
934     node->n.pos = (node->pos + (node->pos - node->p.pos));
937 /**
938  * Change line type at node, with side effects on neighbours.
939  */
940 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
942     g_assert(end);
943     g_assert(end->subpath);
944     g_assert(end->p.other);
946     if (end->code == static_cast< guint > ( code ) )
947         return;
949    Inkscape::NodePath::Node *start = end->p.other;
951     end->code = code;
953     if (code == NR_LINETO) {
954         if (start->code == NR_LINETO) {
955             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
956         }
957         if (end->n.other) {
958             if (end->n.other->code == NR_LINETO) {
959                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
960             }
961         }
962     } else {
963         NR::Point delta = end->pos - start->pos;
964         start->n.pos = start->pos + delta / 3;
965         end->p.pos = end->pos - delta / 3;
966         sp_node_adjust_handle(start, 1);
967         sp_node_adjust_handle(end, -1);
968     }
970     sp_node_update_handles(start);
971     sp_node_update_handles(end);
974 /**
975  * Change node type, and its handles accordingly.
976  */
977 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
979     g_assert(node);
980     g_assert(node->subpath);
982     if ((node->p.other != NULL) && (node->n.other != NULL)) {
983         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
984             type =Inkscape::NodePath::NODE_CUSP;
985         }
986     }
988     node->type = type;
990     if (node->type == Inkscape::NodePath::NODE_CUSP) {
991         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
992         node->knot->setSize (node->selected? 11 : 9);
993         sp_knot_update_ctrl(node->knot);
994     } else {
995         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
996         node->knot->setSize (node->selected? 9 : 7);
997         sp_knot_update_ctrl(node->knot);
998     }
1000     // if one of handles is mouseovered, preserve its position
1001     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1002         sp_node_adjust_handle(node, 1);
1003     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1004         sp_node_adjust_handle(node, -1);
1005     } else {
1006         sp_node_adjust_handles(node);
1007     }
1009     sp_node_update_handles(node);
1011     sp_nodepath_update_statusbar(node->subpath->nodepath);
1013     return node;
1016 bool
1017 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1019         Inkscape::NodePath::Node *othernode = side->other;
1020         if (!othernode)
1021             return false;
1022         NRPathcode const code = sp_node_path_code_from_side(node, side);
1023         if (code == NR_LINETO)
1024             return true;
1025         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1026         if (&node->p == side) {
1027             other_to_me = &othernode->n;
1028         } else if (&node->n == side) {
1029             other_to_me = &othernode->p;
1030         } 
1031         if (!other_to_me)
1032             return false;
1033         bool is_line = 
1034              (NR::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1035               NR::L2(node->pos - side->pos) < 1e-6);
1036         return is_line;
1039 /**
1040  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1041  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1042  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1043  * If already cusp and set to cusp, retracts handles.
1044 */
1045 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1047     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1049 /* 
1050   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1051  
1052         if (two_handles) {
1053             // do nothing, adjust_handles called via set_node_type will line them up
1054         } else if (one_handle) {
1055             if (opposite_to_handle_is_line) {
1056                 if (lined_up) {
1057                     // already half-smooth; pull opposite handle too making it fully smooth
1058                 } else {
1059                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1060                 }
1061             } else {
1062                 // pull opposite handle in line with the existing one
1063             }
1064         } else if (no_handles) {
1065             if (both_segments_are_lines OR both_segments_are_curves) {
1066                 //pull both handles
1067             } else {
1068                 // pull the handle opposite to line segment, making node half-smooth
1069             }
1070         }
1071 */
1072         bool p_has_handle = (NR::L2(node->pos  - node->p.pos) > 1e-6);
1073         bool n_has_handle = (NR::L2(node->pos  - node->n.pos) > 1e-6);
1074         bool p_is_line = sp_node_side_is_line(node, &node->p);
1075         bool n_is_line = sp_node_side_is_line(node, &node->n);
1077         if (p_has_handle && n_has_handle) {
1078             // do nothing, adjust_handles will line them up
1079         } else if (p_has_handle || n_has_handle) {
1080             if (p_has_handle && n_is_line) {
1081                 Radial line (node->n.other->pos - node->pos);
1082                 Radial handle (node->pos - node->p.pos);
1083                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1084                     // already half-smooth; pull opposite handle too making it fully smooth
1085                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1086                 } else {
1087                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1088                 }
1089             } else if (n_has_handle && p_is_line) {
1090                 Radial line (node->p.other->pos - node->pos);
1091                 Radial handle (node->pos - node->n.pos);
1092                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1093                     // already half-smooth; pull opposite handle too making it fully smooth
1094                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1095                 } else {
1096                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1097                 }
1098             } else if (p_has_handle && node->n.other) {
1099                 // pull n handle
1100                 node->n.other->code = NR_CURVETO;
1101                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1102                     NR::L2(node->p.pos - node->pos) :
1103                     NR::L2(node->n.other->pos - node->pos) / 3;
1104                 node->n.pos = node->pos - (len / NR::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1105             } else if (n_has_handle && node->p.other) {
1106                 // pull p handle
1107                 node->code = NR_CURVETO;
1108                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1109                     NR::L2(node->n.pos - node->pos) :
1110                     NR::L2(node->p.other->pos - node->pos) / 3;
1111                 node->p.pos = node->pos - (len / NR::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1112             }
1113         } else if (!p_has_handle && !n_has_handle) {
1114             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1115                 // no handles, but both segments are either lnes or curves:
1116                 //pull both handles
1118                 // convert both to curves:
1119                 node->code = NR_CURVETO;
1120                 node->n.other->code = NR_CURVETO;
1122                 NR::Point leg_prev = node->pos - node->p.other->pos;
1123                 NR::Point leg_next = node->pos - node->n.other->pos;
1125                 double norm_leg_prev = L2(leg_prev);
1126                 double norm_leg_next = L2(leg_next);
1128                 NR::Point delta;
1129                 if (norm_leg_next > 0.0) {
1130                     delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1131                     (&delta)->normalize();
1132                 }
1134                 if (type == Inkscape::NodePath::NODE_SYMM) {
1135                     double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1136                     node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1137                     node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1138                 } else {
1139                     // length of handle is proportional to distance to adjacent node
1140                     node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1141                     node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1142                 }
1144             } else {
1145                 // pull the handle opposite to line segment, making it half-smooth
1146                 if (p_is_line && node->n.other) {
1147                     if (type != Inkscape::NodePath::NODE_SYMM) {
1148                         // pull n handle
1149                         node->n.other->code = NR_CURVETO;
1150                         double len =  NR::L2(node->n.other->pos - node->pos) / 3;
1151                         node->n.pos = node->pos + (len / NR::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1152                     }
1153                 } else if (n_is_line && node->p.other) {
1154                     if (type != Inkscape::NodePath::NODE_SYMM) {
1155                         // pull p handle
1156                         node->code = NR_CURVETO;
1157                         double len =  NR::L2(node->p.other->pos - node->pos) / 3;
1158                         node->p.pos = node->pos + (len / NR::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1159                     }
1160                 }
1161             }
1162         }
1163     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1164         // cusping a cusp: retract nodes
1165         node->p.pos = node->pos;
1166         node->n.pos = node->pos;
1167     }
1169     sp_nodepath_set_node_type (node, type);
1172 /**
1173  * Move node to point, and adjust its and neighbouring handles.
1174  */
1175 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1177     NR::Point delta = p - node->pos;
1178     node->pos = p;
1180     node->p.pos += delta;
1181     node->n.pos += delta;
1183     Inkscape::NodePath::Node *node_p = NULL;
1184     Inkscape::NodePath::Node *node_n = NULL;
1186     if (node->p.other) {
1187         if (node->code == NR_LINETO) {
1188             sp_node_adjust_handle(node, 1);
1189             sp_node_adjust_handle(node->p.other, -1);
1190             node_p = node->p.other;
1191         }
1192     }
1193     if (node->n.other) {
1194         if (node->n.other->code == NR_LINETO) {
1195             sp_node_adjust_handle(node, -1);
1196             sp_node_adjust_handle(node->n.other, 1);
1197             node_n = node->n.other;
1198         }
1199     }
1201     // this function is only called from batch movers that will update display at the end
1202     // themselves, so here we just move all the knots without emitting move signals, for speed
1203     sp_node_update_handles(node, false);
1204     if (node_n) {
1205         sp_node_update_handles(node_n, false);
1206     }
1207     if (node_p) {
1208         sp_node_update_handles(node_p, false);
1209     }
1212 /**
1213  * Call sp_node_moveto() for node selection and handle possible snapping.
1214  */
1215 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1216                                             bool const snap, bool constrained = false, 
1217                                             Inkscape::Snapper::ConstraintLine const &constraint = NR::Point())
1219     NR::Coord best = NR_HUGE;
1220     NR::Point delta(dx, dy);
1221     NR::Point best_pt = delta;
1222     Inkscape::SnappedPoint best_abs;
1223     
1224     if (snap) {    
1225         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1226          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1227          * must provide that information. */
1228           
1229         // Build a list of the unselected nodes to which the snapper should snap 
1230         std::vector<NR::Point> unselected_nodes;
1231         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1232             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1233             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1234                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1235                 if (!node->selected) {
1236                     unselected_nodes.push_back(node->pos);
1237                 }    
1238             }
1239         }        
1240         
1241         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1242         
1243         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1244             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1245             m.setup(NULL, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1246             Inkscape::SnappedPoint s;
1247             if (constrained) {
1248                 Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1249                 dedicated_constraint.setPoint(n->pos);
1250                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, dedicated_constraint);
1251             } else {
1252                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta);
1253             }            
1254             if (s.getSnapped() && (s.getDistance() < best)) {
1255                 best = s.getDistance();
1256                 best_abs = s;
1257                 best_pt = s.getPoint() - n->pos;
1258             }
1259         }
1260                         
1261         if (best_abs.getSnapped()) {
1262             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1263         } else {
1264             nodepath->desktop->snapindicator->remove_snappoint();    
1265         }
1266     }
1268     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1269         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1270         sp_node_moveto(n, n->pos + best_pt);
1271     }
1273     // do not update repr here so that node dragging is acceptably fast
1274     update_object(nodepath);
1277 /**
1278 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1279 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1280 near x = 0.
1281  */
1282 double
1283 sculpt_profile (double x, double alpha, guint profile)
1285     if (x >= 1)
1286         return 0;
1287     if (x <= 0)
1288         return 1;
1290     switch (profile) {
1291         case SCULPT_PROFILE_LINEAR:
1292         return 1 - x;
1293         case SCULPT_PROFILE_BELL:
1294         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1295         case SCULPT_PROFILE_ELLIPTIC:
1296         return sqrt(1 - x*x);
1297     }
1299     return 1;
1302 double
1303 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1305     // extremely primitive for now, don't have time to look for the real one
1306     double lower = NR::L2(b - a);
1307     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1308     return (lower + upper)/2;
1311 void
1312 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1314     n->pos = n->origin + delta;
1315     n->n.pos = n->n.origin + delta_n;
1316     n->p.pos = n->p.origin + delta_p;
1317     sp_node_adjust_handles(n);
1318     sp_node_update_handles(n, false);
1321 /**
1322  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1323  * on how far they are from the dragged node n.
1324  */
1325 static void
1326 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1328     g_assert (n);
1329     g_assert (nodepath);
1330     g_assert (n->subpath->nodepath == nodepath);
1332     double pressure = n->knot->pressure;
1333     if (pressure == 0)
1334         pressure = 0.5; // default
1335     pressure = CLAMP (pressure, 0.2, 0.8);
1337     // map pressure to alpha = 1/5 ... 5
1338     double alpha = 1 - 2 * fabs(pressure - 0.5);
1339     if (pressure > 0.5)
1340         alpha = 1/alpha;
1342     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1344     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1345         // Only one subpath has selected nodes:
1346         // use linear mode, where the distance from n to node being dragged is calculated along the path
1348         double n_sel_range = 0, p_sel_range = 0;
1349         guint n_nodes = 0, p_nodes = 0;
1350         guint n_sel_nodes = 0, p_sel_nodes = 0;
1352         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1353         {
1354             double n_range = 0, p_range = 0;
1355             bool n_going = true, p_going = true;
1356             Inkscape::NodePath::Node *n_node = n;
1357             Inkscape::NodePath::Node *p_node = n;
1358             do {
1359                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1360                 if (n_node && n_going)
1361                     n_node = n_node->n.other;
1362                 if (n_node == NULL) {
1363                     n_going = false;
1364                 } else {
1365                     n_nodes ++;
1366                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1367                     if (n_node->selected) {
1368                         n_sel_nodes ++;
1369                         n_sel_range = n_range;
1370                     }
1371                     if (n_node == p_node) {
1372                         n_going = false;
1373                         p_going = false;
1374                     }
1375                 }
1376                 if (p_node && p_going)
1377                     p_node = p_node->p.other;
1378                 if (p_node == NULL) {
1379                     p_going = false;
1380                 } else {
1381                     p_nodes ++;
1382                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1383                     if (p_node->selected) {
1384                         p_sel_nodes ++;
1385                         p_sel_range = p_range;
1386                     }
1387                     if (p_node == n_node) {
1388                         n_going = false;
1389                         p_going = false;
1390                     }
1391                 }
1392             } while (n_going || p_going);
1393         }
1395         // Second pass: actually move nodes in this subpath
1396         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1397         {
1398             double n_range = 0, p_range = 0;
1399             bool n_going = true, p_going = true;
1400             Inkscape::NodePath::Node *n_node = n;
1401             Inkscape::NodePath::Node *p_node = n;
1402             do {
1403                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1404                 if (n_node && n_going)
1405                     n_node = n_node->n.other;
1406                 if (n_node == NULL) {
1407                     n_going = false;
1408                 } else {
1409                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1410                     if (n_node->selected) {
1411                         sp_nodepath_move_node_and_handles (n_node,
1412                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1413                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1414                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1415                     }
1416                     if (n_node == p_node) {
1417                         n_going = false;
1418                         p_going = false;
1419                     }
1420                 }
1421                 if (p_node && p_going)
1422                     p_node = p_node->p.other;
1423                 if (p_node == NULL) {
1424                     p_going = false;
1425                 } else {
1426                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1427                     if (p_node->selected) {
1428                         sp_nodepath_move_node_and_handles (p_node,
1429                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1430                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1431                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1432                     }
1433                     if (p_node == n_node) {
1434                         n_going = false;
1435                         p_going = false;
1436                     }
1437                 }
1438             } while (n_going || p_going);
1439         }
1441     } else {
1442         // Multiple subpaths have selected nodes:
1443         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1444         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1445         // fix the pear-like shape when sculpting e.g. a ring
1447         // First pass: calculate range
1448         gdouble direct_range = 0;
1449         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1450             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1451             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1452                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1453                 if (node->selected) {
1454                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1455                 }
1456             }
1457         }
1459         // Second pass: actually move nodes
1460         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1461             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1462             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1463                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1464                 if (node->selected) {
1465                     if (direct_range > 1e-6) {
1466                         sp_nodepath_move_node_and_handles (node,
1467                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1468                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1469                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1470                     } else {
1471                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1472                     }
1474                 }
1475             }
1476         }
1477     }
1479     // do not update repr here so that node dragging is acceptably fast
1480     update_object(nodepath);
1484 /**
1485  * Move node selection to point, adjust its and neighbouring handles,
1486  * handle possible snapping, and commit the change with possible undo.
1487  */
1488 void
1489 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1491     if (!nodepath) return;
1493     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1495     if (dx == 0) {
1496         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1497     } else if (dy == 0) {
1498         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1499     } else {
1500         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1501     }
1504 /**
1505  * Move node selection off screen and commit the change.
1506  */
1507 void
1508 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1510     // borrowed from sp_selection_move_screen in selection-chemistry.c
1511     // we find out the current zoom factor and divide deltas by it
1512     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1514     gdouble zoom = desktop->current_zoom();
1515     gdouble zdx = dx / zoom;
1516     gdouble zdy = dy / zoom;
1518     if (!nodepath) return;
1520     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1522     if (dx == 0) {
1523         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1524     } else if (dy == 0) {
1525         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1526     } else {
1527         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1528     }
1531 /**
1532  * Move selected nodes to the absolute position given
1533  */
1534 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis)
1536     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1537         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1538         NR::Point npos(axis == NR::X ? val : n->pos[NR::X], axis == NR::Y ? val : n->pos[NR::Y]);
1539         sp_node_moveto(n, npos);
1540     }
1542     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1545 /**
1546  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1547  */
1548 NR::Maybe<NR::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1550     NR::Maybe<NR::Coord> no_coord = NR::Nothing();
1551     g_return_val_if_fail(nodepath->selected, no_coord);
1553     // determine coordinate of first selected node
1554     GList *nsel = nodepath->selected;
1555     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1556     NR::Coord coord = n->pos[axis];
1557     bool coincide = true;
1559     // compare it to the coordinates of all the other selected nodes
1560     for (GList *l = nsel->next; l != NULL; l = l->next) {
1561         n = (Inkscape::NodePath::Node *) l->data;
1562         if (n->pos[axis] != coord) {
1563             coincide = false;
1564         }
1565     }
1566     if (coincide) {
1567         return coord;
1568     } else {
1569         NR::Rect bbox = sp_node_selected_bbox(nodepath);
1570         // currently we return the coordinate of the bounding box midpoint because I don't know how
1571         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1572         return bbox.midpoint()[axis];
1573     }
1576 /** If they don't yet exist, creates knot and line for the given side of the node */
1577 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1579     if (!side->knot) {
1580         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"));
1582         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1583         side->knot->setSize (7);
1584         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1585         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1586         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1587         sp_knot_update_ctrl(side->knot);
1589         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1590         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1591         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1592         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1593         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1594         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1595     }
1597     if (!side->line) {
1598         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1599                                         SP_TYPE_CTRLLINE, NULL);
1600     }
1603 /**
1604  * Ensure the given handle of the node is visible/invisible, update its screen position
1605  */
1606 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1608     g_assert(node != NULL);
1610    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1611     NRPathcode code = sp_node_path_code_from_side(node, side);
1613     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1615     if (show_handle) {
1616         if (!side->knot) { // No handle knot at all
1617             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1618             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1619             side->knot->pos = side->pos;
1620             if (side->knot->item)
1621                 SP_CTRL(side->knot->item)->moveto(side->pos);
1622             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1623             sp_knot_show(side->knot);
1624         } else {
1625             if (side->knot->pos != side->pos) { // only if it's really moved
1626                 if (fire_move_signals) {
1627                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1628                 } else {
1629                     sp_knot_moveto(side->knot, &side->pos);
1630                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1631                 }
1632             }
1633             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1634                 sp_knot_show(side->knot);
1635             }
1636         }
1637         sp_canvas_item_show(side->line);
1638     } else {
1639         if (side->knot) {
1640             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1641                 sp_knot_hide(side->knot);
1642             }
1643         }
1644         if (side->line) {
1645             sp_canvas_item_hide(side->line);
1646         }
1647     }
1650 /**
1651  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1652  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1653  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1654  * updated; otherwise, just move the knots silently (used in batch moves).
1655  */
1656 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1658     g_assert(node != NULL);
1660     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1661         sp_knot_show(node->knot);
1662     }
1664     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1665         if (fire_move_signals)
1666             sp_knot_set_position(node->knot, &node->pos, 0);
1667         else
1668             sp_knot_moveto(node->knot, &node->pos);
1669     }
1671     gboolean show_handles = node->selected;
1672     if (node->p.other != NULL) {
1673         if (node->p.other->selected) show_handles = TRUE;
1674     }
1675     if (node->n.other != NULL) {
1676         if (node->n.other->selected) show_handles = TRUE;
1677     }
1679     if (node->subpath->nodepath->show_handles == false)
1680         show_handles = FALSE;
1682     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1683     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1686 /**
1687  * Call sp_node_update_handles() for all nodes on subpath.
1688  */
1689 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1691     g_assert(subpath != NULL);
1693     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1694         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1695     }
1698 /**
1699  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1700  */
1701 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1703     g_assert(nodepath != NULL);
1705     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1706         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1707     }
1710 void
1711 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1713     if (nodepath == NULL) return;
1715     nodepath->show_handles = show;
1716     sp_nodepath_update_handles(nodepath);
1719 /**
1720  * Adds all selected nodes in nodepath to list.
1721  */
1722 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1724     StlConv<Node *>::list(l, selected);
1725 /// \todo this adds a copying, rework when the selection becomes a stl list
1728 /**
1729  * Align selected nodes on the specified axis.
1730  */
1731 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1733     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1734         return;
1735     }
1737     if ( !nodepath->selected->next ) { // only one node selected
1738         return;
1739     }
1740    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1741     NR::Point dest(pNode->pos);
1742     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1743         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1744         if (pNode) {
1745             dest[axis] = pNode->pos[axis];
1746             sp_node_moveto(pNode, dest);
1747         }
1748     }
1750     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1753 /// Helper struct.
1754 struct NodeSort
1756    Inkscape::NodePath::Node *_node;
1757     NR::Coord _coord;
1758     /// \todo use vectorof pointers instead of calling copy ctor
1759     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1760         _node(node), _coord(node->pos[axis])
1761     {}
1763 };
1765 static bool operator<(NodeSort const &a, NodeSort const &b)
1767     return (a._coord < b._coord);
1770 /**
1771  * Distribute selected nodes on the specified axis.
1772  */
1773 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1775     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1776         return;
1777     }
1779     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1780         return;
1781     }
1783    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1784     std::vector<NodeSort> sorted;
1785     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1786         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1787         if (pNode) {
1788             NodeSort n(pNode, axis);
1789             sorted.push_back(n);
1790             //dest[axis] = pNode->pos[axis];
1791             //sp_node_moveto(pNode, dest);
1792         }
1793     }
1794     std::sort(sorted.begin(), sorted.end());
1795     unsigned int len = sorted.size();
1796     //overall bboxes span
1797     float dist = (sorted.back()._coord -
1798                   sorted.front()._coord);
1799     //new distance between each bbox
1800     float step = (dist) / (len - 1);
1801     float pos = sorted.front()._coord;
1802     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1803           it < sorted.end();
1804           it ++ )
1805     {
1806         NR::Point dest((*it)._node->pos);
1807         dest[axis] = pos;
1808         sp_node_moveto((*it)._node, dest);
1809         pos += step;
1810     }
1812     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1816 /**
1817  * Call sp_nodepath_line_add_node() for all selected segments.
1818  */
1819 void
1820 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1822     if (!nodepath) {
1823         return;
1824     }
1826     GList *nl = NULL;
1828     int n_added = 0;
1830     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1831        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1832         g_assert(t->selected);
1833         if (t->p.other && t->p.other->selected) {
1834             nl = g_list_prepend(nl, t);
1835         }
1836     }
1838     while (nl) {
1839        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1840        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1841        sp_nodepath_node_select(n, TRUE, FALSE);
1842        n_added ++;
1843        nl = g_list_remove(nl, t);
1844     }
1846     /** \todo fixme: adjust ? */
1847     sp_nodepath_update_handles(nodepath);
1849     if (n_added > 1) {
1850         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1851     } else if (n_added > 0) {
1852         sp_nodepath_update_repr(nodepath, _("Add node"));
1853     }
1855     sp_nodepath_update_statusbar(nodepath);
1858 /**
1859  * Select segment nearest to point
1860  */
1861 void
1862 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1864     if (!nodepath) {
1865         return;
1866     }
1868     sp_nodepath_ensure_livarot_path(nodepath);
1869     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1870     if (!maybe_position) {
1871         return;
1872     }
1873     Path::cut_position position = *maybe_position;
1875     //find segment to segment
1876     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1878     //fixme: this can return NULL, so check before proceeding.
1879     g_return_if_fail(e != NULL);
1881     gboolean force = FALSE;
1882     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1883         force = TRUE;
1884     }
1885     sp_nodepath_node_select(e, (gboolean) toggle, force);
1886     if (e->p.other)
1887         sp_nodepath_node_select(e->p.other, TRUE, force);
1889     sp_nodepath_update_handles(nodepath);
1891     sp_nodepath_update_statusbar(nodepath);
1894 /**
1895  * Add a node nearest to point
1896  */
1897 void
1898 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1900     if (!nodepath) {
1901         return;
1902     }
1904     sp_nodepath_ensure_livarot_path(nodepath);
1905     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1906     if (!maybe_position) {
1907         return;
1908     }
1909     Path::cut_position position = *maybe_position;
1911     //find segment to split
1912     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1914     //don't know why but t seems to flip for lines
1915     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1916         position.t = 1.0 - position.t;
1917     }
1918     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1919     sp_nodepath_node_select(n, FALSE, TRUE);
1921     /* fixme: adjust ? */
1922     sp_nodepath_update_handles(nodepath);
1924     sp_nodepath_update_repr(nodepath, _("Add node"));
1926     sp_nodepath_update_statusbar(nodepath);
1929 /*
1930  * Adjusts a segment so that t moves by a certain delta for dragging
1931  * converts lines to curves
1932  *
1933  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1934  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1935  */
1936 void
1937 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1939     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1941     //fixme: e and e->p can be NULL, so check for those before proceeding
1942     g_return_if_fail(e != NULL);
1943     g_return_if_fail(&e->p != NULL);
1945     /* feel good is an arbitrary parameter that distributes the delta between handles
1946      * if t of the drag point is less than 1/6 distance form the endpoint only
1947      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1948      */
1949     double feel_good;
1950     if (t <= 1.0 / 6.0)
1951         feel_good = 0;
1952     else if (t <= 0.5)
1953         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1954     else if (t <= 5.0 / 6.0)
1955         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1956     else
1957         feel_good = 1;
1959     //if we're dragging a line convert it to a curve
1960     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1961         sp_nodepath_set_line_type(e, NR_CURVETO);
1962     }
1964     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1965     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1966     e->p.other->n.pos += offsetcoord0;
1967     e->p.pos += offsetcoord1;
1969     // adjust handles of adjacent nodes where necessary
1970     sp_node_adjust_handle(e,1);
1971     sp_node_adjust_handle(e->p.other,-1);
1973     sp_nodepath_update_handles(e->subpath->nodepath);
1975     update_object(e->subpath->nodepath);
1977     sp_nodepath_update_statusbar(e->subpath->nodepath);
1981 /**
1982  * Call sp_nodepath_break() for all selected segments.
1983  */
1984 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
1986     if (!nodepath) return;
1988     GList *temp = NULL;
1989     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1990        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1991        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1992         if (nn == NULL) continue; // no break, no new node
1993         temp = g_list_prepend(temp, nn);
1994     }
1996     if (temp) {
1997         sp_nodepath_deselect(nodepath);
1998     }
1999     for (GList *l = temp; l != NULL; l = l->next) {
2000         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2001     }
2003     sp_nodepath_update_handles(nodepath);
2005     sp_nodepath_update_repr(nodepath, _("Break path"));
2008 /**
2009  * Duplicate the selected node(s).
2010  */
2011 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2013     if (!nodepath) {
2014         return;
2015     }
2017     GList *temp = NULL;
2018     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2019        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2020        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2021         if (nn == NULL) continue; // could not duplicate
2022         temp = g_list_prepend(temp, nn);
2023     }
2025     if (temp) {
2026         sp_nodepath_deselect(nodepath);
2027     }
2028     for (GList *l = temp; l != NULL; l = l->next) {
2029         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2030     }
2032     sp_nodepath_update_handles(nodepath);
2034     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2037 /**
2038  *  Internal function to join two nodes by merging them into one.
2039  */
2040 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2042     /* a and b are endpoints */
2044     // if one of the two nodes is mouseovered, fix its position
2045     NR::Point c;
2046     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2047         c = a->pos;
2048     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2049         c = b->pos;
2050     } else {
2051         // otherwise, move joined node to the midpoint
2052         c = (a->pos + b->pos) / 2;
2053     }
2055     if (a->subpath == b->subpath) {
2056        Inkscape::NodePath::SubPath *sp = a->subpath;
2057         sp_nodepath_subpath_close(sp);
2058         sp_node_moveto (sp->first, c);
2060         sp_nodepath_update_handles(sp->nodepath);
2061         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2062         return;
2063     }
2065     /* a and b are separate subpaths */
2066     Inkscape::NodePath::SubPath *sa = a->subpath;
2067     Inkscape::NodePath::SubPath *sb = b->subpath;
2068     NR::Point p;
2069     Inkscape::NodePath::Node *n;
2070     NRPathcode code;
2071     if (a == sa->first) {
2072         // we will now reverse sa, so that a is its last node, not first, and drop that node
2073         p = sa->first->n.pos;
2074         code = (NRPathcode)sa->first->n.other->code;
2075         // create new subpath
2076        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2077        // create a first moveto node on it
2078         n = sa->last;
2079         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2080         n = n->p.other;
2081         if (n == sa->first) n = NULL;
2082         while (n) {
2083             // copy the rest of the nodes from sa to t, going backwards
2084             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2085             n = n->p.other;
2086             if (n == sa->first) n = NULL;
2087         }
2088         // replace sa with t
2089         sp_nodepath_subpath_destroy(sa);
2090         sa = t;
2091     } else if (a == sa->last) {
2092         // a is already last, just drop it
2093         p = sa->last->p.pos;
2094         code = (NRPathcode)sa->last->code;
2095         sp_nodepath_node_destroy(sa->last);
2096     } else {
2097         code = NR_END;
2098         g_assert_not_reached();
2099     }
2101     if (b == sb->first) {
2102         // copy all nodes from b to a, forward 
2103         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2104         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2105             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2106         }
2107     } else if (b == sb->last) {
2108         // copy all nodes from b to a, backward 
2109         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2110         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2111             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2112         }
2113     } else {
2114         g_assert_not_reached();
2115     }
2116     /* and now destroy sb */
2118     sp_nodepath_subpath_destroy(sb);
2120     sp_nodepath_update_handles(sa->nodepath);
2122     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2124     sp_nodepath_update_statusbar(nodepath);
2127 /**
2128  *  Internal function to join two nodes by adding a segment between them.
2129  */
2130 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2132     if (a->subpath == b->subpath) {
2133        Inkscape::NodePath::SubPath *sp = a->subpath;
2135         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2136         sp->closed = TRUE;
2138         sp->first->p.other = sp->last;
2139         sp->last->n.other  = sp->first;
2141         sp_node_handle_mirror_p_to_n(sp->last);
2142         sp_node_handle_mirror_n_to_p(sp->first);
2144         sp->first->code = sp->last->code;
2145         sp->first       = sp->last;
2147         sp_nodepath_update_handles(sp->nodepath);
2149         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2151         return;
2152     }
2154     /* a and b are separate subpaths */
2155     Inkscape::NodePath::SubPath *sa = a->subpath;
2156     Inkscape::NodePath::SubPath *sb = b->subpath;
2158     Inkscape::NodePath::Node *n;
2159     NR::Point p;
2160     NRPathcode code;
2161     if (a == sa->first) {
2162         code = (NRPathcode) sa->first->n.other->code;
2163        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2164         n = sa->last;
2165         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2166         for (n = n->p.other; n != NULL; n = n->p.other) {
2167             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2168         }
2169         sp_nodepath_subpath_destroy(sa);
2170         sa = t;
2171     } else if (a == sa->last) {
2172         code = (NRPathcode)sa->last->code;
2173     } else {
2174         code = NR_END;
2175         g_assert_not_reached();
2176     }
2178     if (b == sb->first) {
2179         n = sb->first;
2180         sp_node_handle_mirror_p_to_n(sa->last);
2181         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2182         sp_node_handle_mirror_n_to_p(sa->last);
2183         for (n = n->n.other; n != NULL; n = n->n.other) {
2184             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2185         }
2186     } else if (b == sb->last) {
2187         n = sb->last;
2188         sp_node_handle_mirror_p_to_n(sa->last);
2189         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2190         sp_node_handle_mirror_n_to_p(sa->last);
2191         for (n = n->p.other; n != NULL; n = n->p.other) {
2192             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2193         }
2194     } else {
2195         g_assert_not_reached();
2196     }
2197     /* and now destroy sb */
2199     sp_nodepath_subpath_destroy(sb);
2201     sp_nodepath_update_handles(sa->nodepath);
2203     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2206 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2208 /**
2209  * Internal function to handle joining two nodes.
2210  */
2211 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2213     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2215     if (g_list_length(nodepath->selected) != 2) {
2216         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2217         return;
2218     }
2220     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2221     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2223     g_assert(a != b);
2224     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2225         // someone tried to join an orphan node (i.e. a single-node subpath).
2226         // this is not worth an error message, just fail silently.
2227         return;
2228     }
2230     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2231         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2232         return;
2233     }
2235     switch(mode) {
2236         case NODE_JOIN_ENDPOINTS:
2237             do_node_selected_join(nodepath, a, b);
2238             break;
2239         case NODE_JOIN_SEGMENT:
2240             do_node_selected_join_segment(nodepath, a, b);
2241             break;
2242     }
2245 /**
2246  *  Join two nodes by merging them into one.
2247  */
2248 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2250     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2253 /**
2254  *  Join two nodes by adding a segment between them.
2255  */
2256 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2258     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2261 /**
2262  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2263  */
2264 void sp_node_delete_preserve(GList *nodes_to_delete)
2266     GSList *nodepaths = NULL;
2268     while (nodes_to_delete) {
2269         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2270         Inkscape::NodePath::SubPath *sp = node->subpath;
2271         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2272         Inkscape::NodePath::Node *sample_cursor = NULL;
2273         Inkscape::NodePath::Node *sample_end = NULL;
2274         Inkscape::NodePath::Node *delete_cursor = node;
2275         bool just_delete = false;
2277         //find the start of this contiguous selection
2278         //move left to the first node that is not selected
2279         //or the start of the non-closed path
2280         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2281             delete_cursor = curr;
2282         }
2284         //just delete at the beginning of an open path
2285         if (!delete_cursor->p.other) {
2286             sample_cursor = delete_cursor;
2287             just_delete = true;
2288         } else {
2289             sample_cursor = delete_cursor->p.other;
2290         }
2292         //calculate points for each segment
2293         int rate = 5;
2294         float period = 1.0 / rate;
2295         std::vector<NR::Point> data;
2296         if (!just_delete) {
2297             data.push_back(sample_cursor->pos);
2298             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2299                 //just delete at the end of an open path
2300                 if (!sp->closed && curr == sp->last) {
2301                     just_delete = true;
2302                     break;
2303                 }
2305                 //sample points on the contiguous selected segment
2306                 NR::Point *bez;
2307                 bez = new NR::Point [4];
2308                 bez[0] = curr->pos;
2309                 bez[1] = curr->n.pos;
2310                 bez[2] = curr->n.other->p.pos;
2311                 bez[3] = curr->n.other->pos;
2312                 for (int i=1; i<rate; i++) {
2313                     gdouble t = i * period;
2314                     NR::Point p = bezier_pt(3, bez, t);
2315                     data.push_back(p);
2316                 }
2317                 data.push_back(curr->n.other->pos);
2319                 sample_end = curr->n.other;
2320                 //break if we've come full circle or hit the end of the selection
2321                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2322                     break;
2323                 }
2324             }
2325         }
2327         if (!just_delete) {
2328             //calculate the best fitting single segment and adjust the endpoints
2329             NR::Point *adata;
2330             adata = new NR::Point [data.size()];
2331             copy(data.begin(), data.end(), adata);
2333             NR::Point *bez;
2334             bez = new NR::Point [4];
2335             //would decreasing error create a better fitting approximation?
2336             gdouble error = 1.0;
2337             gint ret;
2338             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2340             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2341             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2342             //the resulting nodes behave as expected.
2343             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2344                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2345             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2346                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2348             //adjust endpoints
2349             sample_cursor->n.pos = bez[1];
2350             sample_end->p.pos = bez[2];
2351         }
2353         //destroy this contiguous selection
2354         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2355             Inkscape::NodePath::Node *temp = delete_cursor;
2356             if (delete_cursor->n.other == delete_cursor) {
2357                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2358                 delete_cursor = NULL;
2359             } else {
2360                 delete_cursor = delete_cursor->n.other;
2361             }
2362             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2363             sp_nodepath_node_destroy(temp);
2364         }
2366         sp_nodepath_update_handles(nodepath);
2368         if (!g_slist_find(nodepaths, nodepath))
2369             nodepaths = g_slist_prepend (nodepaths, nodepath);
2370     }
2372     for (GSList *i = nodepaths; i; i = i->next) {
2373         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2374         // different nodepaths will give us one undo event per nodepath
2375         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2377         // if the entire nodepath is removed, delete the selected object.
2378         if (nodepath->subpaths == NULL ||
2379             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2380             //at least 2
2381             sp_nodepath_get_node_count(nodepath) < 2) {
2382             SPDocument *document = sp_desktop_document (nodepath->desktop);
2383             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2384             //delete this nodepath's object, not the entire selection! (though at this time, this
2385             //does not matter)
2386             sp_selection_delete();
2387             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2388                               _("Delete nodes"));
2389         } else {
2390             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2391             sp_nodepath_update_statusbar(nodepath);
2392         }
2393     }
2395     g_slist_free (nodepaths);
2398 /**
2399  * Delete one or more selected nodes.
2400  */
2401 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2403     if (!nodepath) return;
2404     if (!nodepath->selected) return;
2406     /** \todo fixme: do it the right way */
2407     while (nodepath->selected) {
2408        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2409         sp_nodepath_node_destroy(node);
2410     }
2413     //clean up the nodepath (such as for trivial subpaths)
2414     sp_nodepath_cleanup(nodepath);
2416     sp_nodepath_update_handles(nodepath);
2418     // if the entire nodepath is removed, delete the selected object.
2419     if (nodepath->subpaths == NULL ||
2420         sp_nodepath_get_node_count(nodepath) < 2) {
2421         SPDocument *document = sp_desktop_document (nodepath->desktop);
2422         sp_selection_delete();
2423         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2424                           _("Delete nodes"));
2425         return;
2426     }
2428     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2430     sp_nodepath_update_statusbar(nodepath);
2433 /**
2434  * Delete one or more segments between two selected nodes.
2435  * This is the code for 'split'.
2436  */
2437 void
2438 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2440    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2441    Inkscape::NodePath::Node *curr, *next;     //Iterators
2443     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2445     if (g_list_length(nodepath->selected) != 2) {
2446         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2447                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2448         return;
2449     }
2451     //Selected nodes, not inclusive
2452    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2453    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2455     if ( ( a==b)                       ||  //same node
2456          (a->subpath  != b->subpath )  ||  //not the same path
2457          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2458          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2459     {
2460         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2461                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2462         return;
2463     }
2465     //###########################################
2466     //# BEGIN EDITS
2467     //###########################################
2468     //##################################
2469     //# CLOSED PATH
2470     //##################################
2471     if (a->subpath->closed) {
2474         gboolean reversed = FALSE;
2476         //Since we can go in a circle, we need to find the shorter distance.
2477         //  a->b or b->a
2478         start = end = NULL;
2479         int distance    = 0;
2480         int minDistance = 0;
2481         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2482             if (curr==b) {
2483                 //printf("a to b:%d\n", distance);
2484                 start = a;//go from a to b
2485                 end   = b;
2486                 minDistance = distance;
2487                 //printf("A to B :\n");
2488                 break;
2489             }
2490             distance++;
2491         }
2493         //try again, the other direction
2494         distance = 0;
2495         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2496             if (curr==a) {
2497                 //printf("b to a:%d\n", distance);
2498                 if (distance < minDistance) {
2499                     start    = b;  //we go from b to a
2500                     end      = a;
2501                     reversed = TRUE;
2502                     //printf("B to A\n");
2503                 }
2504                 break;
2505             }
2506             distance++;
2507         }
2510         //Copy everything from 'end' to 'start' to a new subpath
2511        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2512         for (curr=end ; curr ; curr=curr->n.other) {
2513             NRPathcode code = (NRPathcode) curr->code;
2514             if (curr == end)
2515                 code = NR_MOVETO;
2516             sp_nodepath_node_new(t, NULL,
2517                                  (Inkscape::NodePath::NodeType)curr->type, code,
2518                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2519             if (curr == start)
2520                 break;
2521         }
2522         sp_nodepath_subpath_destroy(a->subpath);
2525     }
2529     //##################################
2530     //# OPEN PATH
2531     //##################################
2532     else {
2534         //We need to get the direction of the list between A and B
2535         //Can we walk from a to b?
2536         start = end = NULL;
2537         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2538             if (curr==b) {
2539                 start = a;  //did it!  we go from a to b
2540                 end   = b;
2541                 //printf("A to B\n");
2542                 break;
2543             }
2544         }
2545         if (!start) {//didn't work?  let's try the other direction
2546             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2547                 if (curr==a) {
2548                     start = b;  //did it!  we go from b to a
2549                     end   = a;
2550                     //printf("B to A\n");
2551                     break;
2552                 }
2553             }
2554         }
2555         if (!start) {
2556             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2557                                                      _("Cannot find path between nodes."));
2558             return;
2559         }
2563         //Copy everything after 'end' to a new subpath
2564        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2565         for (curr=end ; curr ; curr=curr->n.other) {
2566             NRPathcode code = (NRPathcode) curr->code;
2567             if (curr == end)
2568                 code = NR_MOVETO;
2569             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2570                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2571         }
2573         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2574         for (curr = start->n.other ; curr  ; curr=next) {
2575             next = curr->n.other;
2576             sp_nodepath_node_destroy(curr);
2577         }
2579     }
2580     //###########################################
2581     //# END EDITS
2582     //###########################################
2584     //clean up the nodepath (such as for trivial subpaths)
2585     sp_nodepath_cleanup(nodepath);
2587     sp_nodepath_update_handles(nodepath);
2589     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2591     sp_nodepath_update_statusbar(nodepath);
2594 /**
2595  * Call sp_nodepath_set_line() for all selected segments.
2596  */
2597 void
2598 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2600     if (nodepath == NULL) return;
2602     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2603        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2604         g_assert(n->selected);
2605         if (n->p.other && n->p.other->selected) {
2606             sp_nodepath_set_line_type(n, code);
2607         }
2608     }
2610     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2613 /**
2614  * Call sp_nodepath_convert_node_type() for all selected nodes.
2615  */
2616 void
2617 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2619     if (nodepath == NULL) return;
2621     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2623     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2624         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2625     }
2627     sp_nodepath_update_repr(nodepath, _("Change node type"));
2630 /**
2631  * Change select status of node, update its own and neighbour handles.
2632  */
2633 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2635     node->selected = selected;
2637     if (selected) {
2638         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2639         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2640         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2641         sp_knot_update_ctrl(node->knot);
2642     } else {
2643         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2644         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2645         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2646         sp_knot_update_ctrl(node->knot);
2647     }
2649     sp_node_update_handles(node);
2650     if (node->n.other) sp_node_update_handles(node->n.other);
2651     if (node->p.other) sp_node_update_handles(node->p.other);
2654 /**
2655 \brief Select a node
2656 \param node     The node to select
2657 \param incremental   If true, add to selection, otherwise deselect others
2658 \param override   If true, always select this node, otherwise toggle selected status
2659 */
2660 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2662     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2664     if (incremental) {
2665         if (override) {
2666             if (!g_list_find(nodepath->selected, node)) {
2667                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2668             }
2669             sp_node_set_selected(node, TRUE);
2670         } else { // toggle
2671             if (node->selected) {
2672                 g_assert(g_list_find(nodepath->selected, node));
2673                 nodepath->selected = g_list_remove(nodepath->selected, node);
2674             } else {
2675                 g_assert(!g_list_find(nodepath->selected, node));
2676                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2677             }
2678             sp_node_set_selected(node, !node->selected);
2679         }
2680     } else {
2681         sp_nodepath_deselect(nodepath);
2682         nodepath->selected = g_list_prepend(nodepath->selected, node);
2683         sp_node_set_selected(node, TRUE);
2684     }
2686     sp_nodepath_update_statusbar(nodepath);
2690 /**
2691 \brief Deselect all nodes in the nodepath
2692 */
2693 void
2694 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2696     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2698     while (nodepath->selected) {
2699         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2700         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2701     }
2702     sp_nodepath_update_statusbar(nodepath);
2705 /**
2706 \brief Select or invert selection of all nodes in the nodepath
2707 */
2708 void
2709 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2711     if (!nodepath) return;
2713     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2714        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2715         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2716            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2717            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2718         }
2719     }
2722 /**
2723  * If nothing selected, does the same as sp_nodepath_select_all();
2724  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2725  * (i.e., similar to "select all in layer", with the "selected" subpaths
2726  * being treated as "layers" in the path).
2727  */
2728 void
2729 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2731     if (!nodepath) return;
2733     if (g_list_length (nodepath->selected) == 0) {
2734         sp_nodepath_select_all (nodepath, invert);
2735         return;
2736     }
2738     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2739     GSList *subpaths = NULL;
2741     for (GList *l = copy; l != NULL; l = l->next) {
2742         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2743         Inkscape::NodePath::SubPath *subpath = n->subpath;
2744         if (!g_slist_find (subpaths, subpath))
2745             subpaths = g_slist_prepend (subpaths, subpath);
2746     }
2748     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2749         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2750         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2751             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2752             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2753         }
2754     }
2756     g_slist_free (subpaths);
2757     g_list_free (copy);
2760 /**
2761  * \brief Select the node after the last selected; if none is selected,
2762  * select the first within path.
2763  */
2764 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2766     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2768    Inkscape::NodePath::Node *last = NULL;
2769     if (nodepath->selected) {
2770         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2771            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2772             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2773             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2774                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2775                 if (node->selected) {
2776                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2777                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2778                             if (spl->next) { // there's a next subpath
2779                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2780                                 last = subpath_next->first;
2781                             } else if (spl->prev) { // there's a previous subpath
2782                                 last = NULL; // to be set later to the first node of first subpath
2783                             } else {
2784                                 last = node->n.other;
2785                             }
2786                         } else {
2787                             last = node->n.other;
2788                         }
2789                     } else {
2790                         if (node->n.other) {
2791                             last = node->n.other;
2792                         } else {
2793                             if (spl->next) { // there's a next subpath
2794                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2795                                 last = subpath_next->first;
2796                             } else if (spl->prev) { // there's a previous subpath
2797                                 last = NULL; // to be set later to the first node of first subpath
2798                             } else {
2799                                 last = (Inkscape::NodePath::Node *) subpath->first;
2800                             }
2801                         }
2802                     }
2803                 }
2804             }
2805         }
2806         sp_nodepath_deselect(nodepath);
2807     }
2809     if (last) { // there's at least one more node after selected
2810         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2811     } else { // no more nodes, select the first one in first subpath
2812        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2813         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2814     }
2817 /**
2818  * \brief Select the node before the first selected; if none is selected,
2819  * select the last within path
2820  */
2821 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2823     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2825    Inkscape::NodePath::Node *last = NULL;
2826     if (nodepath->selected) {
2827         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2828            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2829             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2830                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2831                 if (node->selected) {
2832                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2833                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2834                             if (spl->prev) { // there's a prev subpath
2835                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2836                                 last = subpath_prev->last;
2837                             } else if (spl->next) { // there's a next subpath
2838                                 last = NULL; // to be set later to the last node of last subpath
2839                             } else {
2840                                 last = node->p.other;
2841                             }
2842                         } else {
2843                             last = node->p.other;
2844                         }
2845                     } else {
2846                         if (node->p.other) {
2847                             last = node->p.other;
2848                         } else {
2849                             if (spl->prev) { // there's a prev subpath
2850                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2851                                 last = subpath_prev->last;
2852                             } else if (spl->next) { // there's a next subpath
2853                                 last = NULL; // to be set later to the last node of last subpath
2854                             } else {
2855                                 last = (Inkscape::NodePath::Node *) subpath->last;
2856                             }
2857                         }
2858                     }
2859                 }
2860             }
2861         }
2862         sp_nodepath_deselect(nodepath);
2863     }
2865     if (last) { // there's at least one more node before selected
2866         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2867     } else { // no more nodes, select the last one in last subpath
2868         GList *spl = g_list_last(nodepath->subpaths);
2869        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2870         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2871     }
2874 /**
2875  * \brief Select all nodes that are within the rectangle.
2876  */
2877 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2879     if (!incremental) {
2880         sp_nodepath_deselect(nodepath);
2881     }
2883     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2884        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2885         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2886            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2888             if (b.contains(node->pos)) {
2889                 sp_nodepath_node_select(node, TRUE, TRUE);
2890             }
2891         }
2892     }
2896 void
2897 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2899     g_assert (n);
2900     g_assert (nodepath);
2901     g_assert (n->subpath->nodepath == nodepath);
2903     if (g_list_length (nodepath->selected) == 0) {
2904         if (grow > 0) {
2905             sp_nodepath_node_select(n, TRUE, TRUE);
2906         }
2907         return;
2908     }
2910     if (g_list_length (nodepath->selected) == 1) {
2911         if (grow < 0) {
2912             sp_nodepath_deselect (nodepath);
2913             return;
2914         }
2915     }
2917         double n_sel_range = 0, p_sel_range = 0;
2918             Inkscape::NodePath::Node *farthest_n_node = n;
2919             Inkscape::NodePath::Node *farthest_p_node = n;
2921         // Calculate ranges
2922         {
2923             double n_range = 0, p_range = 0;
2924             bool n_going = true, p_going = true;
2925             Inkscape::NodePath::Node *n_node = n;
2926             Inkscape::NodePath::Node *p_node = n;
2927             do {
2928                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2929                 if (n_node && n_going)
2930                     n_node = n_node->n.other;
2931                 if (n_node == NULL) {
2932                     n_going = false;
2933                 } else {
2934                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2935                     if (n_node->selected) {
2936                         n_sel_range = n_range;
2937                         farthest_n_node = n_node;
2938                     }
2939                     if (n_node == p_node) {
2940                         n_going = false;
2941                         p_going = false;
2942                     }
2943                 }
2944                 if (p_node && p_going)
2945                     p_node = p_node->p.other;
2946                 if (p_node == NULL) {
2947                     p_going = false;
2948                 } else {
2949                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2950                     if (p_node->selected) {
2951                         p_sel_range = p_range;
2952                         farthest_p_node = p_node;
2953                     }
2954                     if (p_node == n_node) {
2955                         n_going = false;
2956                         p_going = false;
2957                     }
2958                 }
2959             } while (n_going || p_going);
2960         }
2962     if (grow > 0) {
2963         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2964                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2965         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2966                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2967         }
2968     } else {
2969         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2970                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2971         } else if (farthest_p_node && farthest_p_node->selected) {
2972                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2973         }
2974     }
2977 void
2978 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2980     g_assert (n);
2981     g_assert (nodepath);
2982     g_assert (n->subpath->nodepath == nodepath);
2984     if (g_list_length (nodepath->selected) == 0) {
2985         if (grow > 0) {
2986             sp_nodepath_node_select(n, TRUE, TRUE);
2987         }
2988         return;
2989     }
2991     if (g_list_length (nodepath->selected) == 1) {
2992         if (grow < 0) {
2993             sp_nodepath_deselect (nodepath);
2994             return;
2995         }
2996     }
2998     Inkscape::NodePath::Node *farthest_selected = NULL;
2999     double farthest_dist = 0;
3001     Inkscape::NodePath::Node *closest_unselected = NULL;
3002     double closest_dist = NR_HUGE;
3004     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3005        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3006         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3007            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3008            if (node == n)
3009                continue;
3010            if (node->selected) {
3011                if (NR::L2(node->pos - n->pos) > farthest_dist) {
3012                    farthest_dist = NR::L2(node->pos - n->pos);
3013                    farthest_selected = node;
3014                }
3015            } else {
3016                if (NR::L2(node->pos - n->pos) < closest_dist) {
3017                    closest_dist = NR::L2(node->pos - n->pos);
3018                    closest_unselected = node;
3019                }
3020            }
3021         }
3022     }
3024     if (grow > 0) {
3025         if (closest_unselected) {
3026             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3027         }
3028     } else {
3029         if (farthest_selected) {
3030             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3031         }
3032     }
3036 /**
3037 \brief  Saves all nodes' and handles' current positions in their origin members
3038 */
3039 void
3040 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3042     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3043        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3044         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3045            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3046            n->origin = n->pos;
3047            n->p.origin = n->p.pos;
3048            n->n.origin = n->n.pos;
3049         }
3050     }
3053 /**
3054 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3055 */
3056 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3058     if (!nodepath->selected) {
3059         return NULL;
3060     }
3062     GList *r = NULL;
3063     guint i = 0;
3064     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3065        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3066         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3067            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3068             i++;
3069             if (node->selected) {
3070                 r = g_list_append(r, GINT_TO_POINTER(i));
3071             }
3072         }
3073     }
3074     return r;
3077 /**
3078 \brief  Restores selection by selecting nodes whose positions are in the list
3079 */
3080 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3082     sp_nodepath_deselect(nodepath);
3084     guint i = 0;
3085     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3086        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3087         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3088            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3089             i++;
3090             if (g_list_find(r, GINT_TO_POINTER(i))) {
3091                 sp_nodepath_node_select(node, TRUE, TRUE);
3092             }
3093         }
3094     }
3098 /**
3099 \brief Adjusts handle according to node type and line code.
3100 */
3101 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3103     g_assert(node);
3105    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3106    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3108    // nothing to do if we are an end node
3109     if (me->other == NULL) return;
3110     if (other->other == NULL) return;
3112     // nothing to do if we are a cusp node
3113     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3115     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3116     NRPathcode mecode;
3117     if (which_adjust == 1) {
3118         mecode = (NRPathcode)me->other->code;
3119     } else {
3120         mecode = (NRPathcode)node->code;
3121     }
3122     if (mecode == NR_LINETO) return;
3124     if (sp_node_side_is_line(node, other)) {
3125         // other is a line, and we are either smooth or symm
3126        Inkscape::NodePath::Node *othernode = other->other;
3127         double len = NR::L2(me->pos - node->pos);
3128         NR::Point delta = node->pos - othernode->pos;
3129         double linelen = NR::L2(delta);
3130         if (linelen < 1e-18)
3131             return;
3132         me->pos = node->pos + (len / linelen)*delta;
3133         return;
3134     }
3136     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3137         // symmetrize 
3138         me->pos = 2 * node->pos - other->pos;
3139         return;
3140     } else {
3141         // smoothify
3142         double len = NR::L2(me->pos - node->pos);
3143         NR::Point delta = other->pos - node->pos;
3144         double otherlen = NR::L2(delta);
3145         if (otherlen < 1e-18) return;
3146         me->pos = node->pos - (len / otherlen) * delta;
3147     }
3150 /**
3151  \brief Adjusts both handles according to node type and line code
3152  */
3153 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3155     g_assert(node);
3157     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3159     /* we are either smooth or symm */
3161     if (node->p.other == NULL) return;
3162     if (node->n.other == NULL) return;
3164     if (sp_node_side_is_line(node, &node->p)) {
3165         sp_node_adjust_handle(node, 1);
3166         return;
3167     }
3169     if (sp_node_side_is_line(node, &node->n)) {
3170         sp_node_adjust_handle(node, -1);
3171         return;
3172     }
3174     /* both are curves */
3175     NR::Point const delta( node->n.pos - node->p.pos );
3177     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3178         node->p.pos = node->pos - delta / 2;
3179         node->n.pos = node->pos + delta / 2;
3180         return;
3181     }
3183     /* We are smooth */
3184     double plen = NR::L2(node->p.pos - node->pos);
3185     if (plen < 1e-18) return;
3186     double nlen = NR::L2(node->n.pos - node->pos);
3187     if (nlen < 1e-18) return;
3188     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3189     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3192 /**
3193  * Node event callback.
3194  */
3195 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3197     gboolean ret = FALSE;
3198     switch (event->type) {
3199         case GDK_ENTER_NOTIFY:
3200             Inkscape::NodePath::Path::active_node = n;
3201             break;
3202         case GDK_LEAVE_NOTIFY:
3203             Inkscape::NodePath::Path::active_node = NULL;
3204             break;
3205         case GDK_SCROLL:
3206             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3207                 switch (event->scroll.direction) {
3208                     case GDK_SCROLL_UP:
3209                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3210                         break;
3211                     case GDK_SCROLL_DOWN:
3212                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3213                         break;
3214                     default:
3215                         break;
3216                 }
3217                 ret = TRUE;
3218             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3219                 switch (event->scroll.direction) {
3220                     case GDK_SCROLL_UP:
3221                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3222                         break;
3223                     case GDK_SCROLL_DOWN:
3224                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3225                         break;
3226                     default:
3227                         break;
3228                 }
3229                 ret = TRUE;
3230             }
3231             break;
3232         case GDK_KEY_PRESS:
3233             switch (get_group0_keyval (&event->key)) {
3234                 case GDK_space:
3235                     if (event->key.state & GDK_BUTTON1_MASK) {
3236                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3237                         stamp_repr(nodepath);
3238                         ret = TRUE;
3239                     }
3240                     break;
3241                 case GDK_Page_Up:
3242                     if (event->key.state & GDK_CONTROL_MASK) {
3243                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3244                     } else {
3245                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3246                     }
3247                     break;
3248                 case GDK_Page_Down:
3249                     if (event->key.state & GDK_CONTROL_MASK) {
3250                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3251                     } else {
3252                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3253                     }
3254                     break;
3255                 default:
3256                     break;
3257             }
3258             break;
3259         default:
3260             break;
3261     }
3263     return ret;
3266 /**
3267  * Handle keypress on node; directly called.
3268  */
3269 gboolean node_key(GdkEvent *event)
3271     Inkscape::NodePath::Path *np;
3273     // there is no way to verify nodes so set active_node to nil when deleting!!
3274     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3276     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3277         gint ret = FALSE;
3278         switch (get_group0_keyval (&event->key)) {
3279             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3280             case GDK_BackSpace:
3281                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3282                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3283                 sp_nodepath_update_repr(np, _("Delete node"));
3284                 Inkscape::NodePath::Path::active_node = NULL;
3285                 ret = TRUE;
3286                 break;
3287             case GDK_c:
3288                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3289                 ret = TRUE;
3290                 break;
3291             case GDK_s:
3292                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3293                 ret = TRUE;
3294                 break;
3295             case GDK_y:
3296                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3297                 ret = TRUE;
3298                 break;
3299             case GDK_b:
3300                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3301                 ret = TRUE;
3302                 break;
3303         }
3304         return ret;
3305     }
3306     return FALSE;
3309 /**
3310  * Mouseclick on node callback.
3311  */
3312 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3314    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3316     if (state & GDK_CONTROL_MASK) {
3317         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3319         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3320             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3321                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3322             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3323                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3324             } else {
3325                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3326             }
3327             sp_nodepath_update_repr(nodepath, _("Change node type"));
3328             sp_nodepath_update_statusbar(nodepath);
3330         } else { //ctrl+alt+click: delete node
3331             GList *node_to_delete = NULL;
3332             node_to_delete = g_list_append(node_to_delete, n);
3333             sp_node_delete_preserve(node_to_delete);
3334         }
3336     } else {
3337         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3338     }
3341 /**
3342  * Mouse grabbed node callback.
3343  */
3344 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3346    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3348     if (!n->selected) {
3349         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3350     }
3352     n->is_dragging = true;
3353     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3355     sp_nodepath_remember_origins (n->subpath->nodepath);
3358 /**
3359  * Mouse ungrabbed node callback.
3360  */
3361 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3363    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3365    n->dragging_out = NULL;
3366    n->is_dragging = false;
3367    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3369    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3372 /**
3373  * The point on a line, given by its angle, closest to the given point.
3374  * \param p  A point.
3375  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3376  * \param closest  Pointer to the point struct where the result is stored.
3377  * \todo FIXME: use dot product perhaps?
3378  */
3379 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3381     if (a == HUGE_VAL) { // vertical
3382         *closest = NR::Point(0, (*p)[NR::Y]);
3383     } else {
3384         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3385         (*closest)[NR::Y] = a * (*closest)[NR::X];
3386     }
3389 /**
3390  * Distance from the point to a line given by its angle.
3391  * \param p  A point.
3392  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3393  */
3394 static double point_line_distance(NR::Point *p, double a)
3396     NR::Point c;
3397     point_line_closest(p, a, &c);
3398     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]));
3401 /**
3402  * Callback for node "request" signal.
3403  * \todo fixme: This goes to "moved" event? (lauris)
3404  */
3405 static gboolean
3406 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3408     double yn, xn, yp, xp;
3409     double an, ap, na, pa;
3410     double d_an, d_ap, d_na, d_pa;
3411     gboolean collinear = FALSE;
3412     NR::Point c;
3413     NR::Point pr;
3415     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3417     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3419     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3420     if ( (!n->subpath->nodepath->straight_path) &&
3421          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3422            || n->dragging_out ) )
3423     {
3424        NR::Point mouse = (*p);
3426        if (!n->dragging_out) {
3427            // This is the first drag-out event; find out which handle to drag out
3428            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3429            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3431            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3432                return FALSE;
3434            Inkscape::NodePath::NodeSide *opposite;
3435            if (appr_p > appr_n) { // closer to p
3436                n->dragging_out = &n->p;
3437                opposite = &n->n;
3438                n->code = NR_CURVETO;
3439            } else if (appr_p < appr_n) { // closer to n
3440                n->dragging_out = &n->n;
3441                opposite = &n->p;
3442                n->n.other->code = NR_CURVETO;
3443            } else { // p and n nodes are the same
3444                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3445                    n->dragging_out = &n->p;
3446                    opposite = &n->n;
3447                    n->code = NR_CURVETO;
3448                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3449                    n->dragging_out = &n->n;
3450                    opposite = &n->p;
3451                    n->n.other->code = NR_CURVETO;
3452                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3453                    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);
3454                    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);
3455                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3456                        n->dragging_out = &n->n;
3457                        opposite = &n->p;
3458                        n->n.other->code = NR_CURVETO;
3459                    } else { // closer to other's n handle
3460                        n->dragging_out = &n->p;
3461                        opposite = &n->n;
3462                        n->code = NR_CURVETO;
3463                    }
3464                }
3465            }
3467            // if there's another handle, make sure the one we drag out starts parallel to it
3468            if (opposite->pos != n->pos) {
3469                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3470            }
3472            // knots might not be created yet!
3473            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3474            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3475        }
3477        // pass this on to the handle-moved callback
3478        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3479        sp_node_update_handles(n);
3480        return TRUE;
3481    }
3483     if (state & GDK_CONTROL_MASK) { // constrained motion
3485         // calculate relative distances of handles
3486         // n handle:
3487         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3488         xn = n->n.pos[NR::X] - n->pos[NR::X];
3489         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3490         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3491             if (n->n.other) { // if there is the next point
3492                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3493                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3494                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3495             }
3496         }
3497         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3498         if (yn < 0) { xn = -xn; yn = -yn; }
3500         // p handle:
3501         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3502         xp = n->p.pos[NR::X] - n->pos[NR::X];
3503         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3504         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3505             if (n->p.other) {
3506                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3507                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3508                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3509             }
3510         }
3511         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3512         if (yp < 0) { xp = -xp; yp = -yp; }
3514         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3515             // sliding on handles, only if at least one of the handles is non-vertical
3516             // (otherwise it's the same as ctrl+drag anyway)
3518             // calculate angles of the handles
3519             if (xn == 0) {
3520                 if (yn == 0) { // no handle, consider it the continuation of the other one
3521                     an = 0;
3522                     collinear = TRUE;
3523                 }
3524                 else an = 0; // vertical; set the angle to horizontal
3525             } else an = yn/xn;
3527             if (xp == 0) {
3528                 if (yp == 0) { // no handle, consider it the continuation of the other one
3529                     ap = an;
3530                 }
3531                 else ap = 0; // vertical; set the angle to horizontal
3532             } else  ap = yp/xp;
3534             if (collinear) an = ap;
3536             // angles of the perpendiculars; HUGE_VAL means vertical
3537             if (an == 0) na = HUGE_VAL; else na = -1/an;
3538             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3540             // mouse point relative to the node's original pos
3541             pr = (*p) - n->origin;
3543             // distances to the four lines (two handles and two perpendiculars)
3544             d_an = point_line_distance(&pr, an);
3545             d_na = point_line_distance(&pr, na);
3546             d_ap = point_line_distance(&pr, ap);
3547             d_pa = point_line_distance(&pr, pa);
3549             // find out which line is the closest, save its closest point in c
3550             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3551                 point_line_closest(&pr, an, &c);
3552             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3553                 point_line_closest(&pr, ap, &c);
3554             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3555                 point_line_closest(&pr, na, &c);
3556             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3557                 point_line_closest(&pr, pa, &c);
3558             }
3560             // move the node to the closest point
3561             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3562                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3563                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y], 
3564                                             true);
3566         } else {  // constraining to hor/vert
3568             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3569                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3570                                                 (*p)[NR::X] - n->pos[NR::X], 
3571                                                 n->origin[NR::Y] - n->pos[NR::Y],
3572                                                 true, 
3573                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::X]));
3574             } else { // snap to vert
3575                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3576                                                 n->origin[NR::X] - n->pos[NR::X],
3577                                                 (*p)[NR::Y] - n->pos[NR::Y],
3578                                                 true,
3579                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::Y]));
3580             }
3581         }
3582     } else { // move freely
3583         if (n->is_dragging) {
3584             if (state & GDK_MOD1_MASK) { // sculpt
3585                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3586             } else {
3587                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3588                                             (*p)[NR::X] - n->pos[NR::X],
3589                                             (*p)[NR::Y] - n->pos[NR::Y],
3590                                             (state & GDK_SHIFT_MASK) == 0);
3591             }
3592         }
3593     }
3595     n->subpath->nodepath->desktop->scroll_to_point(p);
3597     return TRUE;
3600 /**
3601  * Node handle clicked callback.
3602  */
3603 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3605    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3607     if (state & GDK_CONTROL_MASK) { // "delete" handle
3608         if (n->p.knot == knot) {
3609             n->p.pos = n->pos;
3610         } else if (n->n.knot == knot) {
3611             n->n.pos = n->pos;
3612         }
3613         sp_node_update_handles(n);
3614         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3615         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3616         sp_nodepath_update_statusbar(nodepath);
3618     } else { // just select or add to selection, depending in Shift
3619         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3620     }
3623 /**
3624  * Node handle grabbed callback.
3625  */
3626 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3628    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3630     if (!n->selected) {
3631         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3632     }
3634     // remember the origin point of the handle
3635     if (n->p.knot == knot) {
3636         n->p.origin_radial = n->p.pos - n->pos;
3637     } else if (n->n.knot == knot) {
3638         n->n.origin_radial = n->n.pos - n->pos;
3639     } else {
3640         g_assert_not_reached();
3641     }
3643     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3646 /**
3647  * Node handle ungrabbed callback.
3648  */
3649 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3651    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3653     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3654     if (n->p.knot == knot) {
3655         n->p.origin_radial.a = 0;
3656         sp_knot_set_position(knot, &n->p.pos, state);
3657     } else if (n->n.knot == knot) {
3658         n->n.origin_radial.a = 0;
3659         sp_knot_set_position(knot, &n->n.pos, state);
3660     } else {
3661         g_assert_not_reached();
3662     }
3664     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3667 /**
3668  * Node handle "request" signal callback.
3669  */
3670 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint /*state*/, gpointer data)
3672     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3674     Inkscape::NodePath::NodeSide *me, *opposite;
3675     gint which;
3676     if (n->p.knot == knot) {
3677         me = &n->p;
3678         opposite = &n->n;
3679         which = -1;
3680     } else if (n->n.knot == knot) {
3681         me = &n->n;
3682         opposite = &n->p;
3683         which = 1;
3684     } else {
3685         me = opposite = NULL;
3686         which = 0;
3687         g_assert_not_reached();
3688     }
3690     SPDesktop *desktop = n->subpath->nodepath->desktop;
3691     SnapManager &m = desktop->namedview->snap_manager;
3692     m.setup(desktop, n->subpath->nodepath->item);
3693     Inkscape::SnappedPoint s ;
3695     Inkscape::NodePath::Node *othernode = opposite->other;
3696     if (othernode) {
3697         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3698             /* We are smooth node adjacent with line */
3699             NR::Point const delta = *p - n->pos;
3700             NR::Coord const len = NR::L2(delta);
3701             Inkscape::NodePath::Node *othernode = opposite->other;
3702             NR::Point const ndelta = n->pos - othernode->pos;
3703             NR::Coord const linelen = NR::L2(ndelta);
3704             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3705                 NR::Coord const scal = dot(delta, ndelta) / linelen;
3706                 (*p) = n->pos + (scal / linelen) * ndelta;
3707             }
3708             s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta));
3709         } else {
3710             s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3711         }
3712     } else {
3713         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3714     }
3715     
3716     s.getPoint(*p);
3717     
3718     sp_node_adjust_handle(n, -which);
3720     return FALSE;
3723 /**
3724  * Node handle moved callback.
3725  */
3726 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3728    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3730    Inkscape::NodePath::NodeSide *me;
3731    Inkscape::NodePath::NodeSide *other;
3732     if (n->p.knot == knot) {
3733         me = &n->p;
3734         other = &n->n;
3735     } else if (n->n.knot == knot) {
3736         me = &n->n;
3737         other = &n->p;
3738     } else {
3739         me = NULL;
3740         other = NULL;
3741         g_assert_not_reached();
3742     }
3744     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3745     Radial rme(me->pos - n->pos);
3746     Radial rother(other->pos - n->pos);
3747     Radial rnew(*p - n->pos);
3749     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3750         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3751         /* 0 interpreted as "no snapping". */
3753         // 1. Snap to the closest PI/snaps angle, starting from zero.
3754         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3756         // 2. Snap to the original angle, its opposite and perpendiculars
3757         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3758             /* The closest PI/2 angle, starting from original angle */
3759             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3761             // Snap to the closest.
3762             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3763                        ? a_snapped
3764                        : a_ortho );
3765         }
3767         // 3. Snap to the angle of the opposite line, if any
3768         Inkscape::NodePath::Node *othernode = other->other;
3769         if (othernode) {
3770             NR::Point other_to_snap(0,0);
3771             if (sp_node_side_is_line(n, other)) {
3772                 other_to_snap = othernode->pos - n->pos;
3773             } else {
3774                 other_to_snap = other->pos - n->pos;
3775             }
3776             if (NR::L2(other_to_snap) > 1e-3) {
3777                 Radial rother_to_snap(other_to_snap);
3778                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
3779                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
3781                 // Snap to the closest.
3782                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
3783                        ? a_snapped
3784                        : a_oppo );
3785             }
3786         }
3788         rnew.a = a_snapped;
3789     }
3791     if (state & GDK_MOD1_MASK) {
3792         // lock handle length
3793         rnew.r = me->origin_radial.r;
3794     }
3796     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3797         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3798         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3799         rother.a += rnew.a - rme.a;
3800         other->pos = NR::Point(rother) + n->pos;
3801         if (other->knot) {
3802             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3803             sp_knot_moveto(other->knot, &other->pos);
3804         }
3805     }
3807     me->pos = NR::Point(rnew) + n->pos;
3808     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3810     // move knot, but without emitting the signal:
3811     // we cannot emit a "moved" signal because we're now processing it
3812     sp_knot_moveto(me->knot, &(me->pos));
3814     update_object(n->subpath->nodepath);
3816     /* status text */
3817     SPDesktop *desktop = n->subpath->nodepath->desktop;
3818     if (!desktop) return;
3819     SPEventContext *ec = desktop->event_context;
3820     if (!ec) return;
3821     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3822     if (!mc) return;
3824     double degrees = 180 / M_PI * rnew.a;
3825     if (degrees > 180) degrees -= 360;
3826     if (degrees < -180) degrees += 360;
3827     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3828         degrees = angle_to_compass (degrees);
3830     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3832     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
3833          _("<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);
3835     g_string_free(length, TRUE);
3838 /**
3839  * Node handle event callback.
3840  */
3841 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3843     gboolean ret = FALSE;
3844     switch (event->type) {
3845         case GDK_KEY_PRESS:
3846             switch (get_group0_keyval (&event->key)) {
3847                 case GDK_space:
3848                     if (event->key.state & GDK_BUTTON1_MASK) {
3849                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3850                         stamp_repr(nodepath);
3851                         ret = TRUE;
3852                     }
3853                     break;
3854                 default:
3855                     break;
3856             }
3857             break;
3858         case GDK_ENTER_NOTIFY:
3859             // we use an experimentally determined threshold that seems to work fine
3860             if (NR::L2(n->pos - knot->pos) < 0.75)
3861                 Inkscape::NodePath::Path::active_node = n;
3862             break;
3863         case GDK_LEAVE_NOTIFY:
3864             // we use an experimentally determined threshold that seems to work fine
3865             if (NR::L2(n->pos - knot->pos) < 0.75)
3866                 Inkscape::NodePath::Path::active_node = NULL;
3867             break;
3868         default:
3869             break;
3870     }
3872     return ret;
3875 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3876                                  Radial &rme, Radial &rother, gboolean const both)
3878     rme.a += angle;
3879     if ( both
3880          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3881          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3882     {
3883         rother.a += angle;
3884     }
3887 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3888                                         Radial &rme, Radial &rother, gboolean const both)
3890     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3892     gdouble r;
3893     if ( both
3894          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3895          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3896     {
3897         r = MAX(rme.r, rother.r);
3898     } else {
3899         r = rme.r;
3900     }
3902     gdouble const weird_angle = atan2(norm_angle, r);
3903 /* Bulia says norm_angle is just the visible distance that the
3904  * object's end must travel on the screen.  Left as 'angle' for want of
3905  * a better name.*/
3907     rme.a += weird_angle;
3908     if ( both
3909          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3910          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3911     {
3912         rother.a += weird_angle;
3913     }
3916 /**
3917  * Rotate one node.
3918  */
3919 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3921     Inkscape::NodePath::NodeSide *me, *other;
3922     bool both = false;
3924     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3925     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3927     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3928         me = &(n->p);
3929         other = &(n->n);
3930     } else if (!n->p.other) {
3931         me = &(n->n);
3932         other = &(n->p);
3933     } else {
3934         if (which > 0) { // right handle
3935             if (xn > xp) {
3936                 me = &(n->n);
3937                 other = &(n->p);
3938             } else {
3939                 me = &(n->p);
3940                 other = &(n->n);
3941             }
3942         } else if (which < 0){ // left 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 { // both handles
3951             me = &(n->n);
3952             other = &(n->p);
3953             both = true;
3954         }
3955     }
3957     Radial rme(me->pos - n->pos);
3958     Radial rother(other->pos - n->pos);
3960     if (screen) {
3961         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3962     } else {
3963         node_rotate_one_internal (*n, angle, rme, rother, both);
3964     }
3966     me->pos = n->pos + NR::Point(rme);
3968     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3969         other->pos =  n->pos + NR::Point(rother);
3970     }
3972     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3973     // so here we just move all the knots without emitting move signals, for speed
3974     sp_node_update_handles(n, false);
3977 /**
3978  * Rotate selected nodes.
3979  */
3980 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3982     if (!nodepath || !nodepath->selected) return;
3984     if (g_list_length(nodepath->selected) == 1) {
3985        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3986         node_rotate_one (n, angle, which, screen);
3987     } else {
3988        // rotate as an object:
3990         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3991         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3992         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3993             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3994             box.expandTo (n->pos); // contain all selected nodes
3995         }
3997         gdouble rot;
3998         if (screen) {
3999             gdouble const zoom = nodepath->desktop->current_zoom();
4000             gdouble const zmove = angle / zoom;
4001             gdouble const r = NR::L2(box.max() - box.midpoint());
4002             rot = atan2(zmove, r);
4003         } else {
4004             rot = angle;
4005         }
4007         NR::Point rot_center;
4008         if (Inkscape::NodePath::Path::active_node == NULL)
4009             rot_center = box.midpoint();
4010         else
4011             rot_center = Inkscape::NodePath::Path::active_node->pos;
4013         NR::Matrix t =
4014             NR::Matrix (NR::translate(-rot_center)) *
4015             NR::Matrix (NR::rotate(rot)) *
4016             NR::Matrix (NR::translate(rot_center));
4018         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4019             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4020             n->pos *= t;
4021             n->n.pos *= t;
4022             n->p.pos *= t;
4023             sp_node_update_handles(n, false);
4024         }
4025     }
4027     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4030 /**
4031  * Scale one node.
4032  */
4033 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4035     bool both = false;
4036     Inkscape::NodePath::NodeSide *me, *other;
4038     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4039     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4041     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4042         me = &(n->p);
4043         other = &(n->n);
4044         n->code = NR_CURVETO;
4045     } else if (!n->p.other) {
4046         me = &(n->n);
4047         other = &(n->p);
4048         if (n->n.other)
4049             n->n.other->code = NR_CURVETO;
4050     } else {
4051         if (which > 0) { // right handle
4052             if (xn > xp) {
4053                 me = &(n->n);
4054                 other = &(n->p);
4055                 if (n->n.other)
4056                     n->n.other->code = NR_CURVETO;
4057             } else {
4058                 me = &(n->p);
4059                 other = &(n->n);
4060                 n->code = NR_CURVETO;
4061             }
4062         } else if (which < 0){ // left handle
4063             if (xn <= xp) {
4064                 me = &(n->n);
4065                 other = &(n->p);
4066                 if (n->n.other)
4067                     n->n.other->code = NR_CURVETO;
4068             } else {
4069                 me = &(n->p);
4070                 other = &(n->n);
4071                 n->code = NR_CURVETO;
4072             }
4073         } else { // both handles
4074             me = &(n->n);
4075             other = &(n->p);
4076             both = true;
4077             n->code = NR_CURVETO;
4078             if (n->n.other)
4079                 n->n.other->code = NR_CURVETO;
4080         }
4081     }
4083     Radial rme(me->pos - n->pos);
4084     Radial rother(other->pos - n->pos);
4086     rme.r += grow;
4087     if (rme.r < 0) rme.r = 0;
4088     if (rme.a == HUGE_VAL) {
4089         if (me->other) { // if direction is unknown, initialize it towards the next node
4090             Radial rme_next(me->other->pos - n->pos);
4091             rme.a = rme_next.a;
4092         } else { // if there's no next, initialize to 0
4093             rme.a = 0;
4094         }
4095     }
4096     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4097         rother.r += grow;
4098         if (rother.r < 0) rother.r = 0;
4099         if (rother.a == HUGE_VAL) {
4100             rother.a = rme.a + M_PI;
4101         }
4102     }
4104     me->pos = n->pos + NR::Point(rme);
4106     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4107         other->pos = n->pos + NR::Point(rother);
4108     }
4110     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4111     // so here we just move all the knots without emitting move signals, for speed
4112     sp_node_update_handles(n, false);
4115 /**
4116  * Scale selected nodes.
4117  */
4118 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4120     if (!nodepath || !nodepath->selected) return;
4122     if (g_list_length(nodepath->selected) == 1) {
4123         // scale handles of the single selected node
4124         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4125         node_scale_one (n, grow, which);
4126     } else {
4127         // scale nodes as an "object":
4129         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4130         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4131         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4132             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4133             box.expandTo (n->pos); // contain all selected nodes
4134         }
4136         double scale = (box.maxExtent() + grow)/box.maxExtent();
4138         NR::Point scale_center;
4139         if (Inkscape::NodePath::Path::active_node == NULL)
4140             scale_center = box.midpoint();
4141         else
4142             scale_center = Inkscape::NodePath::Path::active_node->pos;
4144         NR::Matrix t =
4145             NR::Matrix (NR::translate(-scale_center)) *
4146             NR::Matrix (NR::scale(scale, scale)) *
4147             NR::Matrix (NR::translate(scale_center));
4149         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4150             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4151             n->pos *= t;
4152             n->n.pos *= t;
4153             n->p.pos *= t;
4154             sp_node_update_handles(n, false);
4155         }
4156     }
4158     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4161 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4163     if (!nodepath) return;
4164     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4167 /**
4168  * Flip selected nodes horizontally/vertically.
4169  */
4170 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
4172     if (!nodepath || !nodepath->selected) return;
4174     if (g_list_length(nodepath->selected) == 1 && !center) {
4175         // flip handles of the single selected node
4176         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4177         double temp = n->p.pos[axis];
4178         n->p.pos[axis] = n->n.pos[axis];
4179         n->n.pos[axis] = temp;
4180         sp_node_update_handles(n, false);
4181     } else {
4182         // scale nodes as an "object":
4184         NR::Rect box = sp_node_selected_bbox (nodepath);
4185         if (!center) {
4186             center = box.midpoint();
4187         }
4188         NR::Matrix t =
4189             NR::Matrix (NR::translate(- *center)) *
4190             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
4191             NR::Matrix (NR::translate(*center));
4193         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4194             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4195             n->pos *= t;
4196             n->n.pos *= t;
4197             n->p.pos *= t;
4198             sp_node_update_handles(n, false);
4199         }
4200     }
4202     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4205 NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4207     g_assert (nodepath->selected);
4209     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4210     NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4211     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4212         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4213         box.expandTo (n->pos); // contain all selected nodes
4214     }
4215     return box;
4218 //-----------------------------------------------
4219 /**
4220  * Return new subpath under given nodepath.
4221  */
4222 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4224     g_assert(nodepath);
4225     g_assert(nodepath->desktop);
4227    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4229     s->nodepath = nodepath;
4230     s->closed = FALSE;
4231     s->nodes = NULL;
4232     s->first = NULL;
4233     s->last = NULL;
4235     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4236     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4237     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4239     return s;
4242 /**
4243  * Destroy nodes in subpath, then subpath itself.
4244  */
4245 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4247     g_assert(subpath);
4248     g_assert(subpath->nodepath);
4249     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4251     while (subpath->nodes) {
4252         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4253     }
4255     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4257     g_free(subpath);
4260 /**
4261  * Link head to tail in subpath.
4262  */
4263 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4265     g_assert(!sp->closed);
4266     g_assert(sp->last != sp->first);
4267     g_assert(sp->first->code == NR_MOVETO);
4269     sp->closed = TRUE;
4271     //Link the head to the tail
4272     sp->first->p.other = sp->last;
4273     sp->last->n.other  = sp->first;
4274     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4275     sp->first          = sp->last;
4277     //Remove the extra end node
4278     sp_nodepath_node_destroy(sp->last->n.other);
4281 /**
4282  * Open closed (loopy) subpath at node.
4283  */
4284 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4286     g_assert(sp->closed);
4287     g_assert(n->subpath == sp);
4288     g_assert(sp->first == sp->last);
4290     /* We create new startpoint, current node will become last one */
4292    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4293                                                 &n->pos, &n->pos, &n->n.pos);
4296     sp->closed        = FALSE;
4298     //Unlink to make a head and tail
4299     sp->first         = new_path;
4300     sp->last          = n;
4301     n->n.other        = NULL;
4302     new_path->p.other = NULL;
4305 /**
4306  * Return new node in subpath with given properties.
4307  * \param pos Position of node.
4308  * \param ppos Handle position in previous direction
4309  * \param npos Handle position in previous direction
4310  */
4311 Inkscape::NodePath::Node *
4312 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)
4314     g_assert(sp);
4315     g_assert(sp->nodepath);
4316     g_assert(sp->nodepath->desktop);
4318     if (nodechunk == NULL)
4319         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4321     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4323     n->subpath  = sp;
4325     if (type != Inkscape::NodePath::NODE_NONE) {
4326         // use the type from sodipodi:nodetypes
4327         n->type = type;
4328     } else {
4329         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4330             // points are (almost) collinear
4331             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4332                 // endnode, or a node with a retracted handle
4333                 n->type = Inkscape::NodePath::NODE_CUSP;
4334             } else {
4335                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4336             }
4337         } else {
4338             n->type = Inkscape::NodePath::NODE_CUSP;
4339         }
4340     }
4342     n->code     = code;
4343     n->selected = FALSE;
4344     n->pos      = *pos;
4345     n->p.pos    = *ppos;
4346     n->n.pos    = *npos;
4348     n->dragging_out = NULL;
4350     Inkscape::NodePath::Node *prev;
4351     if (next) {
4352         //g_assert(g_list_find(sp->nodes, next));
4353         prev = next->p.other;
4354     } else {
4355         prev = sp->last;
4356     }
4358     if (prev)
4359         prev->n.other = n;
4360     else
4361         sp->first = n;
4363     if (next)
4364         next->p.other = n;
4365     else
4366         sp->last = n;
4368     n->p.other = prev;
4369     n->n.other = next;
4371     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"));
4372     sp_knot_set_position(n->knot, pos, 0);
4374     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4375     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4376     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4377     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4378     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4379     sp_knot_update_ctrl(n->knot);
4381     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4382     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4383     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4384     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4385     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4386     sp_knot_show(n->knot);
4388     // We only create handle knots and lines on demand
4389     n->p.knot = NULL;
4390     n->p.line = NULL;
4391     n->n.knot = NULL;
4392     n->n.line = NULL;
4394     sp->nodes = g_list_prepend(sp->nodes, n);
4396     return n;
4399 /**
4400  * Destroy node and its knots, link neighbors in subpath.
4401  */
4402 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4404     g_assert(node);
4405     g_assert(node->subpath);
4406     g_assert(SP_IS_KNOT(node->knot));
4408    Inkscape::NodePath::SubPath *sp = node->subpath;
4410     if (node->selected) { // first, deselect
4411         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4412         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4413     }
4415     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4417     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4418     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4419     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4420     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4421     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4422     g_object_unref(G_OBJECT(node->knot));
4424     if (node->p.knot) {
4425         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4426         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4427         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4428         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4429         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4430         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4431         g_object_unref(G_OBJECT(node->p.knot));
4432         node->p.knot = NULL;
4433     }
4435     if (node->n.knot) {
4436         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4437         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4438         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4439         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4440         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4441         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4442         g_object_unref(G_OBJECT(node->n.knot));
4443         node->n.knot = NULL;
4444     }
4446     if (node->p.line)
4447         gtk_object_destroy(GTK_OBJECT(node->p.line));
4448     if (node->n.line)
4449         gtk_object_destroy(GTK_OBJECT(node->n.line));
4451     if (sp->nodes) { // there are others nodes on the subpath
4452         if (sp->closed) {
4453             if (sp->first == node) {
4454                 g_assert(sp->last == node);
4455                 sp->first = node->n.other;
4456                 sp->last = sp->first;
4457             }
4458             node->p.other->n.other = node->n.other;
4459             node->n.other->p.other = node->p.other;
4460         } else {
4461             if (sp->first == node) {
4462                 sp->first = node->n.other;
4463                 sp->first->code = NR_MOVETO;
4464             }
4465             if (sp->last == node) sp->last = node->p.other;
4466             if (node->p.other) node->p.other->n.other = node->n.other;
4467             if (node->n.other) node->n.other->p.other = node->p.other;
4468         }
4469     } else { // this was the last node on subpath
4470         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4471     }
4473     g_mem_chunk_free(nodechunk, node);
4476 /**
4477  * Returns one of the node's two sides.
4478  * \param which Indicates which side.
4479  * \return Pointer to previous node side if which==-1, next if which==1.
4480  */
4481 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4483     g_assert(node);
4485     switch (which) {
4486         case -1:
4487             return &node->p;
4488         case 1:
4489             return &node->n;
4490         default:
4491             break;
4492     }
4494     g_assert_not_reached();
4496     return NULL;
4499 /**
4500  * Return the other side of the node, given one of its sides.
4501  */
4502 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4504     g_assert(node);
4506     if (me == &node->p) return &node->n;
4507     if (me == &node->n) return &node->p;
4509     g_assert_not_reached();
4511     return NULL;
4514 /**
4515  * Return NRPathcode on the given side of the node.
4516  */
4517 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4519     g_assert(node);
4521     if (me == &node->p) {
4522         if (node->p.other) return (NRPathcode)node->code;
4523         return NR_MOVETO;
4524     }
4526     if (me == &node->n) {
4527         if (node->n.other) return (NRPathcode)node->n.other->code;
4528         return NR_MOVETO;
4529     }
4531     g_assert_not_reached();
4533     return NR_END;
4536 /**
4537  * Return node with the given index
4538  */
4539 Inkscape::NodePath::Node *
4540 sp_nodepath_get_node_by_index(int index)
4542     Inkscape::NodePath::Node *e = NULL;
4544     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4545     if (!nodepath) {
4546         return e;
4547     }
4549     //find segment
4550     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4552         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4553         int n = g_list_length(sp->nodes);
4554         if (sp->closed) {
4555             n++;
4556         }
4558         //if the piece belongs to this subpath grab it
4559         //otherwise move onto the next subpath
4560         if (index < n) {
4561             e = sp->first;
4562             for (int i = 0; i < index; ++i) {
4563                 e = e->n.other;
4564             }
4565             break;
4566         } else {
4567             if (sp->closed) {
4568                 index -= (n+1);
4569             } else {
4570                 index -= n;
4571             }
4572         }
4573     }
4575     return e;
4578 /**
4579  * Returns plain text meaning of node type.
4580  */
4581 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4583     unsigned retracted = 0;
4584     bool endnode = false;
4586     for (int which = -1; which <= 1; which += 2) {
4587         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4588         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4589             retracted ++;
4590         if (!side->other)
4591             endnode = true;
4592     }
4594     if (retracted == 0) {
4595         if (endnode) {
4596                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4597                 return _("end node");
4598         } else {
4599             switch (node->type) {
4600                 case Inkscape::NodePath::NODE_CUSP:
4601                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4602                     return _("cusp");
4603                 case Inkscape::NodePath::NODE_SMOOTH:
4604                     // TRANSLATORS: "smooth" is an adjective here
4605                     return _("smooth");
4606                 case Inkscape::NodePath::NODE_SYMM:
4607                     return _("symmetric");
4608             }
4609         }
4610     } else if (retracted == 1) {
4611         if (endnode) {
4612             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4613             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4614         } else {
4615             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4616         }
4617     } else {
4618         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4619     }
4621     return NULL;
4624 /**
4625  * Handles content of statusbar as long as node tool is active.
4626  */
4627 void
4628 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4630     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");
4631     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4633     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4634     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4635     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4636     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4638     SPDesktop *desktop = NULL;
4639     if (nodepath) {
4640         desktop = nodepath->desktop;
4641     } else {
4642         desktop = SP_ACTIVE_DESKTOP;
4643     }
4645     SPEventContext *ec = desktop->event_context;
4646     if (!ec) return;
4647     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4648     if (!mc) return;
4650     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4652     if (selected_nodes == 0) {
4653         Inkscape::Selection *sel = desktop->selection;
4654         if (!sel || sel->isEmpty()) {
4655             mc->setF(Inkscape::NORMAL_MESSAGE,
4656                      _("Select a single object to edit its nodes or handles."));
4657         } else {
4658             if (nodepath) {
4659             mc->setF(Inkscape::NORMAL_MESSAGE,
4660                      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.",
4661                               "<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.",
4662                               total_nodes),
4663                      total_nodes);
4664             } else {
4665                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4666                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4667                 } else {
4668                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4669                 }
4670             }
4671         }
4672     } else if (nodepath && selected_nodes == 1) {
4673         mc->setF(Inkscape::NORMAL_MESSAGE,
4674                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4675                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4676                           total_nodes),
4677                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4678     } else {
4679         if (selected_subpaths > 1) {
4680             mc->setF(Inkscape::NORMAL_MESSAGE,
4681                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4682                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4683                               total_nodes),
4684                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4685         } else {
4686             mc->setF(Inkscape::NORMAL_MESSAGE,
4687                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4688                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4689                               total_nodes),
4690                      selected_nodes, total_nodes, when_selected);
4691         }
4692     }
4695 /*
4696  * returns a *copy* of the curve of that object.
4697  */
4698 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4699     if (!object)
4700         return NULL;
4702     SPCurve *curve = NULL;
4703     if (SP_IS_PATH(object)) {
4704         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4705         curve = curve_new->copy();
4706     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4707         const gchar *svgd = object->repr->attribute(key);
4708         if (svgd) {
4709             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4710             SPCurve *curve_new = new SPCurve(pv);
4711             if (curve_new) {
4712                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4713             }
4714         }
4715     }
4717     return curve;
4720 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4721     if (!np || !np->object || !curve)
4722         return;
4724     if (SP_IS_PATH(np->object)) {
4725         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4726             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4727         } else {
4728             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4729         }
4730     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4731         // FIXME: this writing to string and then reading from string is bound to be slow.
4732         // create a method to convert from curve directly to 2geom...
4733         gchar *svgpath = sp_svg_write_path( np->curve->get_pathvector() );
4734         LIVEPATHEFFECT(np->object)->lpe->setParameter(np->repr_key, svgpath);
4735         g_free(svgpath);
4737         np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4738     }
4741 SPCanvasItem *
4742 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) {
4743     SPCurve *flash_curve = curve->copy();
4744     Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity();
4745     flash_curve->transform(i2d);
4746     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4747     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4748     // unless we also flash the nodes...
4749     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4750     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4751     sp_canvas_item_show(canvasitem);
4752     flash_curve->unref();
4753     return canvasitem;
4756 SPCanvasItem *
4757 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) {
4758     return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path),
4759                                            prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff));
4762 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4763     np->show_helperpath = show;
4765     if (show) {
4766         SPCurve *helper_curve = np->curve->copy();
4767         helper_curve->transform(np->i2d );
4768         if (!np->helper_path) {
4769             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4770             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);
4771             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4772             sp_canvas_item_move_to_z(np->helper_path, 0);
4773             sp_canvas_item_show(np->helper_path);
4774         } else {
4775             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4776         }
4777         helper_curve->unref();
4778     } else {
4779         if (np->helper_path) {
4780             GtkObject *temp = np->helper_path;
4781             np->helper_path = NULL;
4782             gtk_object_destroy(temp);
4783         }
4784     }
4787 /* sp_nodepath_make_straight_path:
4788  *   Prevents user from curving the path by dragging a segment or activating handles etc.
4789  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
4790  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
4791  */
4792 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4793     np->straight_path = true;
4794     np->show_handles = false;
4795     g_message("add code to make the path straight.");
4796     // do sp_nodepath_convert_node_type on all nodes?
4797     // coding tip: search for this text : "Make selected segments lines"
4801 /*
4802   Local Variables:
4803   mode:c++
4804   c-file-style:"stroustrup"
4805   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4806   indent-tabs-mode:nil
4807   fill-column:99
4808   End:
4809 */
4810 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :