Code

455a0e7d8c67c2468148f847381592c9c6131993
[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/curve.h"
19 #include "display/sp-ctrlline.h"
20 #include "display/sodipodi-ctrl.h"
21 #include <glibmm/i18n.h>
22 #include "libnr/n-art-bpath.h"
23 #include "helper/units.h"
24 #include "knot.h"
25 #include "inkscape.h"
26 #include "document.h"
27 #include "sp-namedview.h"
28 #include "desktop.h"
29 #include "desktop-handles.h"
30 #include "snap.h"
31 #include "message-stack.h"
32 #include "message-context.h"
33 #include "node-context.h"
34 #include "selection-chemistry.h"
35 #include "selection.h"
36 #include "xml/repr.h"
37 #include "prefs-utils.h"
38 #include "sp-metrics.h"
39 #include "sp-path.h"
40 #include "libnr/nr-matrix-ops.h"
41 #include "splivarot.h"
42 #include "svg/svg.h"
43 #include "verbs.h"
44 #include "display/bezier-utils.h"
45 #include <vector>
46 #include <algorithm>
48 class NR::Matrix;
50 /// \todo
51 /// evil evil evil. FIXME: conflict of two different Path classes!
52 /// There is a conflict in the namespace between two classes named Path.
53 /// #include "sp-flowtext.h"
54 /// #include "sp-flowregion.h"
56 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
57 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
58 GType sp_flowregion_get_type (void);
59 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
60 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
61 GType sp_flowtext_get_type (void);
62 // end evil workaround
64 #include "helper/stlport.h"
67 /// \todo fixme: Implement these via preferences */
69 #define NODE_FILL          0xbfbfbf00
70 #define NODE_STROKE        0x000000ff
71 #define NODE_FILL_HI       0xff000000
72 #define NODE_STROKE_HI     0x000000ff
73 #define NODE_FILL_SEL      0x0000ffff
74 #define NODE_STROKE_SEL    0x000000ff
75 #define NODE_FILL_SEL_HI   0xff000000
76 #define NODE_STROKE_SEL_HI 0x000000ff
77 #define KNOT_FILL          0xffffffff
78 #define KNOT_STROKE        0x000000ff
79 #define KNOT_FILL_HI       0xff000000
80 #define KNOT_STROKE_HI     0x000000ff
82 static GMemChunk *nodechunk = NULL;
84 /* Creation from object */
86 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
87 static gchar *parse_nodetypes(gchar const *types, gint length);
89 /* Object updating */
91 static void stamp_repr(Inkscape::NodePath::Path *np);
92 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
93 static gchar *create_typestr(Inkscape::NodePath::Path *np);
95 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
97 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
99 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
101 /* Adjust handle placement, if the node or the other handle is moved */
102 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
103 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
105 /* Node event callbacks */
106 static void node_clicked(SPKnot *knot, guint state, gpointer data);
107 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
108 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
109 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
111 /* Handle event callbacks */
112 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
113 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
114 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
115 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
116 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
117 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
119 /* Constructors and destructors */
121 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
122 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
123 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
124 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
125 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
126                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
127 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
129 /* Helpers */
131 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
132 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
133 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
135 // active_node indicates mouseover node
136 static Inkscape::NodePath::Node *active_node = NULL;
138 /**
139  * \brief Creates new nodepath from item
140  */
141 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPItem *item, bool show_handles)
143     Inkscape::XML::Node *repr = SP_OBJECT(item)->repr;
145     /** \todo
146      * FIXME: remove this. We don't want to edit paths inside flowtext.
147      * Instead we will build our flowtext with cloned paths, so that the
148      * real paths are outside the flowtext and thus editable as usual.
149      */
150     if (SP_IS_FLOWTEXT(item)) {
151         for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
152             if SP_IS_FLOWREGION(child) {
153                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
154                 if (grandchild && SP_IS_PATH(grandchild)) {
155                     item = SP_ITEM(grandchild);
156                     break;
157                 }
158             }
159         }
160     }
162     if (!SP_IS_PATH(item))
163         return NULL;
164     SPPath *path = SP_PATH(item);
165     SPCurve *curve = sp_shape_get_curve(SP_SHAPE(path));
166     if (curve == NULL)
167         return NULL;
169     NArtBpath *bpath = sp_curve_first_bpath(curve);
170     gint length = curve->end;
171     if (length == 0)
172         return NULL; // prevent crash for one-node paths
174     gchar const *nodetypes = repr->attribute("sodipodi:nodetypes");
175     gchar *typestr = parse_nodetypes(nodetypes, length);
177     //Create new nodepath
178     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
179     if (!np)
180         return NULL;
182     // Set defaults
183     np->desktop     = desktop;
184     np->path        = path;
185     np->subpaths    = NULL;
186     np->selected    = NULL;
187     np->nodeContext = NULL; //Let the context that makes this set it
188     np->livarot_path = NULL;
189     np->local_change = 0;
190     np->show_handles = show_handles;
192     // we need to update item's transform from the repr here,
193     // because they may be out of sync when we respond
194     // to a change in repr by regenerating nodepath     --bb
195     sp_object_read_attr(SP_OBJECT(item), "transform");
197     np->i2d  = sp_item_i2d_affine(SP_ITEM(path));
198     np->d2i  = np->i2d.inverse();
199     np->repr = repr;
201     // create the subpath(s) from the bpath
202     NArtBpath *b = bpath;
203     while (b->code != NR_END) {
204         b = subpath_from_bpath(np, b, typestr + (b - bpath));
205     }
207     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
208     np->subpaths = g_list_reverse(np->subpaths);
210     g_free(typestr);
211     sp_curve_unref(curve);
213     // create the livarot representation from the same item
214     np->livarot_path = Path_for_item(item, true, true);
215     if (np->livarot_path)
216         np->livarot_path->ConvertWithBackData(0.01);
218     return np;
221 /**
222  * Destroys nodepath's subpaths, then itself, also tell context about it.
223  */
224 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
226     if (!np)  //soft fail, like delete
227         return;
229     while (np->subpaths) {
230         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
231     }
233     //Inform the context that made me, if any, that I am gone.
234     if (np->nodeContext)
235         np->nodeContext->nodepath = NULL;
237     g_assert(!np->selected);
239     if (np->livarot_path) {
240         delete np->livarot_path;
241         np->livarot_path = NULL;
242     }
244     np->desktop = NULL;
246     g_free(np);
250 /**
251  *  Return the node count of a given NodeSubPath.
252  */
253 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
255     if (!subpath)
256         return 0;
257     gint nodeCount = g_list_length(subpath->nodes);
258     return nodeCount;
261 /**
262  *  Return the node count of a given NodePath.
263  */
264 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
266     if (!np)
267         return 0;
268     gint nodeCount = 0;
269     for (GList *item = np->subpaths ; item ; item=item->next) {
270        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
271         nodeCount += g_list_length(subpath->nodes);
272     }
273     return nodeCount;
276 /**
277  *  Return the subpath count of a given NodePath.
278  */
279 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
281     if (!np)
282         return 0;
283     return g_list_length (np->subpaths);
286 /**
287  *  Return the selected node count of a given NodePath.
288  */
289 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
291     if (!np)
292         return 0;
293     return g_list_length (np->selected);
296 /**
297  *  Return the number of subpaths where nodes are selected in a given NodePath.
298  */
299 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
301     if (!np)
302         return 0;
303     if (!np->selected)
304         return 0;
305     if (!np->selected->next)
306         return 1;
307     gint count = 0;
308     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
309         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
310         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
311             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
312             if (node->selected) {
313                 count ++;
314                 break;
315             }
316         }
317     }
318     return count;
320  
321 /**
322  * Clean up a nodepath after editing.
323  *
324  * Currently we are deleting trivial subpaths.
325  */
326 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
328     GList *badSubPaths = NULL;
330     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
331     for (GList *l = nodepath->subpaths; l ; l=l->next) {
332        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
333        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
334             badSubPaths = g_list_append(badSubPaths, sp);
335     }
337     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
338     //also removes the subpath from nodepath->subpaths
339     for (GList *l = badSubPaths; l ; l=l->next) {
340        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
341         sp_nodepath_subpath_destroy(sp);
342     }
344     g_list_free(badSubPaths);
347 /**
348  * Create new nodepath from b, make it subpath of np.
349  * \param t The node type.
350  * \todo Fixme: t should be a proper type, rather than gchar
351  */
352 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
354     NR::Point ppos, pos, npos;
356     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
358     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
359     bool const closed = (b->code == NR_MOVETO);
361     pos = NR::Point(b->x3, b->y3) * np->i2d;
362     if (b[1].code == NR_CURVETO) {
363         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
364     } else {
365         npos = pos;
366     }
367     Inkscape::NodePath::Node *n;
368     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
369     g_assert(sp->first == n);
370     g_assert(sp->last  == n);
372     b++;
373     t++;
374     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
375         pos = NR::Point(b->x3, b->y3) * np->i2d;
376         if (b->code == NR_CURVETO) {
377             ppos = NR::Point(b->x2, b->y2) * np->i2d;
378         } else {
379             ppos = pos;
380         }
381         if (b[1].code == NR_CURVETO) {
382             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
383         } else {
384             npos = pos;
385         }
386         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
387         b++;
388         t++;
389     }
391     if (closed) sp_nodepath_subpath_close(sp);
393     return b;
396 /**
397  * Convert from sodipodi:nodetypes to new style type string.
398  */
399 static gchar *parse_nodetypes(gchar const *types, gint length)
401     g_assert(length > 0);
403     gchar *typestr = g_new(gchar, length + 1);
405     gint pos = 0;
407     if (types) {
408         for (gint i = 0; types[i] && ( i < length ); i++) {
409             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
410             if (types[i] != '\0') {
411                 switch (types[i]) {
412                     case 's':
413                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
414                         break;
415                     case 'z':
416                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
417                         break;
418                     case 'c':
419                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
420                         break;
421                     default:
422                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
423                         break;
424                 }
425             }
426         }
427     }
429     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
431     return typestr;
434 /**
435  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
436  * updated but repr is not (for speed). Used during curve and node drag.
437  */
438 static void update_object(Inkscape::NodePath::Path *np)
440     g_assert(np);
442     SPCurve *curve = create_curve(np);
444     sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
446     sp_curve_unref(curve);
449 /**
450  * Update XML path node with data from path object.
451  */
452 static void update_repr_internal(Inkscape::NodePath::Path *np)
454     g_assert(np);
456     Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
458     SPCurve *curve = create_curve(np);
459     gchar *typestr = create_typestr(np);
460     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
462     if (repr->attribute("d") == NULL || strcmp(svgpath, repr->attribute("d"))) { // d changed
463         np->local_change++;
464         repr->setAttribute("d", svgpath);
465     }
467     if (repr->attribute("sodipodi:nodetypes") == NULL || strcmp(typestr, repr->attribute("sodipodi:nodetypes"))) { // nodetypes changed
468         np->local_change++;
469         repr->setAttribute("sodipodi:nodetypes", typestr);
470     }
472     g_free(svgpath);
473     g_free(typestr);
474     sp_curve_unref(curve);
477 /**
478  * Update XML path node with data from path object, commit changes forever.
479  */
480 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np)
482     update_repr_internal(np);
483     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE, 
484                      /* TODO: annotate */ "nodepath.cpp:484");
486     if (np->livarot_path) {
487         delete np->livarot_path;
488         np->livarot_path = NULL;
489     }
491     if (np->path && SP_IS_ITEM(np->path)) {
492         np->livarot_path = Path_for_item (np->path, true, true);
493         if (np->livarot_path)
494             np->livarot_path->ConvertWithBackData(0.01);
495     }
498 /**
499  * Update XML path node with data from path object, commit changes with undo.
500  */
501 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key)
503     update_repr_internal(np);
504     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE, 
505                            /* TODO: annotate */ "nodepath.cpp:505");
507     if (np->livarot_path) {
508         delete np->livarot_path;
509         np->livarot_path = NULL;
510     }
512     if (np->path && SP_IS_ITEM(np->path)) {
513         np->livarot_path = Path_for_item (np->path, true, true);
514         if (np->livarot_path)
515             np->livarot_path->ConvertWithBackData(0.01);
516     }
519 /**
520  * Make duplicate of path, replace corresponding XML node in tree, commit.
521  */
522 static void stamp_repr(Inkscape::NodePath::Path *np)
524     g_assert(np);
526     Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
527     Inkscape::XML::Node *new_repr = old_repr->duplicate();
529     // remember the position of the item
530     gint pos = old_repr->position();
531     // remember parent
532     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
534     SPCurve *curve = create_curve(np);
535     gchar *typestr = create_typestr(np);
537     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
539     new_repr->setAttribute("d", svgpath);
540     new_repr->setAttribute("sodipodi:nodetypes", typestr);
542     // add the new repr to the parent
543     parent->appendChild(new_repr);
544     // move to the saved position
545     new_repr->setPosition(pos > 0 ? pos : 0);
547     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
548                      /* TODO: annotate */ "nodepath.cpp:548");
550     Inkscape::GC::release(new_repr);
551     g_free(svgpath);
552     g_free(typestr);
553     sp_curve_unref(curve);
556 /**
557  * Create curve from path.
558  */
559 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
561     SPCurve *curve = sp_curve_new();
563     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
564        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
565         sp_curve_moveto(curve,
566                         sp->first->pos * np->d2i);
567        Inkscape::NodePath::Node *n = sp->first->n.other;
568         while (n) {
569             NR::Point const end_pt = n->pos * np->d2i;
570             switch (n->code) {
571                 case NR_LINETO:
572                     sp_curve_lineto(curve, end_pt);
573                     break;
574                 case NR_CURVETO:
575                     sp_curve_curveto(curve,
576                                      n->p.other->n.pos * np->d2i,
577                                      n->p.pos * np->d2i,
578                                      end_pt);
579                     break;
580                 default:
581                     g_assert_not_reached();
582                     break;
583             }
584             if (n != sp->last) {
585                 n = n->n.other;
586             } else {
587                 n = NULL;
588             }
589         }
590         if (sp->closed) {
591             sp_curve_closepath(curve);
592         }
593     }
595     return curve;
598 /**
599  * Convert path type string to sodipodi:nodetypes style.
600  */
601 static gchar *create_typestr(Inkscape::NodePath::Path *np)
603     gchar *typestr = g_new(gchar, 32);
604     gint len = 32;
605     gint pos = 0;
607     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
608        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
610         if (pos >= len) {
611             typestr = g_renew(gchar, typestr, len + 32);
612             len += 32;
613         }
615         typestr[pos++] = 'c';
617        Inkscape::NodePath::Node *n;
618         n = sp->first->n.other;
619         while (n) {
620             gchar code;
622             switch (n->type) {
623                 case Inkscape::NodePath::NODE_CUSP:
624                     code = 'c';
625                     break;
626                 case Inkscape::NodePath::NODE_SMOOTH:
627                     code = 's';
628                     break;
629                 case Inkscape::NodePath::NODE_SYMM:
630                     code = 'z';
631                     break;
632                 default:
633                     g_assert_not_reached();
634                     code = '\0';
635                     break;
636             }
638             if (pos >= len) {
639                 typestr = g_renew(gchar, typestr, len + 32);
640                 len += 32;
641             }
643             typestr[pos++] = code;
645             if (n != sp->last) {
646                 n = n->n.other;
647             } else {
648                 n = NULL;
649             }
650         }
651     }
653     if (pos >= len) {
654         typestr = g_renew(gchar, typestr, len + 1);
655         len += 1;
656     }
658     typestr[pos++] = '\0';
660     return typestr;
663 /**
664  * Returns current path in context.
665  */
666 static Inkscape::NodePath::Path *sp_nodepath_current()
668     if (!SP_ACTIVE_DESKTOP) {
669         return NULL;
670     }
672     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
674     if (!SP_IS_NODE_CONTEXT(event_context)) {
675         return NULL;
676     }
678     return SP_NODE_CONTEXT(event_context)->nodepath;
683 /**
684  \brief Fills node and handle positions for three nodes, splitting line
685   marked by end at distance t.
686  */
687 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
689     g_assert(new_path != NULL);
690     g_assert(end      != NULL);
692     g_assert(end->p.other == new_path);
693    Inkscape::NodePath::Node *start = new_path->p.other;
694     g_assert(start);
696     if (end->code == NR_LINETO) {
697         new_path->type =Inkscape::NodePath::NODE_CUSP;
698         new_path->code = NR_LINETO;
699         new_path->pos  = (t * start->pos + (1 - t) * end->pos);
700     } else {
701         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
702         new_path->code = NR_CURVETO;
703         gdouble s      = 1 - t;
704         for (int dim = 0; dim < 2; dim++) {
705             NR::Coord const f000 = start->pos[dim];
706             NR::Coord const f001 = start->n.pos[dim];
707             NR::Coord const f011 = end->p.pos[dim];
708             NR::Coord const f111 = end->pos[dim];
709             NR::Coord const f00t = s * f000 + t * f001;
710             NR::Coord const f01t = s * f001 + t * f011;
711             NR::Coord const f11t = s * f011 + t * f111;
712             NR::Coord const f0tt = s * f00t + t * f01t;
713             NR::Coord const f1tt = s * f01t + t * f11t;
714             NR::Coord const fttt = s * f0tt + t * f1tt;
715             start->n.pos[dim]    = f00t;
716             new_path->p.pos[dim] = f0tt;
717             new_path->pos[dim]   = fttt;
718             new_path->n.pos[dim] = f1tt;
719             end->p.pos[dim]      = f11t;
720         }
721     }
724 /**
725  * Adds new node on direct line between two nodes, activates handles of all
726  * three nodes.
727  */
728 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
730     g_assert(end);
731     g_assert(end->subpath);
732     g_assert(g_list_find(end->subpath->nodes, end));
734    Inkscape::NodePath::Node *start = end->p.other;
735     g_assert( start->n.other == end );
736    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
737                                                end,
738                                               Inkscape::NodePath::NODE_SMOOTH,
739                                                (NRPathcode)end->code,
740                                                &start->pos, &start->pos, &start->n.pos);
741     sp_nodepath_line_midpoint(newnode, end, t);
743     sp_node_update_handles(start);
744     sp_node_update_handles(newnode);
745     sp_node_update_handles(end);
747     return newnode;
750 /**
751 \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
752 */
753 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
755     g_assert(node);
756     g_assert(node->subpath);
757     g_assert(g_list_find(node->subpath->nodes, node));
759    Inkscape::NodePath::SubPath *sp = node->subpath;
760     Inkscape::NodePath::Path *np    = sp->nodepath;
762     if (sp->closed) {
763         sp_nodepath_subpath_open(sp, node);
764         return sp->first;
765     } else {
766         // no break for end nodes
767         if (node == sp->first) return NULL;
768         if (node == sp->last ) return NULL;
770         // create a new subpath
771        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
773         // duplicate the break node as start of the new subpath
774        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
776         while (node->n.other) { // copy the remaining nodes into the new subpath
777            Inkscape::NodePath::Node *n  = node->n.other;
778            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);
779             if (n->selected) {
780                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
781             }
782             sp_nodepath_node_destroy(n); // remove the point on the original subpath
783         }
785         return newnode;
786     }
789 /**
790  * Duplicate node and connect to neighbours.
791  */
792 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
794     g_assert(node);
795     g_assert(node->subpath);
796     g_assert(g_list_find(node->subpath->nodes, node));
798    Inkscape::NodePath::SubPath *sp = node->subpath;
800     NRPathcode code = (NRPathcode) node->code;
801     if (code == NR_MOVETO) { // if node is the endnode,
802         node->code = NR_LINETO; // new one is inserted before it, so change that to line
803     }
805     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
807     if (!node->n.other || !node->p.other) // if node is an endnode, select it
808         return node;
809     else
810         return newnode; // otherwise select the newly created node
813 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
815     node->p.pos = (node->pos + (node->pos - node->n.pos));
818 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
820     node->n.pos = (node->pos + (node->pos - node->p.pos));
823 /**
824  * Change line type at node, with side effects on neighbours.
825  */
826 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
828     g_assert(end);
829     g_assert(end->subpath);
830     g_assert(end->p.other);
832     if (end->code == static_cast< guint > ( code ) )
833         return;
835    Inkscape::NodePath::Node *start = end->p.other;
837     end->code = code;
839     if (code == NR_LINETO) {
840         if (start->code == NR_LINETO) start->type =Inkscape::NodePath::NODE_CUSP;
841         if (end->n.other) {
842             if (end->n.other->code == NR_LINETO) end->type =Inkscape::NodePath::NODE_CUSP;
843         }
844         sp_node_adjust_handle(start, -1);
845         sp_node_adjust_handle(end, 1);
846     } else {
847         NR::Point delta = end->pos - start->pos;
848         start->n.pos = start->pos + delta / 3;
849         end->p.pos = end->pos - delta / 3;
850         sp_node_adjust_handle(start, 1);
851         sp_node_adjust_handle(end, -1);
852     }
854     sp_node_update_handles(start);
855     sp_node_update_handles(end);
858 /**
859  * Change node type, and its handles accordingly.
860  */
861 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
863     g_assert(node);
864     g_assert(node->subpath);
866     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
867         return node;
869     if ((node->p.other != NULL) && (node->n.other != NULL)) {
870         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
871             type =Inkscape::NodePath::NODE_CUSP;
872         }
873     }
875     node->type = type;
877     if (node->type == Inkscape::NodePath::NODE_CUSP) {
878         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
879         node->knot->setSize (node->selected? 11 : 9);
880         sp_knot_update_ctrl(node->knot);
881     } else {
882         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
883         node->knot->setSize (node->selected? 9 : 7);
884         sp_knot_update_ctrl(node->knot);
885     }
887     // if one of handles is mouseovered, preserve its position
888     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
889         sp_node_adjust_handle(node, 1);
890     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
891         sp_node_adjust_handle(node, -1);
892     } else {
893         sp_node_adjust_handles(node);
894     }
896     sp_node_update_handles(node);
898     sp_nodepath_update_statusbar(node->subpath->nodepath);
900     return node;
903 /**
904  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
905  * adjacent segments from lines to curves.
906 */
907 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
909     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
910         if ((node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos)) {
911             // convert adjacent segment BEFORE to curve
912             node->code = NR_CURVETO;
913             NR::Point delta;
914             if (node->n.other != NULL)
915                 delta = node->n.other->pos - node->p.other->pos;
916             else
917                 delta = node->pos - node->p.other->pos;
918             node->p.pos = node->pos - delta / 4;
919             sp_node_update_handles(node);
920         }
922         if ((node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos)) {
923             // convert adjacent segment AFTER to curve
924             node->n.other->code = NR_CURVETO;
925             NR::Point delta;
926             if (node->p.other != NULL)
927                 delta = node->p.other->pos - node->n.other->pos;
928             else
929                 delta = node->pos - node->n.other->pos;
930             node->n.pos = node->pos - delta / 4;
931             sp_node_update_handles(node);
932         }
933     }
935     sp_nodepath_set_node_type (node, type);
938 /**
939  * Move node to point, and adjust its and neighbouring handles.
940  */
941 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
943     NR::Point delta = p - node->pos;
944     node->pos = p;
946     node->p.pos += delta;
947     node->n.pos += delta;
949     if (node->p.other) {
950         if (node->code == NR_LINETO) {
951             sp_node_adjust_handle(node, 1);
952             sp_node_adjust_handle(node->p.other, -1);
953         }
954     }
955     if (node->n.other) {
956         if (node->n.other->code == NR_LINETO) {
957             sp_node_adjust_handle(node, -1);
958             sp_node_adjust_handle(node->n.other, 1);
959         }
960     }
962     // this function is only called from batch movers that will update display at the end
963     // themselves, so here we just move all the knots without emitting move signals, for speed
964     sp_node_update_handles(node, false);
967 /**
968  * Call sp_node_moveto() for node selection and handle possible snapping.
969  */
970 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
971                                             bool const snap = true)
973     NR::Coord best = NR_HUGE;
974     NR::Point delta(dx, dy);
975     NR::Point best_pt = delta;
977     if (snap) {
978         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
979         
980         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
981             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
982             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAP_POINT, n->pos + delta, NULL);
983             if (s.getDistance() < best) {
984                 best = s.getDistance();
985                 best_pt = s.getPoint() - n->pos;
986             }
987         }
988     }
990     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
991        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
992         sp_node_moveto(n, n->pos + best_pt);
993     }
995     // do not update repr here so that node dragging is acceptably fast
996     update_object(nodepath);
999 /**
1000 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1001 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1002 near x = 0.
1003  */
1004 double
1005 sculpt_profile (double x, double alpha, guint profile)
1007     if (x >= 1)
1008         return 0;
1009     if (x <= 0)
1010         return 1;
1012     switch (profile) {
1013         case SCULPT_PROFILE_LINEAR:
1014         return 1 - x;
1015         case SCULPT_PROFILE_BELL:
1016         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1017         case SCULPT_PROFILE_ELLIPTIC:
1018         return sqrt(1 - x*x);
1019     }
1021     return 1;
1024 double
1025 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1027     // extremely primitive for now, don't have time to look for the real one
1028     double lower = NR::L2(b - a);
1029     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1030     return (lower + upper)/2;
1033 void
1034 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1036     n->pos = n->origin + delta;
1037     n->n.pos = n->n.origin + delta_n;
1038     n->p.pos = n->p.origin + delta_p;
1039     sp_node_adjust_handles(n);
1040     sp_node_update_handles(n, false);
1043 /**
1044  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1045  * on how far they are from the dragged node n.
1046  */
1047 static void 
1048 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1050     g_assert (n);
1051     g_assert (nodepath);
1052     g_assert (n->subpath->nodepath == nodepath);
1054     double pressure = n->knot->pressure;
1055     if (pressure == 0)
1056         pressure = 0.5; // default
1057     pressure = CLAMP (pressure, 0.2, 0.8);
1059     // map pressure to alpha = 1/5 ... 5
1060     double alpha = 1 - 2 * fabs(pressure - 0.5);
1061     if (pressure > 0.5)
1062         alpha = 1/alpha;
1064     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1066     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1067         // Only one subpath has selected nodes:
1068         // use linear mode, where the distance from n to node being dragged is calculated along the path
1070         double n_sel_range = 0, p_sel_range = 0;
1071         guint n_nodes = 0, p_nodes = 0;
1072         guint n_sel_nodes = 0, p_sel_nodes = 0;
1074         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1075         {
1076             double n_range = 0, p_range = 0;
1077             bool n_going = true, p_going = true;
1078             Inkscape::NodePath::Node *n_node = n;
1079             Inkscape::NodePath::Node *p_node = n;
1080             do {
1081                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1082                 if (n_node && n_going)
1083                     n_node = n_node->n.other;
1084                 if (n_node == NULL) {
1085                     n_going = false;
1086                 } else {
1087                     n_nodes ++;
1088                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1089                     if (n_node->selected) {
1090                         n_sel_nodes ++;
1091                         n_sel_range = n_range;
1092                     }
1093                     if (n_node == p_node) {
1094                         n_going = false;
1095                         p_going = false;
1096                     }
1097                 }
1098                 if (p_node && p_going)
1099                     p_node = p_node->p.other;
1100                 if (p_node == NULL) {
1101                     p_going = false;
1102                 } else {
1103                     p_nodes ++;
1104                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1105                     if (p_node->selected) {
1106                         p_sel_nodes ++;
1107                         p_sel_range = p_range;
1108                     }
1109                     if (p_node == n_node) {
1110                         n_going = false;
1111                         p_going = false;
1112                     }
1113                 }
1114             } while (n_going || p_going);
1115         }
1117         // Second pass: actually move nodes in this subpath
1118         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1119         {
1120             double n_range = 0, p_range = 0;
1121             bool n_going = true, p_going = true;
1122             Inkscape::NodePath::Node *n_node = n;
1123             Inkscape::NodePath::Node *p_node = n;
1124             do {
1125                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1126                 if (n_node && n_going)
1127                     n_node = n_node->n.other;
1128                 if (n_node == NULL) {
1129                     n_going = false;
1130                 } else {
1131                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1132                     if (n_node->selected) {
1133                         sp_nodepath_move_node_and_handles (n_node, 
1134                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1135                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1136                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1137                     }
1138                     if (n_node == p_node) {
1139                         n_going = false;
1140                         p_going = false;
1141                     }
1142                 }
1143                 if (p_node && p_going)
1144                     p_node = p_node->p.other;
1145                 if (p_node == NULL) {
1146                     p_going = false;
1147                 } else {
1148                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1149                     if (p_node->selected) {
1150                         sp_nodepath_move_node_and_handles (p_node, 
1151                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1152                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1153                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1154                     }
1155                     if (p_node == n_node) {
1156                         n_going = false;
1157                         p_going = false;
1158                     }
1159                 }
1160             } while (n_going || p_going);
1161         }
1163     } else {
1164         // Multiple subpaths have selected nodes:
1165         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1166         // TODO: correct these distances taking into account their angle relative to the bisector, so as to 
1167         // fix the pear-like shape when sculpting e.g. a ring
1169         // First pass: calculate range
1170         gdouble direct_range = 0;
1171         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1172             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1173             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1174                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1175                 if (node->selected) {
1176                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1177                 }
1178             }
1179         }
1181         // Second pass: actually move nodes
1182         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1183             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1184             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1185                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1186                 if (node->selected) {
1187                     if (direct_range > 1e-6) {
1188                         sp_nodepath_move_node_and_handles (node,
1189                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1190                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1191                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1192                     } else {
1193                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1194                     }
1196                 }
1197             }
1198         }
1199     }
1201     // do not update repr here so that node dragging is acceptably fast
1202     update_object(nodepath);
1206 /**
1207  * Move node selection to point, adjust its and neighbouring handles,
1208  * handle possible snapping, and commit the change with possible undo.
1209  */
1210 void
1211 sp_node_selected_move(gdouble dx, gdouble dy)
1213     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1214     if (!nodepath) return;
1216     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1218     if (dx == 0) {
1219         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
1220     } else if (dy == 0) {
1221         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
1222     } else {
1223         sp_nodepath_update_repr(nodepath);
1224     }
1227 /**
1228  * Move node selection off screen and commit the change.
1229  */
1230 void
1231 sp_node_selected_move_screen(gdouble dx, gdouble dy)
1233     // borrowed from sp_selection_move_screen in selection-chemistry.c
1234     // we find out the current zoom factor and divide deltas by it
1235     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1237     gdouble zoom = desktop->current_zoom();
1238     gdouble zdx = dx / zoom;
1239     gdouble zdy = dy / zoom;
1241     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1242     if (!nodepath) return;
1244     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1246     if (dx == 0) {
1247         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
1248     } else if (dy == 0) {
1249         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
1250     } else {
1251         sp_nodepath_update_repr(nodepath);
1252     }
1255 /** If they don't yet exist, creates knot and line for the given side of the node */
1256 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1258     if (!side->knot) {
1259         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"));
1261         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1262         side->knot->setSize (7);
1263         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1264         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1265         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1266         sp_knot_update_ctrl(side->knot);
1268         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1269         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1270         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1271         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1272         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1273         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1274     }
1276     if (!side->line) {
1277         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1278                                         SP_TYPE_CTRLLINE, NULL);
1279     }
1282 /**
1283  * Ensure the given handle of the node is visible/invisible, update its screen position
1284  */
1285 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1287     g_assert(node != NULL);
1289    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1290     NRPathcode code = sp_node_path_code_from_side(node, side);
1292     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1294     if (show_handle) {
1295         if (!side->knot) { // No handle knot at all
1296             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1297             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1298             side->knot->pos = side->pos;
1299             if (side->knot->item) 
1300                 SP_CTRL(side->knot->item)->moveto(side->pos);
1301             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1302             sp_knot_show(side->knot);
1303         } else {
1304             if (side->knot->pos != side->pos) { // only if it's really moved
1305                 if (fire_move_signals) {
1306                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1307                 } else {
1308                     sp_knot_moveto(side->knot, &side->pos);
1309                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1310                 }
1311             }
1312             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1313                 sp_knot_show(side->knot);
1314             }
1315         }
1316         sp_canvas_item_show(side->line);
1317     } else {
1318         if (side->knot) {
1319             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1320                 sp_knot_hide(side->knot);
1321             }
1322         }
1323         if (side->line) {
1324             sp_canvas_item_hide(side->line);
1325         }
1326     }
1329 /**
1330  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1331  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1332  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1333  * updated; otherwise, just move the knots silently (used in batch moves).
1334  */
1335 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1337     g_assert(node != NULL);
1339     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1340         sp_knot_show(node->knot);
1341     }
1343     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1344         if (fire_move_signals)
1345             sp_knot_set_position(node->knot, &node->pos, 0);
1346         else 
1347             sp_knot_moveto(node->knot, &node->pos);
1348     }
1350     gboolean show_handles = node->selected;
1351     if (node->p.other != NULL) {
1352         if (node->p.other->selected) show_handles = TRUE;
1353     }
1354     if (node->n.other != NULL) {
1355         if (node->n.other->selected) show_handles = TRUE;
1356     }
1358     if (node->subpath->nodepath->show_handles == false)
1359         show_handles = FALSE;
1361     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1362     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1365 /**
1366  * Call sp_node_update_handles() for all nodes on subpath.
1367  */
1368 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1370     g_assert(subpath != NULL);
1372     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1373         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1374     }
1377 /**
1378  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1379  */
1380 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1382     g_assert(nodepath != NULL);
1384     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1385         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1386     }
1389 void
1390 sp_nodepath_show_handles(bool show)
1392     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1393     if (nodepath == NULL) return;
1395     nodepath->show_handles = show;
1396     sp_nodepath_update_handles(nodepath);
1399 /**
1400  * Adds all selected nodes in nodepath to list.
1401  */
1402 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1404     StlConv<Node *>::list(l, selected);
1405 /// \todo this adds a copying, rework when the selection becomes a stl list
1408 /**
1409  * Align selected nodes on the specified axis.
1410  */
1411 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1413     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1414         return;
1415     }
1417     if ( !nodepath->selected->next ) { // only one node selected
1418         return;
1419     }
1420    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1421     NR::Point dest(pNode->pos);
1422     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1423         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1424         if (pNode) {
1425             dest[axis] = pNode->pos[axis];
1426             sp_node_moveto(pNode, dest);
1427         }
1428     }
1430     sp_nodepath_update_repr(nodepath);
1433 /// Helper struct.
1434 struct NodeSort
1436    Inkscape::NodePath::Node *_node;
1437     NR::Coord _coord;
1438     /// \todo use vectorof pointers instead of calling copy ctor
1439     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1440         _node(node), _coord(node->pos[axis])
1441     {}
1443 };
1445 static bool operator<(NodeSort const &a, NodeSort const &b)
1447     return (a._coord < b._coord);
1450 /**
1451  * Distribute selected nodes on the specified axis.
1452  */
1453 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1455     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1456         return;
1457     }
1459     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1460         return;
1461     }
1463    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1464     std::vector<NodeSort> sorted;
1465     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1466         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1467         if (pNode) {
1468             NodeSort n(pNode, axis);
1469             sorted.push_back(n);
1470             //dest[axis] = pNode->pos[axis];
1471             //sp_node_moveto(pNode, dest);
1472         }
1473     }
1474     std::sort(sorted.begin(), sorted.end());
1475     unsigned int len = sorted.size();
1476     //overall bboxes span
1477     float dist = (sorted.back()._coord -
1478                   sorted.front()._coord);
1479     //new distance between each bbox
1480     float step = (dist) / (len - 1);
1481     float pos = sorted.front()._coord;
1482     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1483           it < sorted.end();
1484           it ++ )
1485     {
1486         NR::Point dest((*it)._node->pos);
1487         dest[axis] = pos;
1488         sp_node_moveto((*it)._node, dest);
1489         pos += step;
1490     }
1492     sp_nodepath_update_repr(nodepath);
1496 /**
1497  * Call sp_nodepath_line_add_node() for all selected segments.
1498  */
1499 void
1500 sp_node_selected_add_node(void)
1502     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1503     if (!nodepath) {
1504         return;
1505     }
1507     GList *nl = NULL;
1509     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1510        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1511         g_assert(t->selected);
1512         if (t->p.other && t->p.other->selected) {
1513             nl = g_list_prepend(nl, t);
1514         }
1515     }
1517     while (nl) {
1518        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1519        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1520         sp_nodepath_node_select(n, TRUE, FALSE);
1521         nl = g_list_remove(nl, t);
1522     }
1524     /** \todo fixme: adjust ? */
1525     sp_nodepath_update_handles(nodepath);
1527     sp_nodepath_update_repr(nodepath);
1529     sp_nodepath_update_statusbar(nodepath);
1532 /**
1533  * Select segment nearest to point
1534  */
1535 void
1536 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1538     if (!nodepath) {
1539         return;
1540     }
1542     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1544     //find segment to segment
1545     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1547     gboolean force = FALSE;
1548     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1549         force = TRUE;
1550     }
1551     sp_nodepath_node_select(e, (gboolean) toggle, force);
1552     if (e->p.other)
1553         sp_nodepath_node_select(e->p.other, TRUE, force);
1555     sp_nodepath_update_handles(nodepath);
1557     sp_nodepath_update_statusbar(nodepath);
1560 /**
1561  * Add a node nearest to point
1562  */
1563 void
1564 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1566     if (!nodepath) {
1567         return;
1568     }
1570     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1572     //find segment to split
1573     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1575     //don't know why but t seems to flip for lines
1576     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1577         position.t = 1.0 - position.t;
1578     }
1579     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1580     sp_nodepath_node_select(n, FALSE, TRUE);
1582     /* fixme: adjust ? */
1583     sp_nodepath_update_handles(nodepath);
1585     sp_nodepath_update_repr(nodepath);
1587     sp_nodepath_update_statusbar(nodepath);
1590 /*
1591  * Adjusts a segment so that t moves by a certain delta for dragging
1592  * converts lines to curves
1593  *
1594  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1595  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1596  */
1597 void
1598 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta)
1600     /* feel good is an arbitrary parameter that distributes the delta between handles
1601      * if t of the drag point is less than 1/6 distance form the endpoint only
1602      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1603      */
1604     double feel_good;
1605     if (t <= 1.0 / 6.0)
1606         feel_good = 0;
1607     else if (t <= 0.5)
1608         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1609     else if (t <= 5.0 / 6.0)
1610         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1611     else
1612         feel_good = 1;
1614     //if we're dragging a line convert it to a curve
1615     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1616         sp_nodepath_set_line_type(e, NR_CURVETO);
1617     }
1619     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1620     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1621     e->p.other->n.pos += offsetcoord0;
1622     e->p.pos += offsetcoord1;
1624     // adjust handles of adjacent nodes where necessary
1625     sp_node_adjust_handle(e,1);
1626     sp_node_adjust_handle(e->p.other,-1);
1628     sp_nodepath_update_handles(e->subpath->nodepath);
1630     update_object(e->subpath->nodepath);
1632     sp_nodepath_update_statusbar(e->subpath->nodepath);
1636 /**
1637  * Call sp_nodepath_break() for all selected segments.
1638  */
1639 void sp_node_selected_break()
1641     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1642     if (!nodepath) return;
1644     GList *temp = NULL;
1645     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1646        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1647        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1648         if (nn == NULL) continue; // no break, no new node
1649         temp = g_list_prepend(temp, nn);
1650     }
1652     if (temp) {
1653         sp_nodepath_deselect(nodepath);
1654     }
1655     for (GList *l = temp; l != NULL; l = l->next) {
1656         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1657     }
1659     sp_nodepath_update_handles(nodepath);
1661     sp_nodepath_update_repr(nodepath);
1664 /**
1665  * Duplicate the selected node(s).
1666  */
1667 void sp_node_selected_duplicate()
1669     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1670     if (!nodepath) {
1671         return;
1672     }
1674     GList *temp = NULL;
1675     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1676        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1677        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1678         if (nn == NULL) continue; // could not duplicate
1679         temp = g_list_prepend(temp, nn);
1680     }
1682     if (temp) {
1683         sp_nodepath_deselect(nodepath);
1684     }
1685     for (GList *l = temp; l != NULL; l = l->next) {
1686         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1687     }
1689     sp_nodepath_update_handles(nodepath);
1691     sp_nodepath_update_repr(nodepath);
1694 /**
1695  *  Join two nodes by merging them into one.
1696  */
1697 void sp_node_selected_join()
1699     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1700     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1702     if (g_list_length(nodepath->selected) != 2) {
1703         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1704         return;
1705     }
1707    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1708    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1710     g_assert(a != b);
1711     g_assert(a->p.other || a->n.other);
1712     g_assert(b->p.other || b->n.other);
1714     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1715         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1716         return;
1717     }
1719     /* a and b are endpoints */
1721     NR::Point c;
1722     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1723         c = a->pos;
1724     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1725         c = b->pos;
1726     } else {
1727         c = (a->pos + b->pos) / 2;
1728     }
1730     if (a->subpath == b->subpath) {
1731        Inkscape::NodePath::SubPath *sp = a->subpath;
1732         sp_nodepath_subpath_close(sp);
1733         sp_node_moveto (sp->first, c);
1735         sp_nodepath_update_handles(sp->nodepath);
1736         sp_nodepath_update_repr(nodepath);
1737         return;
1738     }
1740     /* a and b are separate subpaths */
1741    Inkscape::NodePath::SubPath *sa = a->subpath;
1742    Inkscape::NodePath::SubPath *sb = b->subpath;
1743     NR::Point p;
1744    Inkscape::NodePath::Node *n;
1745     NRPathcode code;
1746     if (a == sa->first) {
1747         p = sa->first->n.pos;
1748         code = (NRPathcode)sa->first->n.other->code;
1749        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1750         n = sa->last;
1751         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1752         n = n->p.other;
1753         while (n) {
1754             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1755             n = n->p.other;
1756             if (n == sa->first) n = NULL;
1757         }
1758         sp_nodepath_subpath_destroy(sa);
1759         sa = t;
1760     } else if (a == sa->last) {
1761         p = sa->last->p.pos;
1762         code = (NRPathcode)sa->last->code;
1763         sp_nodepath_node_destroy(sa->last);
1764     } else {
1765         code = NR_END;
1766         g_assert_not_reached();
1767     }
1769     if (b == sb->first) {
1770         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1771         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1772             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1773         }
1774     } else if (b == sb->last) {
1775         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1776         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1777             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1778         }
1779     } else {
1780         g_assert_not_reached();
1781     }
1782     /* and now destroy sb */
1784     sp_nodepath_subpath_destroy(sb);
1786     sp_nodepath_update_handles(sa->nodepath);
1788     sp_nodepath_update_repr(nodepath);
1790     sp_nodepath_update_statusbar(nodepath);
1793 /**
1794  *  Join two nodes by adding a segment between them.
1795  */
1796 void sp_node_selected_join_segment()
1798     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1799     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1801     if (g_list_length(nodepath->selected) != 2) {
1802         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1803         return;
1804     }
1806    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1807    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1809     g_assert(a != b);
1810     g_assert(a->p.other || a->n.other);
1811     g_assert(b->p.other || b->n.other);
1813     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1814         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1815         return;
1816     }
1818     if (a->subpath == b->subpath) {
1819        Inkscape::NodePath::SubPath *sp = a->subpath;
1821         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1822         sp->closed = TRUE;
1824         sp->first->p.other = sp->last;
1825         sp->last->n.other  = sp->first;
1827         sp_node_handle_mirror_p_to_n(sp->last);
1828         sp_node_handle_mirror_n_to_p(sp->first);
1830         sp->first->code = sp->last->code;
1831         sp->first       = sp->last;
1833         sp_nodepath_update_handles(sp->nodepath);
1835         sp_nodepath_update_repr(nodepath);
1837         return;
1838     }
1840     /* a and b are separate subpaths */
1841    Inkscape::NodePath::SubPath *sa = a->subpath;
1842    Inkscape::NodePath::SubPath *sb = b->subpath;
1844    Inkscape::NodePath::Node *n;
1845     NR::Point p;
1846     NRPathcode code;
1847     if (a == sa->first) {
1848         code = (NRPathcode) sa->first->n.other->code;
1849        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1850         n = sa->last;
1851         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1852         for (n = n->p.other; n != NULL; n = n->p.other) {
1853             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1854         }
1855         sp_nodepath_subpath_destroy(sa);
1856         sa = t;
1857     } else if (a == sa->last) {
1858         code = (NRPathcode)sa->last->code;
1859     } else {
1860         code = NR_END;
1861         g_assert_not_reached();
1862     }
1864     if (b == sb->first) {
1865         n = sb->first;
1866         sp_node_handle_mirror_p_to_n(sa->last);
1867         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1868         sp_node_handle_mirror_n_to_p(sa->last);
1869         for (n = n->n.other; n != NULL; n = n->n.other) {
1870             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1871         }
1872     } else if (b == sb->last) {
1873         n = sb->last;
1874         sp_node_handle_mirror_p_to_n(sa->last);
1875         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1876         sp_node_handle_mirror_n_to_p(sa->last);
1877         for (n = n->p.other; n != NULL; n = n->p.other) {
1878             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1879         }
1880     } else {
1881         g_assert_not_reached();
1882     }
1883     /* and now destroy sb */
1885     sp_nodepath_subpath_destroy(sb);
1887     sp_nodepath_update_handles(sa->nodepath);
1889     sp_nodepath_update_repr(nodepath);
1892 /**
1893  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1894  */
1895 void sp_node_delete_preserve(GList *nodes_to_delete)
1897     GSList *nodepaths = NULL;
1898     
1899     while (nodes_to_delete) {
1900         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1901         Inkscape::NodePath::SubPath *sp = node->subpath;
1902         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1903         Inkscape::NodePath::Node *sample_cursor = NULL;
1904         Inkscape::NodePath::Node *sample_end = NULL;
1905         Inkscape::NodePath::Node *delete_cursor = node;
1906         bool just_delete = false;
1907         
1908         //find the start of this contiguous selection
1909         //move left to the first node that is not selected
1910         //or the start of the non-closed path
1911         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1912             delete_cursor = curr;
1913         }
1915         //just delete at the beginning of an open path
1916         if (!delete_cursor->p.other) {
1917             sample_cursor = delete_cursor;
1918             just_delete = true;
1919         } else {
1920             sample_cursor = delete_cursor->p.other;
1921         }
1922         
1923         //calculate points for each segment
1924         int rate = 5;
1925         float period = 1.0 / rate;
1926         std::vector<NR::Point> data;
1927         if (!just_delete) {
1928             data.push_back(sample_cursor->pos);
1929             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1930                 //just delete at the end of an open path
1931                 if (!sp->closed && curr->n.other == sp->last) {
1932                     just_delete = true;
1933                     break;
1934                 }
1935                 
1936                 //sample points on the contiguous selected segment
1937                 NR::Point *bez;
1938                 bez = new NR::Point [4];
1939                 bez[0] = curr->pos;
1940                 bez[1] = curr->n.pos;
1941                 bez[2] = curr->n.other->p.pos;
1942                 bez[3] = curr->n.other->pos;
1943                 for (int i=1; i<rate; i++) {
1944                     gdouble t = i * period;
1945                     NR::Point p = bezier_pt(3, bez, t);
1946                     data.push_back(p);
1947                 }
1948                 data.push_back(curr->n.other->pos);
1950                 sample_end = curr->n.other;
1951                 //break if we've come full circle or hit the end of the selection
1952                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
1953                     break;
1954                 }
1955             }
1956         }
1958         if (!just_delete) {
1959             //calculate the best fitting single segment and adjust the endpoints
1960             NR::Point *adata;
1961             adata = new NR::Point [data.size()];
1962             copy(data.begin(), data.end(), adata);
1963             
1964             NR::Point *bez;
1965             bez = new NR::Point [4];
1966             //would decreasing error create a better fitting approximation?
1967             gdouble error = 1.0;
1968             gint ret;
1969             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
1971             //adjust endpoints
1972             sample_cursor->n.pos = bez[1];
1973             sample_end->p.pos = bez[2];
1974         }
1975        
1976         //destroy this contiguous selection
1977         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
1978             Inkscape::NodePath::Node *temp = delete_cursor;
1979             if (delete_cursor->n.other == delete_cursor) {
1980                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
1981                 delete_cursor = NULL; 
1982             } else {
1983                 delete_cursor = delete_cursor->n.other;
1984             }
1985             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
1986             sp_nodepath_node_destroy(temp);
1987         }
1989         sp_nodepath_update_handles(nodepath);
1991         if (!g_slist_find(nodepaths, nodepath))
1992             nodepaths = g_slist_prepend (nodepaths, nodepath);
1993     }
1995     for (GSList *i = nodepaths; i; i = i->next) {
1996         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
1997         // different nodepaths will give us one undo event per nodepath
1998         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2000         // if the entire nodepath is removed, delete the selected object.
2001         if (nodepath->subpaths == NULL ||
2002             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2003             //at least 2
2004             sp_nodepath_get_node_count(nodepath) < 2) {
2005             SPDocument *document = sp_desktop_document (nodepath->desktop);
2006             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2007             //delete this nodepath's object, not the entire selection! (though at this time, this
2008             //does not matter)
2009             sp_selection_delete();
2010             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2011                               /* TODO: annotate */ "nodepath.cpp:2011");
2012         } else {
2013             sp_nodepath_update_repr(nodepath);
2014             sp_nodepath_update_statusbar(nodepath);
2015         }
2016     }
2018     g_slist_free (nodepaths);
2021 /**
2022  * Delete one or more selected nodes.
2023  */
2024 void sp_node_selected_delete()
2026     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2027     if (!nodepath) return;
2028     if (!nodepath->selected) return;
2030     /** \todo fixme: do it the right way */
2031     while (nodepath->selected) {
2032        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2033         sp_nodepath_node_destroy(node);
2034     }
2037     //clean up the nodepath (such as for trivial subpaths)
2038     sp_nodepath_cleanup(nodepath);
2040     sp_nodepath_update_handles(nodepath);
2042     // if the entire nodepath is removed, delete the selected object.
2043     if (nodepath->subpaths == NULL ||
2044         sp_nodepath_get_node_count(nodepath) < 2) {
2045         SPDocument *document = sp_desktop_document (nodepath->desktop);
2046         sp_selection_delete();
2047         sp_document_done (document, SP_VERB_CONTEXT_NODE, 
2048                           /* TODO: annotate */ "nodepath.cpp:2048");
2049         return;
2050     }
2052     sp_nodepath_update_repr(nodepath);
2054     sp_nodepath_update_statusbar(nodepath);
2057 /**
2058  * Delete one or more segments between two selected nodes.
2059  * This is the code for 'split'.
2060  */
2061 void
2062 sp_node_selected_delete_segment(void)
2064    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2065    Inkscape::NodePath::Node *curr, *next;     //Iterators
2067     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2068     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2070     if (g_list_length(nodepath->selected) != 2) {
2071         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2072                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2073         return;
2074     }
2076     //Selected nodes, not inclusive
2077    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2078    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2080     if ( ( a==b)                       ||  //same node
2081          (a->subpath  != b->subpath )  ||  //not the same path
2082          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2083          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2084     {
2085         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2086                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2087         return;
2088     }
2090     //###########################################
2091     //# BEGIN EDITS
2092     //###########################################
2093     //##################################
2094     //# CLOSED PATH
2095     //##################################
2096     if (a->subpath->closed) {
2099         gboolean reversed = FALSE;
2101         //Since we can go in a circle, we need to find the shorter distance.
2102         //  a->b or b->a
2103         start = end = NULL;
2104         int distance    = 0;
2105         int minDistance = 0;
2106         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2107             if (curr==b) {
2108                 //printf("a to b:%d\n", distance);
2109                 start = a;//go from a to b
2110                 end   = b;
2111                 minDistance = distance;
2112                 //printf("A to B :\n");
2113                 break;
2114             }
2115             distance++;
2116         }
2118         //try again, the other direction
2119         distance = 0;
2120         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2121             if (curr==a) {
2122                 //printf("b to a:%d\n", distance);
2123                 if (distance < minDistance) {
2124                     start    = b;  //we go from b to a
2125                     end      = a;
2126                     reversed = TRUE;
2127                     //printf("B to A\n");
2128                 }
2129                 break;
2130             }
2131             distance++;
2132         }
2135         //Copy everything from 'end' to 'start' to a new subpath
2136        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2137         for (curr=end ; curr ; curr=curr->n.other) {
2138             NRPathcode code = (NRPathcode) curr->code;
2139             if (curr == end)
2140                 code = NR_MOVETO;
2141             sp_nodepath_node_new(t, NULL,
2142                                  (Inkscape::NodePath::NodeType)curr->type, code,
2143                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2144             if (curr == start)
2145                 break;
2146         }
2147         sp_nodepath_subpath_destroy(a->subpath);
2150     }
2154     //##################################
2155     //# OPEN PATH
2156     //##################################
2157     else {
2159         //We need to get the direction of the list between A and B
2160         //Can we walk from a to b?
2161         start = end = NULL;
2162         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2163             if (curr==b) {
2164                 start = a;  //did it!  we go from a to b
2165                 end   = b;
2166                 //printf("A to B\n");
2167                 break;
2168             }
2169         }
2170         if (!start) {//didn't work?  let's try the other direction
2171             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2172                 if (curr==a) {
2173                     start = b;  //did it!  we go from b to a
2174                     end   = a;
2175                     //printf("B to A\n");
2176                     break;
2177                 }
2178             }
2179         }
2180         if (!start) {
2181             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2182                                                      _("Cannot find path between nodes."));
2183             return;
2184         }
2188         //Copy everything after 'end' to a new subpath
2189        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2190         for (curr=end ; curr ; curr=curr->n.other) {
2191             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
2192                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2193         }
2195         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2196         for (curr = start->n.other ; curr  ; curr=next) {
2197             next = curr->n.other;
2198             sp_nodepath_node_destroy(curr);
2199         }
2201     }
2202     //###########################################
2203     //# END EDITS
2204     //###########################################
2206     //clean up the nodepath (such as for trivial subpaths)
2207     sp_nodepath_cleanup(nodepath);
2209     sp_nodepath_update_handles(nodepath);
2211     sp_nodepath_update_repr(nodepath);
2213     sp_nodepath_update_statusbar(nodepath);
2216 /**
2217  * Call sp_nodepath_set_line() for all selected segments.
2218  */
2219 void
2220 sp_node_selected_set_line_type(NRPathcode code)
2222     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2223     if (nodepath == NULL) return;
2225     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2226        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2227         g_assert(n->selected);
2228         if (n->p.other && n->p.other->selected) {
2229             sp_nodepath_set_line_type(n, code);
2230         }
2231     }
2233     sp_nodepath_update_repr(nodepath);
2236 /**
2237  * Call sp_nodepath_convert_node_type() for all selected nodes.
2238  */
2239 void
2240 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
2242     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2243     if (nodepath == NULL) return;
2245     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2246         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2247     }
2249     sp_nodepath_update_repr(nodepath);
2252 /**
2253  * Change select status of node, update its own and neighbour handles.
2254  */
2255 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2257     node->selected = selected;
2259     if (selected) {
2260         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2261         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2262         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2263         sp_knot_update_ctrl(node->knot);
2264     } else {
2265         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2266         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2267         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2268         sp_knot_update_ctrl(node->knot);
2269     }
2271     sp_node_update_handles(node);
2272     if (node->n.other) sp_node_update_handles(node->n.other);
2273     if (node->p.other) sp_node_update_handles(node->p.other);
2276 /**
2277 \brief Select a node
2278 \param node     The node to select
2279 \param incremental   If true, add to selection, otherwise deselect others
2280 \param override   If true, always select this node, otherwise toggle selected status
2281 */
2282 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2284     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2286     if (incremental) {
2287         if (override) {
2288             if (!g_list_find(nodepath->selected, node)) {
2289                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2290             }
2291             sp_node_set_selected(node, TRUE);
2292         } else { // toggle
2293             if (node->selected) {
2294                 g_assert(g_list_find(nodepath->selected, node));
2295                 nodepath->selected = g_list_remove(nodepath->selected, node);
2296             } else {
2297                 g_assert(!g_list_find(nodepath->selected, node));
2298                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2299             }
2300             sp_node_set_selected(node, !node->selected);
2301         }
2302     } else {
2303         sp_nodepath_deselect(nodepath);
2304         nodepath->selected = g_list_prepend(nodepath->selected, node);
2305         sp_node_set_selected(node, TRUE);
2306     }
2308     sp_nodepath_update_statusbar(nodepath);
2312 /**
2313 \brief Deselect all nodes in the nodepath
2314 */
2315 void
2316 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2318     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2320     while (nodepath->selected) {
2321         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2322         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2323     }
2324     sp_nodepath_update_statusbar(nodepath);
2327 /**
2328 \brief Select or invert selection of all nodes in the nodepath
2329 */
2330 void
2331 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2333     if (!nodepath) return;
2335     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2336        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2337         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2338            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2339            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2340         }
2341     }
2344 /**
2345  * If nothing selected, does the same as sp_nodepath_select_all();
2346  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2347  * (i.e., similar to "select all in layer", with the "selected" subpaths
2348  * being treated as "layers" in the path).
2349  */
2350 void
2351 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2353     if (!nodepath) return;
2355     if (g_list_length (nodepath->selected) == 0) {
2356         sp_nodepath_select_all (nodepath, invert);
2357         return;
2358     }
2360     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2361     GSList *subpaths = NULL;
2363     for (GList *l = copy; l != NULL; l = l->next) {
2364         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2365         Inkscape::NodePath::SubPath *subpath = n->subpath;
2366         if (!g_slist_find (subpaths, subpath))
2367             subpaths = g_slist_prepend (subpaths, subpath);
2368     }
2370     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2371         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2372         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2373             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2374             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2375         }
2376     }
2378     g_slist_free (subpaths);
2379     g_list_free (copy);
2382 /**
2383  * \brief Select the node after the last selected; if none is selected,
2384  * select the first within path.
2385  */
2386 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2388     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2390    Inkscape::NodePath::Node *last = NULL;
2391     if (nodepath->selected) {
2392         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2393            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2394             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2395             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2396                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2397                 if (node->selected) {
2398                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2399                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2400                             if (spl->next) { // there's a next subpath
2401                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2402                                 last = subpath_next->first;
2403                             } else if (spl->prev) { // there's a previous subpath
2404                                 last = NULL; // to be set later to the first node of first subpath
2405                             } else {
2406                                 last = node->n.other;
2407                             }
2408                         } else {
2409                             last = node->n.other;
2410                         }
2411                     } else {
2412                         if (node->n.other) {
2413                             last = node->n.other;
2414                         } else {
2415                             if (spl->next) { // there's a next subpath
2416                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2417                                 last = subpath_next->first;
2418                             } else if (spl->prev) { // there's a previous subpath
2419                                 last = NULL; // to be set later to the first node of first subpath
2420                             } else {
2421                                 last = (Inkscape::NodePath::Node *) subpath->first;
2422                             }
2423                         }
2424                     }
2425                 }
2426             }
2427         }
2428         sp_nodepath_deselect(nodepath);
2429     }
2431     if (last) { // there's at least one more node after selected
2432         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2433     } else { // no more nodes, select the first one in first subpath
2434        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2435         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2436     }
2439 /**
2440  * \brief Select the node before the first selected; if none is selected,
2441  * select the last within path
2442  */
2443 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2445     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2447    Inkscape::NodePath::Node *last = NULL;
2448     if (nodepath->selected) {
2449         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2450            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2451             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2452                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2453                 if (node->selected) {
2454                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2455                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2456                             if (spl->prev) { // there's a prev subpath
2457                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2458                                 last = subpath_prev->last;
2459                             } else if (spl->next) { // there's a next subpath
2460                                 last = NULL; // to be set later to the last node of last subpath
2461                             } else {
2462                                 last = node->p.other;
2463                             }
2464                         } else {
2465                             last = node->p.other;
2466                         }
2467                     } else {
2468                         if (node->p.other) {
2469                             last = node->p.other;
2470                         } else {
2471                             if (spl->prev) { // there's a prev subpath
2472                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2473                                 last = subpath_prev->last;
2474                             } else if (spl->next) { // there's a next subpath
2475                                 last = NULL; // to be set later to the last node of last subpath
2476                             } else {
2477                                 last = (Inkscape::NodePath::Node *) subpath->last;
2478                             }
2479                         }
2480                     }
2481                 }
2482             }
2483         }
2484         sp_nodepath_deselect(nodepath);
2485     }
2487     if (last) { // there's at least one more node before selected
2488         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2489     } else { // no more nodes, select the last one in last subpath
2490         GList *spl = g_list_last(nodepath->subpaths);
2491        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2492         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2493     }
2496 /**
2497  * \brief Select all nodes that are within the rectangle.
2498  */
2499 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2501     if (!incremental) {
2502         sp_nodepath_deselect(nodepath);
2503     }
2505     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2506        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2507         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2508            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2510             if (b.contains(node->pos)) {
2511                 sp_nodepath_node_select(node, TRUE, TRUE);
2512             }
2513         }
2514     }
2518 void
2519 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2521     g_assert (n);
2522     g_assert (nodepath);
2523     g_assert (n->subpath->nodepath == nodepath);
2525     if (g_list_length (nodepath->selected) == 0) {
2526         if (grow > 0) {
2527             sp_nodepath_node_select(n, TRUE, TRUE);
2528         }
2529         return;
2530     }
2532     if (g_list_length (nodepath->selected) == 1) {
2533         if (grow < 0) {
2534             sp_nodepath_deselect (nodepath);
2535             return;
2536         }
2537     }
2539         double n_sel_range = 0, p_sel_range = 0;
2540             Inkscape::NodePath::Node *farthest_n_node = n;
2541             Inkscape::NodePath::Node *farthest_p_node = n;
2543         // Calculate ranges
2544         {
2545             double n_range = 0, p_range = 0;
2546             bool n_going = true, p_going = true;
2547             Inkscape::NodePath::Node *n_node = n;
2548             Inkscape::NodePath::Node *p_node = n;
2549             do {
2550                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2551                 if (n_node && n_going)
2552                     n_node = n_node->n.other;
2553                 if (n_node == NULL) {
2554                     n_going = false;
2555                 } else {
2556                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2557                     if (n_node->selected) {
2558                         n_sel_range = n_range;
2559                         farthest_n_node = n_node;
2560                     }
2561                     if (n_node == p_node) {
2562                         n_going = false;
2563                         p_going = false;
2564                     }
2565                 }
2566                 if (p_node && p_going)
2567                     p_node = p_node->p.other;
2568                 if (p_node == NULL) {
2569                     p_going = false;
2570                 } else {
2571                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2572                     if (p_node->selected) {
2573                         p_sel_range = p_range;
2574                         farthest_p_node = p_node;
2575                     }
2576                     if (p_node == n_node) {
2577                         n_going = false;
2578                         p_going = false;
2579                     }
2580                 }
2581             } while (n_going || p_going);
2582         }
2584     if (grow > 0) {
2585         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2586                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2587         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2588                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2589         }
2590     } else {
2591         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2592                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2593         } else if (farthest_p_node && farthest_p_node->selected) {
2594                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2595         }
2596     }
2599 void
2600 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2602     g_assert (n);
2603     g_assert (nodepath);
2604     g_assert (n->subpath->nodepath == nodepath);
2606     if (g_list_length (nodepath->selected) == 0) {
2607         if (grow > 0) {
2608             sp_nodepath_node_select(n, TRUE, TRUE);
2609         }
2610         return;
2611     }
2613     if (g_list_length (nodepath->selected) == 1) {
2614         if (grow < 0) {
2615             sp_nodepath_deselect (nodepath);
2616             return;
2617         }
2618     }
2620     Inkscape::NodePath::Node *farthest_selected = NULL;
2621     double farthest_dist = 0;
2623     Inkscape::NodePath::Node *closest_unselected = NULL;
2624     double closest_dist = NR_HUGE;
2626     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2627        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2628         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2629            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2630            if (node == n)
2631                continue;
2632            if (node->selected) {
2633                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2634                    farthest_dist = NR::L2(node->pos - n->pos);
2635                    farthest_selected = node;
2636                }
2637            } else {
2638                if (NR::L2(node->pos - n->pos) < closest_dist) {
2639                    closest_dist = NR::L2(node->pos - n->pos);
2640                    closest_unselected = node;
2641                }
2642            }
2643         }
2644     }
2646     if (grow > 0) {
2647         if (closest_unselected) {
2648             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2649         }
2650     } else {
2651         if (farthest_selected) {
2652             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2653         }
2654     }
2658 /**
2659 \brief  Saves all nodes' and handles' current positions in their origin members
2660 */
2661 void
2662 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2664     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2665        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2666         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2667            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2668            n->origin = n->pos;
2669            n->p.origin = n->p.pos;
2670            n->n.origin = n->n.pos;
2671         }
2672     }
2673
2675 /**
2676 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2677 */
2678 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2680     if (!nodepath->selected) {
2681         return NULL;
2682     }
2684     GList *r = NULL;
2685     guint i = 0;
2686     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2687        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2688         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2689            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2690             i++;
2691             if (node->selected) {
2692                 r = g_list_append(r, GINT_TO_POINTER(i));
2693             }
2694         }
2695     }
2696     return r;
2699 /**
2700 \brief  Restores selection by selecting nodes whose positions are in the list
2701 */
2702 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2704     sp_nodepath_deselect(nodepath);
2706     guint i = 0;
2707     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2708        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2709         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2710            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2711             i++;
2712             if (g_list_find(r, GINT_TO_POINTER(i))) {
2713                 sp_nodepath_node_select(node, TRUE, TRUE);
2714             }
2715         }
2716     }
2720 /**
2721 \brief Adjusts handle according to node type and line code.
2722 */
2723 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2725     double len, otherlen, linelen;
2727     g_assert(node);
2729    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2730    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2732     /** \todo fixme: */
2733     if (me->other == NULL) return;
2734     if (other->other == NULL) return;
2736     /* I have line */
2738     NRPathcode mecode, ocode;
2739     if (which_adjust == 1) {
2740         mecode = (NRPathcode)me->other->code;
2741         ocode = (NRPathcode)node->code;
2742     } else {
2743         mecode = (NRPathcode)node->code;
2744         ocode = (NRPathcode)other->other->code;
2745     }
2747     if (mecode == NR_LINETO) return;
2749     /* I am curve */
2751     if (other->other == NULL) return;
2753     /* Other has line */
2755     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2757     NR::Point delta;
2758     if (ocode == NR_LINETO) {
2759         /* other is lineto, we are either smooth or symm */
2760        Inkscape::NodePath::Node *othernode = other->other;
2761         len = NR::L2(me->pos - node->pos);
2762         delta = node->pos - othernode->pos;
2763         linelen = NR::L2(delta);
2764         if (linelen < 1e-18) 
2765             return;
2766         me->pos = node->pos + (len / linelen)*delta;
2767         return;
2768     }
2770     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2772         me->pos = 2 * node->pos - other->pos;
2773         return;
2774     }
2776     /* We are smooth */
2778     len = NR::L2(me->pos - node->pos);
2779     delta = other->pos - node->pos;
2780     otherlen = NR::L2(delta);
2781     if (otherlen < 1e-18) return;
2783     me->pos = node->pos - (len / otherlen) * delta;
2786 /**
2787  \brief Adjusts both handles according to node type and line code
2788  */
2789 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2791     g_assert(node);
2793     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2795     /* we are either smooth or symm */
2797     if (node->p.other == NULL) return;
2799     if (node->n.other == NULL) return;
2801     if (node->code == NR_LINETO) {
2802         if (node->n.other->code == NR_LINETO) return;
2803         sp_node_adjust_handle(node, 1);
2804         return;
2805     }
2807     if (node->n.other->code == NR_LINETO) {
2808         if (node->code == NR_LINETO) return;
2809         sp_node_adjust_handle(node, -1);
2810         return;
2811     }
2813     /* both are curves */
2814     NR::Point const delta( node->n.pos - node->p.pos );
2816     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2817         node->p.pos = node->pos - delta / 2;
2818         node->n.pos = node->pos + delta / 2;
2819         return;
2820     }
2822     /* We are smooth */
2823     double plen = NR::L2(node->p.pos - node->pos);
2824     if (plen < 1e-18) return;
2825     double nlen = NR::L2(node->n.pos - node->pos);
2826     if (nlen < 1e-18) return;
2827     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2828     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2831 /**
2832  * Node event callback.
2833  */
2834 static gboolean node_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n)
2836     gboolean ret = FALSE;
2837     switch (event->type) {
2838         case GDK_ENTER_NOTIFY:
2839             active_node = n;
2840             break;
2841         case GDK_LEAVE_NOTIFY:
2842             active_node = NULL;
2843             break;
2844         case GDK_KEY_PRESS:
2845             switch (get_group0_keyval (&event->key)) {
2846                 case GDK_space:
2847                     if (event->key.state & GDK_BUTTON1_MASK) {
2848                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2849                         stamp_repr(nodepath);
2850                         ret = TRUE;
2851                     }
2852                     break;
2853                 case GDK_Page_Up:
2854                     if (event->key.state & GDK_CONTROL_MASK) {
2855                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2856                     } else {
2857                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2858                     }
2859                     break;
2860                 case GDK_Page_Down:
2861                     if (event->key.state & GDK_CONTROL_MASK) {
2862                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2863                     } else {
2864                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2865                     }
2866                     break;
2867                 default:
2868                     break;
2869             }
2870             break;
2871         default:
2872             break;
2873     }
2875     return ret;
2878 /**
2879  * Handle keypress on node; directly called.
2880  */
2881 gboolean node_key(GdkEvent *event)
2883     Inkscape::NodePath::Path *np;
2885     // there is no way to verify nodes so set active_node to nil when deleting!!
2886     if (active_node == NULL) return FALSE;
2888     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2889         gint ret = FALSE;
2890         switch (get_group0_keyval (&event->key)) {
2891             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2892             case GDK_BackSpace:
2893                 np = active_node->subpath->nodepath;
2894                 sp_nodepath_node_destroy(active_node);
2895                 sp_nodepath_update_repr(np);
2896                 active_node = NULL;
2897                 ret = TRUE;
2898                 break;
2899             case GDK_c:
2900                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2901                 ret = TRUE;
2902                 break;
2903             case GDK_s:
2904                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2905                 ret = TRUE;
2906                 break;
2907             case GDK_y:
2908                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2909                 ret = TRUE;
2910                 break;
2911             case GDK_b:
2912                 sp_nodepath_node_break(active_node);
2913                 ret = TRUE;
2914                 break;
2915         }
2916         return ret;
2917     }
2918     return FALSE;
2921 /**
2922  * Mouseclick on node callback.
2923  */
2924 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2926    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2928     if (state & GDK_CONTROL_MASK) {
2929         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2931         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2932             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2933                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2934             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2935                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2936             } else {
2937                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2938             }
2939             sp_nodepath_update_repr(nodepath);
2940             sp_nodepath_update_statusbar(nodepath);
2942         } else { //ctrl+alt+click: delete node
2943             GList *node_to_delete = NULL;
2944             node_to_delete = g_list_append(node_to_delete, n);
2945             sp_node_delete_preserve(node_to_delete);
2946         }
2948     } else {
2949         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2950     }
2953 /**
2954  * Mouse grabbed node callback.
2955  */
2956 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2958    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2960     if (!n->selected) {
2961         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2962     }
2964     sp_nodepath_remember_origins (n->subpath->nodepath);
2967 /**
2968  * Mouse ungrabbed node callback.
2969  */
2970 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
2972    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2974    n->dragging_out = NULL;
2976    sp_nodepath_update_repr(n->subpath->nodepath);
2979 /**
2980  * The point on a line, given by its angle, closest to the given point.
2981  * \param p  A point.
2982  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2983  * \param closest  Pointer to the point struct where the result is stored.
2984  * \todo FIXME: use dot product perhaps?
2985  */
2986 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
2988     if (a == HUGE_VAL) { // vertical
2989         *closest = NR::Point(0, (*p)[NR::Y]);
2990     } else {
2991         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
2992         (*closest)[NR::Y] = a * (*closest)[NR::X];
2993     }
2996 /**
2997  * Distance from the point to a line given by its angle.
2998  * \param p  A point.
2999  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3000  */
3001 static double point_line_distance(NR::Point *p, double a)
3003     NR::Point c;
3004     point_line_closest(p, a, &c);
3005     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]));
3008 /**
3009  * Callback for node "request" signal.
3010  * \todo fixme: This goes to "moved" event? (lauris)
3011  */
3012 static gboolean
3013 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3015     double yn, xn, yp, xp;
3016     double an, ap, na, pa;
3017     double d_an, d_ap, d_na, d_pa;
3018     gboolean collinear = FALSE;
3019     NR::Point c;
3020     NR::Point pr;
3022    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3024    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3025    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
3027        NR::Point mouse = (*p);
3029        if (!n->dragging_out) {
3030            // This is the first drag-out event; find out which handle to drag out
3031            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3032            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3034            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3035                return FALSE;
3037            Inkscape::NodePath::NodeSide *opposite;
3038            if (appr_p > appr_n) { // closer to p
3039                n->dragging_out = &n->p;
3040                opposite = &n->n;
3041                n->code = NR_CURVETO;
3042            } else if (appr_p < appr_n) { // closer to n
3043                n->dragging_out = &n->n;
3044                opposite = &n->p;
3045                n->n.other->code = NR_CURVETO;
3046            } else { // p and n nodes are the same
3047                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3048                    n->dragging_out = &n->p;
3049                    opposite = &n->n;
3050                    n->code = NR_CURVETO;
3051                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3052                    n->dragging_out = &n->n;
3053                    opposite = &n->p;
3054                    n->n.other->code = NR_CURVETO;
3055                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3056                    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);
3057                    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);
3058                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3059                        n->dragging_out = &n->n;
3060                        opposite = &n->p;
3061                        n->n.other->code = NR_CURVETO;
3062                    } else { // closer to other's n handle
3063                        n->dragging_out = &n->p;
3064                        opposite = &n->n;
3065                        n->code = NR_CURVETO;
3066                    }
3067                }
3068            }
3070            // if there's another handle, make sure the one we drag out starts parallel to it
3071            if (opposite->pos != n->pos) {
3072                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3073            }
3075            // knots might not be created yet!
3076            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3077            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3078        }
3080        // pass this on to the handle-moved callback
3081        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3082        sp_node_update_handles(n);
3083        return TRUE;
3084    }
3086     if (state & GDK_CONTROL_MASK) { // constrained motion
3088         // calculate relative distances of handles
3089         // n handle:
3090         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3091         xn = n->n.pos[NR::X] - n->pos[NR::X];
3092         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3093         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3094             if (n->n.other) { // if there is the next point
3095                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3096                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3097                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3098             }
3099         }
3100         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3101         if (yn < 0) { xn = -xn; yn = -yn; }
3103         // p handle:
3104         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3105         xp = n->p.pos[NR::X] - n->pos[NR::X];
3106         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3107         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3108             if (n->p.other) {
3109                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3110                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3111                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3112             }
3113         }
3114         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3115         if (yp < 0) { xp = -xp; yp = -yp; }
3117         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3118             // sliding on handles, only if at least one of the handles is non-vertical
3119             // (otherwise it's the same as ctrl+drag anyway)
3121             // calculate angles of the handles
3122             if (xn == 0) {
3123                 if (yn == 0) { // no handle, consider it the continuation of the other one
3124                     an = 0;
3125                     collinear = TRUE;
3126                 }
3127                 else an = 0; // vertical; set the angle to horizontal
3128             } else an = yn/xn;
3130             if (xp == 0) {
3131                 if (yp == 0) { // no handle, consider it the continuation of the other one
3132                     ap = an;
3133                 }
3134                 else ap = 0; // vertical; set the angle to horizontal
3135             } else  ap = yp/xp;
3137             if (collinear) an = ap;
3139             // angles of the perpendiculars; HUGE_VAL means vertical
3140             if (an == 0) na = HUGE_VAL; else na = -1/an;
3141             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3143             // mouse point relative to the node's original pos
3144             pr = (*p) - n->origin;
3146             // distances to the four lines (two handles and two perpendiculars)
3147             d_an = point_line_distance(&pr, an);
3148             d_na = point_line_distance(&pr, na);
3149             d_ap = point_line_distance(&pr, ap);
3150             d_pa = point_line_distance(&pr, pa);
3152             // find out which line is the closest, save its closest point in c
3153             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3154                 point_line_closest(&pr, an, &c);
3155             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3156                 point_line_closest(&pr, ap, &c);
3157             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3158                 point_line_closest(&pr, na, &c);
3159             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3160                 point_line_closest(&pr, pa, &c);
3161             }
3163             // move the node to the closest point
3164             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3165                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3166                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3168         } else {  // constraining to hor/vert
3170             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3171                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3172             } else { // snap to vert
3173                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3174             }
3175         }
3176     } else { // move freely
3177         if (state & GDK_MOD1_MASK) { // sculpt
3178             sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3179         } else {
3180             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3181                                         (*p)[NR::X] - n->pos[NR::X],
3182                                         (*p)[NR::Y] - n->pos[NR::Y],
3183                                         (state & GDK_SHIFT_MASK) == 0);
3184         }
3185     }
3187     n->subpath->nodepath->desktop->scroll_to_point(p);
3189     return TRUE;
3192 /**
3193  * Node handle clicked callback.
3194  */
3195 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3197    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3199     if (state & GDK_CONTROL_MASK) { // "delete" handle
3200         if (n->p.knot == knot) {
3201             n->p.pos = n->pos;
3202         } else if (n->n.knot == knot) {
3203             n->n.pos = n->pos;
3204         }
3205         sp_node_update_handles(n);
3206         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3207         sp_nodepath_update_repr(nodepath);
3208         sp_nodepath_update_statusbar(nodepath);
3210     } else { // just select or add to selection, depending in Shift
3211         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3212     }
3215 /**
3216  * Node handle grabbed callback.
3217  */
3218 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3220    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3222     if (!n->selected) {
3223         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3224     }
3226     // remember the origin point of the handle
3227     if (n->p.knot == knot) {
3228         n->p.origin_radial = n->p.pos - n->pos;
3229     } else if (n->n.knot == knot) {
3230         n->n.origin_radial = n->n.pos - n->pos;
3231     } else {
3232         g_assert_not_reached();
3233     }
3237 /**
3238  * Node handle ungrabbed callback.
3239  */
3240 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3242    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3244     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3245     if (n->p.knot == knot) {
3246         n->p.origin_radial.a = 0;
3247         sp_knot_set_position(knot, &n->p.pos, state);
3248     } else if (n->n.knot == knot) {
3249         n->n.origin_radial.a = 0;
3250         sp_knot_set_position(knot, &n->n.pos, state);
3251     } else {
3252         g_assert_not_reached();
3253     }
3255     sp_nodepath_update_repr(n->subpath->nodepath);
3258 /**
3259  * Node handle "request" signal callback.
3260  */
3261 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3263     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3265     Inkscape::NodePath::NodeSide *me, *opposite;
3266     gint which;
3267     if (n->p.knot == knot) {
3268         me = &n->p;
3269         opposite = &n->n;
3270         which = -1;
3271     } else if (n->n.knot == knot) {
3272         me = &n->n;
3273         opposite = &n->p;
3274         which = 1;
3275     } else {
3276         me = opposite = NULL;
3277         which = 0;
3278         g_assert_not_reached();
3279     }
3281     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3283     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3285     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3286         /* We are smooth node adjacent with line */
3287         NR::Point const delta = *p - n->pos;
3288         NR::Coord const len = NR::L2(delta);
3289         Inkscape::NodePath::Node *othernode = opposite->other;
3290         NR::Point const ndelta = n->pos - othernode->pos;
3291         NR::Coord const linelen = NR::L2(ndelta);
3292         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3293             NR::Coord const scal = dot(delta, ndelta) / linelen;
3294             (*p) = n->pos + (scal / linelen) * ndelta;
3295         }
3296         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
3297     } else {
3298         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
3299     }
3301     sp_node_adjust_handle(n, -which);
3303     return FALSE;
3306 /**
3307  * Node handle moved callback.
3308  */
3309 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3311    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3313    Inkscape::NodePath::NodeSide *me;
3314    Inkscape::NodePath::NodeSide *other;
3315     if (n->p.knot == knot) {
3316         me = &n->p;
3317         other = &n->n;
3318     } else if (n->n.knot == knot) {
3319         me = &n->n;
3320         other = &n->p;
3321     } else {
3322         me = NULL;
3323         other = NULL;
3324         g_assert_not_reached();
3325     }
3327     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3328     Radial rme(me->pos - n->pos);
3329     Radial rother(other->pos - n->pos);
3330     Radial rnew(*p - n->pos);
3332     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3333         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3334         /* 0 interpreted as "no snapping". */
3336         // The closest PI/snaps angle, starting from zero.
3337         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3338         if (me->origin_radial.a == HUGE_VAL) {
3339             // ortho doesn't exist: original handle was zero length.
3340             rnew.a = a_snapped;
3341         } else {
3342             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3343              * its opposite and perpendiculars). */
3344             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3346             // Snap to the closest.
3347             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3348                        ? a_snapped
3349                        : a_ortho );
3350         }
3351     }
3353     if (state & GDK_MOD1_MASK) {
3354         // lock handle length
3355         rnew.r = me->origin_radial.r;
3356     }
3358     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3359         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
3360         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3361         rother.a += rnew.a - rme.a;
3362         other->pos = NR::Point(rother) + n->pos;
3363         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3364         sp_knot_set_position(other->knot, &other->pos, 0);
3365     }
3367     me->pos = NR::Point(rnew) + n->pos;
3368     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3370     // this is what sp_knot_set_position does, but without emitting the signal:
3371     // we cannot emit a "moved" signal because we're now processing it
3372     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
3374     knot->desktop->set_coordinate_status(me->pos);
3376     update_object(n->subpath->nodepath);
3378     /* status text */
3379     SPDesktop *desktop = n->subpath->nodepath->desktop;
3380     if (!desktop) return;
3381     SPEventContext *ec = desktop->event_context;
3382     if (!ec) return;
3383     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3384     if (!mc) return;
3386     double degrees = 180 / M_PI * rnew.a;
3387     if (degrees > 180) degrees -= 360;
3388     if (degrees < -180) degrees += 360;
3389     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3390         degrees = angle_to_compass (degrees);
3392     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3394     mc->setF(Inkscape::NORMAL_MESSAGE,
3395          _("<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);
3397     g_string_free(length, TRUE);
3400 /**
3401  * Node handle event callback.
3402  */
3403 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3405     gboolean ret = FALSE;
3406     switch (event->type) {
3407         case GDK_KEY_PRESS:
3408             switch (get_group0_keyval (&event->key)) {
3409                 case GDK_space:
3410                     if (event->key.state & GDK_BUTTON1_MASK) {
3411                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3412                         stamp_repr(nodepath);
3413                         ret = TRUE;
3414                     }
3415                     break;
3416                 default:
3417                     break;
3418             }
3419             break;
3420         default:
3421             break;
3422     }
3424     return ret;
3427 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3428                                  Radial &rme, Radial &rother, gboolean const both)
3430     rme.a += angle;
3431     if ( both
3432          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3433          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3434     {
3435         rother.a += angle;
3436     }
3439 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3440                                         Radial &rme, Radial &rother, gboolean const both)
3442     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3444     gdouble r;
3445     if ( both
3446          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3447          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3448     {
3449         r = MAX(rme.r, rother.r);
3450     } else {
3451         r = rme.r;
3452     }
3454     gdouble const weird_angle = atan2(norm_angle, r);
3455 /* Bulia says norm_angle is just the visible distance that the
3456  * object's end must travel on the screen.  Left as 'angle' for want of
3457  * a better name.*/
3459     rme.a += weird_angle;
3460     if ( both
3461          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3462          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3463     {
3464         rother.a += weird_angle;
3465     }
3468 /**
3469  * Rotate one node.
3470  */
3471 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3473     Inkscape::NodePath::NodeSide *me, *other;
3474     bool both = false;
3476     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3477     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3479     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3480         me = &(n->p);
3481         other = &(n->n);
3482     } else if (!n->p.other) {
3483         me = &(n->n);
3484         other = &(n->p);
3485     } else {
3486         if (which > 0) { // right handle
3487             if (xn > xp) {
3488                 me = &(n->n);
3489                 other = &(n->p);
3490             } else {
3491                 me = &(n->p);
3492                 other = &(n->n);
3493             }
3494         } else if (which < 0){ // left handle
3495             if (xn <= xp) {
3496                 me = &(n->n);
3497                 other = &(n->p);
3498             } else {
3499                 me = &(n->p);
3500                 other = &(n->n);
3501             }
3502         } else { // both handles
3503             me = &(n->n);
3504             other = &(n->p);
3505             both = true;
3506         }
3507     }
3509     Radial rme(me->pos - n->pos);
3510     Radial rother(other->pos - n->pos);
3512     if (screen) {
3513         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3514     } else {
3515         node_rotate_one_internal (*n, angle, rme, rother, both);
3516     }
3518     me->pos = n->pos + NR::Point(rme);
3520     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3521         other->pos =  n->pos + NR::Point(rother);
3522     }
3524     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3525     // so here we just move all the knots without emitting move signals, for speed
3526     sp_node_update_handles(n, false);
3529 /**
3530  * Rotate selected nodes.
3531  */
3532 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3534     if (!nodepath || !nodepath->selected) return;
3536     if (g_list_length(nodepath->selected) == 1) {
3537        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3538         node_rotate_one (n, angle, which, screen);
3539     } else {
3540        // rotate as an object:
3542         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3543         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3544         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3545             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3546             box.expandTo (n->pos); // contain all selected nodes
3547         }
3549         gdouble rot;
3550         if (screen) {
3551             gdouble const zoom = nodepath->desktop->current_zoom();
3552             gdouble const zmove = angle / zoom;
3553             gdouble const r = NR::L2(box.max() - box.midpoint());
3554             rot = atan2(zmove, r);
3555         } else {
3556             rot = angle;
3557         }
3559         NR::Matrix t =
3560             NR::Matrix (NR::translate(-box.midpoint())) *
3561             NR::Matrix (NR::rotate(rot)) *
3562             NR::Matrix (NR::translate(box.midpoint()));
3564         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3565             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3566             n->pos *= t;
3567             n->n.pos *= t;
3568             n->p.pos *= t;
3569             sp_node_update_handles(n, false);
3570         }
3571     }
3573     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n");
3576 /**
3577  * Scale one node.
3578  */
3579 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3581     bool both = false;
3582     Inkscape::NodePath::NodeSide *me, *other;
3584     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3585     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3587     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3588         me = &(n->p);
3589         other = &(n->n);
3590         n->code = NR_CURVETO;
3591     } else if (!n->p.other) {
3592         me = &(n->n);
3593         other = &(n->p);
3594         if (n->n.other)
3595             n->n.other->code = NR_CURVETO;
3596     } else {
3597         if (which > 0) { // right handle
3598             if (xn > xp) {
3599                 me = &(n->n);
3600                 other = &(n->p);
3601                 if (n->n.other)
3602                     n->n.other->code = NR_CURVETO;
3603             } else {
3604                 me = &(n->p);
3605                 other = &(n->n);
3606                 n->code = NR_CURVETO;
3607             }
3608         } else if (which < 0){ // left handle
3609             if (xn <= xp) {
3610                 me = &(n->n);
3611                 other = &(n->p);
3612                 if (n->n.other)
3613                     n->n.other->code = NR_CURVETO;
3614             } else {
3615                 me = &(n->p);
3616                 other = &(n->n);
3617                 n->code = NR_CURVETO;
3618             }
3619         } else { // both handles
3620             me = &(n->n);
3621             other = &(n->p);
3622             both = true;
3623             n->code = NR_CURVETO;
3624             if (n->n.other)
3625                 n->n.other->code = NR_CURVETO;
3626         }
3627     }
3629     Radial rme(me->pos - n->pos);
3630     Radial rother(other->pos - n->pos);
3632     rme.r += grow;
3633     if (rme.r < 0) rme.r = 0;
3634     if (rme.a == HUGE_VAL) {
3635         if (me->other) { // if direction is unknown, initialize it towards the next node
3636             Radial rme_next(me->other->pos - n->pos);
3637             rme.a = rme_next.a;
3638         } else { // if there's no next, initialize to 0
3639             rme.a = 0;
3640         }
3641     }
3642     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3643         rother.r += grow;
3644         if (rother.r < 0) rother.r = 0;
3645         if (rother.a == HUGE_VAL) {
3646             rother.a = rme.a + M_PI;
3647         }
3648     }
3650     me->pos = n->pos + NR::Point(rme);
3652     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3653         other->pos = n->pos + NR::Point(rother);
3654     }
3656     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3657     // so here we just move all the knots without emitting move signals, for speed
3658     sp_node_update_handles(n, false);
3661 /**
3662  * Scale selected nodes.
3663  */
3664 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3666     if (!nodepath || !nodepath->selected) return;
3668     if (g_list_length(nodepath->selected) == 1) {
3669         // scale handles of the single selected node
3670         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3671         node_scale_one (n, grow, which);
3672     } else {
3673         // scale nodes as an "object":
3675         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3676         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3677         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3678             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3679             box.expandTo (n->pos); // contain all selected nodes
3680         }
3682         double scale = (box.maxExtent() + grow)/box.maxExtent();
3684         NR::Matrix t =
3685             NR::Matrix (NR::translate(-box.midpoint())) *
3686             NR::Matrix (NR::scale(scale, scale)) *
3687             NR::Matrix (NR::translate(box.midpoint()));
3689         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3690             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3691             n->pos *= t;
3692             n->n.pos *= t;
3693             n->p.pos *= t;
3694             sp_node_update_handles(n, false);
3695         }
3696     }
3698     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n");
3701 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3703     if (!nodepath) return;
3704     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3707 /**
3708  * Flip selected nodes horizontally/vertically.
3709  */
3710 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3712     if (!nodepath || !nodepath->selected) return;
3714     if (g_list_length(nodepath->selected) == 1) {
3715         // flip handles of the single selected node
3716         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3717         double temp = n->p.pos[axis];
3718         n->p.pos[axis] = n->n.pos[axis];
3719         n->n.pos[axis] = temp;
3720         sp_node_update_handles(n, false);
3721     } else {
3722         // scale nodes as an "object":
3724         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3725         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3726         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3727             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3728             box.expandTo (n->pos); // contain all selected nodes
3729         }
3731         NR::Matrix t =
3732             NR::Matrix (NR::translate(-box.midpoint())) *
3733             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3734             NR::Matrix (NR::translate(box.midpoint()));
3736         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3737             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3738             n->pos *= t;
3739             n->n.pos *= t;
3740             n->p.pos *= t;
3741             sp_node_update_handles(n, false);
3742         }
3743     }
3745     sp_nodepath_update_repr(nodepath);
3748 //-----------------------------------------------
3749 /**
3750  * Return new subpath under given nodepath.
3751  */
3752 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3754     g_assert(nodepath);
3755     g_assert(nodepath->desktop);
3757    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3759     s->nodepath = nodepath;
3760     s->closed = FALSE;
3761     s->nodes = NULL;
3762     s->first = NULL;
3763     s->last = NULL;
3765     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3766     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3767     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3769     return s;
3772 /**
3773  * Destroy nodes in subpath, then subpath itself.
3774  */
3775 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3777     g_assert(subpath);
3778     g_assert(subpath->nodepath);
3779     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3781     while (subpath->nodes) {
3782         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3783     }
3785     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3787     g_free(subpath);
3790 /**
3791  * Link head to tail in subpath.
3792  */
3793 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3795     g_assert(!sp->closed);
3796     g_assert(sp->last != sp->first);
3797     g_assert(sp->first->code == NR_MOVETO);
3799     sp->closed = TRUE;
3801     //Link the head to the tail
3802     sp->first->p.other = sp->last;
3803     sp->last->n.other  = sp->first;
3804     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3805     sp->first          = sp->last;
3807     //Remove the extra end node
3808     sp_nodepath_node_destroy(sp->last->n.other);
3811 /**
3812  * Open closed (loopy) subpath at node.
3813  */
3814 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3816     g_assert(sp->closed);
3817     g_assert(n->subpath == sp);
3818     g_assert(sp->first == sp->last);
3820     /* We create new startpoint, current node will become last one */
3822    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3823                                                 &n->pos, &n->pos, &n->n.pos);
3826     sp->closed        = FALSE;
3828     //Unlink to make a head and tail
3829     sp->first         = new_path;
3830     sp->last          = n;
3831     n->n.other        = NULL;
3832     new_path->p.other = NULL;
3835 /**
3836  * Returns area in triangle given by points; may be negative.
3837  */
3838 inline double
3839 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3841     return (p1[NR::X]*p2[NR::Y] + p1[NR::Y]*p3[NR::X] + p2[NR::X]*p3[NR::Y] - p2[NR::Y]*p3[NR::X] - p1[NR::Y]*p2[NR::X] - p1[NR::X]*p3[NR::Y]);
3844 /**
3845  * Return new node in subpath with given properties.
3846  * \param pos Position of node.
3847  * \param ppos Handle position in previous direction
3848  * \param npos Handle position in previous direction
3849  */
3850 Inkscape::NodePath::Node *
3851 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)
3853     g_assert(sp);
3854     g_assert(sp->nodepath);
3855     g_assert(sp->nodepath->desktop);
3857     if (nodechunk == NULL)
3858         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3860     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3862     n->subpath  = sp;
3864     if (type != Inkscape::NodePath::NODE_NONE) {
3865         // use the type from sodipodi:nodetypes
3866         n->type = type;
3867     } else {
3868         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3869             // points are (almost) collinear
3870             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3871                 // endnode, or a node with a retracted handle
3872                 n->type = Inkscape::NodePath::NODE_CUSP;
3873             } else {
3874                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3875             }
3876         } else {
3877             n->type = Inkscape::NodePath::NODE_CUSP;
3878         }
3879     }
3881     n->code     = code;
3882     n->selected = FALSE;
3883     n->pos      = *pos;
3884     n->p.pos    = *ppos;
3885     n->n.pos    = *npos;
3887     n->dragging_out = NULL;
3889     Inkscape::NodePath::Node *prev;
3890     if (next) {
3891         //g_assert(g_list_find(sp->nodes, next));
3892         prev = next->p.other;
3893     } else {
3894         prev = sp->last;
3895     }
3897     if (prev)
3898         prev->n.other = n;
3899     else
3900         sp->first = n;
3902     if (next)
3903         next->p.other = n;
3904     else
3905         sp->last = n;
3907     n->p.other = prev;
3908     n->n.other = next;
3910     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"));
3911     sp_knot_set_position(n->knot, pos, 0);
3913     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3914     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3915     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3916     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3917     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3918     sp_knot_update_ctrl(n->knot);
3920     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3921     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3922     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3923     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3924     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3925     sp_knot_show(n->knot);
3927     // We only create handle knots and lines on demand
3928     n->p.knot = NULL;
3929     n->p.line = NULL;
3930     n->n.knot = NULL;
3931     n->n.line = NULL;
3933     sp->nodes = g_list_prepend(sp->nodes, n);
3935     return n;
3938 /**
3939  * Destroy node and its knots, link neighbors in subpath.
3940  */
3941 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3943     g_assert(node);
3944     g_assert(node->subpath);
3945     g_assert(SP_IS_KNOT(node->knot));
3947    Inkscape::NodePath::SubPath *sp = node->subpath;
3949     if (node->selected) { // first, deselect
3950         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3951         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3952     }
3954     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3956     g_object_unref(G_OBJECT(node->knot));
3957     if (node->p.knot)
3958         g_object_unref(G_OBJECT(node->p.knot));
3959     if (node->n.knot)
3960         g_object_unref(G_OBJECT(node->n.knot));
3962     if (node->p.line)
3963         gtk_object_destroy(GTK_OBJECT(node->p.line));
3964     if (node->n.line)
3965         gtk_object_destroy(GTK_OBJECT(node->n.line));
3967     if (sp->nodes) { // there are others nodes on the subpath
3968         if (sp->closed) {
3969             if (sp->first == node) {
3970                 g_assert(sp->last == node);
3971                 sp->first = node->n.other;
3972                 sp->last = sp->first;
3973             }
3974             node->p.other->n.other = node->n.other;
3975             node->n.other->p.other = node->p.other;
3976         } else {
3977             if (sp->first == node) {
3978                 sp->first = node->n.other;
3979                 sp->first->code = NR_MOVETO;
3980             }
3981             if (sp->last == node) sp->last = node->p.other;
3982             if (node->p.other) node->p.other->n.other = node->n.other;
3983             if (node->n.other) node->n.other->p.other = node->p.other;
3984         }
3985     } else { // this was the last node on subpath
3986         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
3987     }
3989     g_mem_chunk_free(nodechunk, node);
3992 /**
3993  * Returns one of the node's two sides.
3994  * \param which Indicates which side.
3995  * \return Pointer to previous node side if which==-1, next if which==1.
3996  */
3997 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
3999     g_assert(node);
4001     switch (which) {
4002         case -1:
4003             return &node->p;
4004         case 1:
4005             return &node->n;
4006         default:
4007             break;
4008     }
4010     g_assert_not_reached();
4012     return NULL;
4015 /**
4016  * Return the other side of the node, given one of its sides.
4017  */
4018 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4020     g_assert(node);
4022     if (me == &node->p) return &node->n;
4023     if (me == &node->n) return &node->p;
4025     g_assert_not_reached();
4027     return NULL;
4030 /**
4031  * Return NRPathcode on the given side of the node.
4032  */
4033 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4035     g_assert(node);
4037     if (me == &node->p) {
4038         if (node->p.other) return (NRPathcode)node->code;
4039         return NR_MOVETO;
4040     }
4042     if (me == &node->n) {
4043         if (node->n.other) return (NRPathcode)node->n.other->code;
4044         return NR_MOVETO;
4045     }
4047     g_assert_not_reached();
4049     return NR_END;
4052 /**
4053  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
4054  */
4055 Inkscape::NodePath::Node *
4056 sp_nodepath_get_node_by_index(int index)
4058     Inkscape::NodePath::Node *e = NULL;
4060     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4061     if (!nodepath) {
4062         return e;
4063     }
4065     //find segment
4066     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4068         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4069         int n = g_list_length(sp->nodes);
4070         if (sp->closed) {
4071             n++;
4072         }
4074         //if the piece belongs to this subpath grab it
4075         //otherwise move onto the next subpath
4076         if (index < n) {
4077             e = sp->first;
4078             for (int i = 0; i < index; ++i) {
4079                 e = e->n.other;
4080             }
4081             break;
4082         } else {
4083             if (sp->closed) {
4084                 index -= (n+1);
4085             } else {
4086                 index -= n;
4087             }
4088         }
4089     }
4091     return e;
4094 /**
4095  * Returns plain text meaning of node type.
4096  */
4097 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4099     unsigned retracted = 0;
4100     bool endnode = false;
4102     for (int which = -1; which <= 1; which += 2) {
4103         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4104         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4105             retracted ++;
4106         if (!side->other)
4107             endnode = true;
4108     }
4110     if (retracted == 0) {
4111         if (endnode) {
4112                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4113                 return _("end node");
4114         } else {
4115             switch (node->type) {
4116                 case Inkscape::NodePath::NODE_CUSP:
4117                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4118                     return _("cusp");
4119                 case Inkscape::NodePath::NODE_SMOOTH:
4120                     // TRANSLATORS: "smooth" is an adjective here
4121                     return _("smooth");
4122                 case Inkscape::NodePath::NODE_SYMM:
4123                     return _("symmetric");
4124             }
4125         }
4126     } else if (retracted == 1) {
4127         if (endnode) {
4128             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4129             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4130         } else {
4131             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4132         }
4133     } else {
4134         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4135     }
4137     return NULL;
4140 /**
4141  * Handles content of statusbar as long as node tool is active.
4142  */
4143 void
4144 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
4146     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");
4147     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4149     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4150     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4151     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4152     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4154     SPDesktop *desktop = NULL;
4155     if (nodepath) {
4156         desktop = nodepath->desktop;
4157     } else {
4158         desktop = SP_ACTIVE_DESKTOP;
4159     }
4161     SPEventContext *ec = desktop->event_context;
4162     if (!ec) return;
4163     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4164     if (!mc) return;
4166     if (selected_nodes == 0) {
4167         Inkscape::Selection *sel = desktop->selection;
4168         if (!sel || sel->isEmpty()) {
4169             mc->setF(Inkscape::NORMAL_MESSAGE,
4170                      _("Select a single object to edit its nodes or handles."));
4171         } else {
4172             if (nodepath) {
4173             mc->setF(Inkscape::NORMAL_MESSAGE,
4174                      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.",
4175                               "<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.",
4176                               total_nodes),
4177                      total_nodes);
4178             } else {
4179                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4180                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4181                 } else {
4182                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4183                 }
4184             }
4185         }
4186     } else if (nodepath && selected_nodes == 1) {
4187         mc->setF(Inkscape::NORMAL_MESSAGE,
4188                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4189                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4190                           total_nodes),
4191                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4192     } else {
4193         if (selected_subpaths > 1) {
4194             mc->setF(Inkscape::NORMAL_MESSAGE,
4195                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4196                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4197                               total_nodes),
4198                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4199         } else {
4200             mc->setF(Inkscape::NORMAL_MESSAGE,
4201                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4202                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4203                               total_nodes),
4204                      selected_nodes, total_nodes, when_selected);
4205         }
4206     }
4210 /*
4211   Local Variables:
4212   mode:c++
4213   c-file-style:"stroustrup"
4214   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4215   indent-tabs-mode:nil
4216   fill-column:99
4217   End:
4218 */
4219 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :