Code

finally getting closer to processing axes and contexts correctly
[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, const gchar *annotation)
482     update_repr_internal(np);
483     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE, 
484                      annotation);
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, const gchar *annotation)
503     update_repr_internal(np);
504     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE, 
505                            annotation);
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                      _("Stamp"));
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", _("Move nodes vertically"));
1220     } else if (dy == 0) {
1221         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1222     } else {
1223         sp_nodepath_update_repr(nodepath, _("Move nodes"));
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", _("Move nodes vertically"));
1248     } else if (dy == 0) {
1249         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1250     } else {
1251         sp_nodepath_update_repr(nodepath, _("Move nodes"));
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, _("Align nodes"));
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, _("Distribute nodes"));
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     int n_added = 0;
1511     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1512        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1513         g_assert(t->selected);
1514         if (t->p.other && t->p.other->selected) {
1515             nl = g_list_prepend(nl, t);
1516         }
1517     }
1519     while (nl) {
1520        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1521        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1522        sp_nodepath_node_select(n, TRUE, FALSE);
1523        n_added ++;
1524        nl = g_list_remove(nl, t);
1525     }
1527     /** \todo fixme: adjust ? */
1528     sp_nodepath_update_handles(nodepath);
1530     if (n_added > 1) {
1531         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1532     } else if (n_added > 0) {
1533         sp_nodepath_update_repr(nodepath, _("Add node"));
1534     }
1536     sp_nodepath_update_statusbar(nodepath);
1539 /**
1540  * Select segment nearest to point
1541  */
1542 void
1543 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1545     if (!nodepath) {
1546         return;
1547     }
1549     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1551     //find segment to segment
1552     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1554     gboolean force = FALSE;
1555     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1556         force = TRUE;
1557     }
1558     sp_nodepath_node_select(e, (gboolean) toggle, force);
1559     if (e->p.other)
1560         sp_nodepath_node_select(e->p.other, TRUE, force);
1562     sp_nodepath_update_handles(nodepath);
1564     sp_nodepath_update_statusbar(nodepath);
1567 /**
1568  * Add a node nearest to point
1569  */
1570 void
1571 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1573     if (!nodepath) {
1574         return;
1575     }
1577     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1579     //find segment to split
1580     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1582     //don't know why but t seems to flip for lines
1583     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1584         position.t = 1.0 - position.t;
1585     }
1586     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1587     sp_nodepath_node_select(n, FALSE, TRUE);
1589     /* fixme: adjust ? */
1590     sp_nodepath_update_handles(nodepath);
1592     sp_nodepath_update_repr(nodepath, _("Add node"));
1594     sp_nodepath_update_statusbar(nodepath);
1597 /*
1598  * Adjusts a segment so that t moves by a certain delta for dragging
1599  * converts lines to curves
1600  *
1601  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1602  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1603  */
1604 void
1605 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta)
1607     /* feel good is an arbitrary parameter that distributes the delta between handles
1608      * if t of the drag point is less than 1/6 distance form the endpoint only
1609      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1610      */
1611     double feel_good;
1612     if (t <= 1.0 / 6.0)
1613         feel_good = 0;
1614     else if (t <= 0.5)
1615         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1616     else if (t <= 5.0 / 6.0)
1617         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1618     else
1619         feel_good = 1;
1621     //if we're dragging a line convert it to a curve
1622     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1623         sp_nodepath_set_line_type(e, NR_CURVETO);
1624     }
1626     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1627     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1628     e->p.other->n.pos += offsetcoord0;
1629     e->p.pos += offsetcoord1;
1631     // adjust handles of adjacent nodes where necessary
1632     sp_node_adjust_handle(e,1);
1633     sp_node_adjust_handle(e->p.other,-1);
1635     sp_nodepath_update_handles(e->subpath->nodepath);
1637     update_object(e->subpath->nodepath);
1639     sp_nodepath_update_statusbar(e->subpath->nodepath);
1643 /**
1644  * Call sp_nodepath_break() for all selected segments.
1645  */
1646 void sp_node_selected_break()
1648     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1649     if (!nodepath) return;
1651     GList *temp = NULL;
1652     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1653        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1654        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1655         if (nn == NULL) continue; // no break, no new node
1656         temp = g_list_prepend(temp, nn);
1657     }
1659     if (temp) {
1660         sp_nodepath_deselect(nodepath);
1661     }
1662     for (GList *l = temp; l != NULL; l = l->next) {
1663         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1664     }
1666     sp_nodepath_update_handles(nodepath);
1668     sp_nodepath_update_repr(nodepath, _("Break path"));
1671 /**
1672  * Duplicate the selected node(s).
1673  */
1674 void sp_node_selected_duplicate()
1676     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1677     if (!nodepath) {
1678         return;
1679     }
1681     GList *temp = NULL;
1682     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1683        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1684        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1685         if (nn == NULL) continue; // could not duplicate
1686         temp = g_list_prepend(temp, nn);
1687     }
1689     if (temp) {
1690         sp_nodepath_deselect(nodepath);
1691     }
1692     for (GList *l = temp; l != NULL; l = l->next) {
1693         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1694     }
1696     sp_nodepath_update_handles(nodepath);
1698     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1701 /**
1702  *  Join two nodes by merging them into one.
1703  */
1704 void sp_node_selected_join()
1706     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1707     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1709     if (g_list_length(nodepath->selected) != 2) {
1710         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1711         return;
1712     }
1714    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1715    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1717     g_assert(a != b);
1718     g_assert(a->p.other || a->n.other);
1719     g_assert(b->p.other || b->n.other);
1721     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1722         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1723         return;
1724     }
1726     /* a and b are endpoints */
1728     NR::Point c;
1729     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1730         c = a->pos;
1731     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1732         c = b->pos;
1733     } else {
1734         c = (a->pos + b->pos) / 2;
1735     }
1737     if (a->subpath == b->subpath) {
1738        Inkscape::NodePath::SubPath *sp = a->subpath;
1739         sp_nodepath_subpath_close(sp);
1740         sp_node_moveto (sp->first, c);
1742         sp_nodepath_update_handles(sp->nodepath);
1743         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1744         return;
1745     }
1747     /* a and b are separate subpaths */
1748    Inkscape::NodePath::SubPath *sa = a->subpath;
1749    Inkscape::NodePath::SubPath *sb = b->subpath;
1750     NR::Point p;
1751    Inkscape::NodePath::Node *n;
1752     NRPathcode code;
1753     if (a == sa->first) {
1754         p = sa->first->n.pos;
1755         code = (NRPathcode)sa->first->n.other->code;
1756        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1757         n = sa->last;
1758         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1759         n = n->p.other;
1760         while (n) {
1761             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1762             n = n->p.other;
1763             if (n == sa->first) n = NULL;
1764         }
1765         sp_nodepath_subpath_destroy(sa);
1766         sa = t;
1767     } else if (a == sa->last) {
1768         p = sa->last->p.pos;
1769         code = (NRPathcode)sa->last->code;
1770         sp_nodepath_node_destroy(sa->last);
1771     } else {
1772         code = NR_END;
1773         g_assert_not_reached();
1774     }
1776     if (b == sb->first) {
1777         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1778         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1779             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1780         }
1781     } else if (b == sb->last) {
1782         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1783         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1784             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1785         }
1786     } else {
1787         g_assert_not_reached();
1788     }
1789     /* and now destroy sb */
1791     sp_nodepath_subpath_destroy(sb);
1793     sp_nodepath_update_handles(sa->nodepath);
1795     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1797     sp_nodepath_update_statusbar(nodepath);
1800 /**
1801  *  Join two nodes by adding a segment between them.
1802  */
1803 void sp_node_selected_join_segment()
1805     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1806     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1808     if (g_list_length(nodepath->selected) != 2) {
1809         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1810         return;
1811     }
1813    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1814    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1816     g_assert(a != b);
1817     g_assert(a->p.other || a->n.other);
1818     g_assert(b->p.other || b->n.other);
1820     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1821         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1822         return;
1823     }
1825     if (a->subpath == b->subpath) {
1826        Inkscape::NodePath::SubPath *sp = a->subpath;
1828         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1829         sp->closed = TRUE;
1831         sp->first->p.other = sp->last;
1832         sp->last->n.other  = sp->first;
1834         sp_node_handle_mirror_p_to_n(sp->last);
1835         sp_node_handle_mirror_n_to_p(sp->first);
1837         sp->first->code = sp->last->code;
1838         sp->first       = sp->last;
1840         sp_nodepath_update_handles(sp->nodepath);
1842         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
1844         return;
1845     }
1847     /* a and b are separate subpaths */
1848    Inkscape::NodePath::SubPath *sa = a->subpath;
1849    Inkscape::NodePath::SubPath *sb = b->subpath;
1851    Inkscape::NodePath::Node *n;
1852     NR::Point p;
1853     NRPathcode code;
1854     if (a == sa->first) {
1855         code = (NRPathcode) sa->first->n.other->code;
1856        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1857         n = sa->last;
1858         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1859         for (n = n->p.other; n != NULL; n = n->p.other) {
1860             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1861         }
1862         sp_nodepath_subpath_destroy(sa);
1863         sa = t;
1864     } else if (a == sa->last) {
1865         code = (NRPathcode)sa->last->code;
1866     } else {
1867         code = NR_END;
1868         g_assert_not_reached();
1869     }
1871     if (b == sb->first) {
1872         n = sb->first;
1873         sp_node_handle_mirror_p_to_n(sa->last);
1874         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1875         sp_node_handle_mirror_n_to_p(sa->last);
1876         for (n = n->n.other; n != NULL; n = n->n.other) {
1877             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1878         }
1879     } else if (b == sb->last) {
1880         n = sb->last;
1881         sp_node_handle_mirror_p_to_n(sa->last);
1882         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1883         sp_node_handle_mirror_n_to_p(sa->last);
1884         for (n = n->p.other; n != NULL; n = n->p.other) {
1885             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1886         }
1887     } else {
1888         g_assert_not_reached();
1889     }
1890     /* and now destroy sb */
1892     sp_nodepath_subpath_destroy(sb);
1894     sp_nodepath_update_handles(sa->nodepath);
1896     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
1899 /**
1900  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1901  */
1902 void sp_node_delete_preserve(GList *nodes_to_delete)
1904     GSList *nodepaths = NULL;
1905     
1906     while (nodes_to_delete) {
1907         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1908         Inkscape::NodePath::SubPath *sp = node->subpath;
1909         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1910         Inkscape::NodePath::Node *sample_cursor = NULL;
1911         Inkscape::NodePath::Node *sample_end = NULL;
1912         Inkscape::NodePath::Node *delete_cursor = node;
1913         bool just_delete = false;
1914         
1915         //find the start of this contiguous selection
1916         //move left to the first node that is not selected
1917         //or the start of the non-closed path
1918         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1919             delete_cursor = curr;
1920         }
1922         //just delete at the beginning of an open path
1923         if (!delete_cursor->p.other) {
1924             sample_cursor = delete_cursor;
1925             just_delete = true;
1926         } else {
1927             sample_cursor = delete_cursor->p.other;
1928         }
1929         
1930         //calculate points for each segment
1931         int rate = 5;
1932         float period = 1.0 / rate;
1933         std::vector<NR::Point> data;
1934         if (!just_delete) {
1935             data.push_back(sample_cursor->pos);
1936             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1937                 //just delete at the end of an open path
1938                 if (!sp->closed && curr->n.other == sp->last) {
1939                     just_delete = true;
1940                     break;
1941                 }
1942                 
1943                 //sample points on the contiguous selected segment
1944                 NR::Point *bez;
1945                 bez = new NR::Point [4];
1946                 bez[0] = curr->pos;
1947                 bez[1] = curr->n.pos;
1948                 bez[2] = curr->n.other->p.pos;
1949                 bez[3] = curr->n.other->pos;
1950                 for (int i=1; i<rate; i++) {
1951                     gdouble t = i * period;
1952                     NR::Point p = bezier_pt(3, bez, t);
1953                     data.push_back(p);
1954                 }
1955                 data.push_back(curr->n.other->pos);
1957                 sample_end = curr->n.other;
1958                 //break if we've come full circle or hit the end of the selection
1959                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
1960                     break;
1961                 }
1962             }
1963         }
1965         if (!just_delete) {
1966             //calculate the best fitting single segment and adjust the endpoints
1967             NR::Point *adata;
1968             adata = new NR::Point [data.size()];
1969             copy(data.begin(), data.end(), adata);
1970             
1971             NR::Point *bez;
1972             bez = new NR::Point [4];
1973             //would decreasing error create a better fitting approximation?
1974             gdouble error = 1.0;
1975             gint ret;
1976             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
1978             //adjust endpoints
1979             sample_cursor->n.pos = bez[1];
1980             sample_end->p.pos = bez[2];
1981         }
1982        
1983         //destroy this contiguous selection
1984         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
1985             Inkscape::NodePath::Node *temp = delete_cursor;
1986             if (delete_cursor->n.other == delete_cursor) {
1987                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
1988                 delete_cursor = NULL; 
1989             } else {
1990                 delete_cursor = delete_cursor->n.other;
1991             }
1992             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
1993             sp_nodepath_node_destroy(temp);
1994         }
1996         sp_nodepath_update_handles(nodepath);
1998         if (!g_slist_find(nodepaths, nodepath))
1999             nodepaths = g_slist_prepend (nodepaths, nodepath);
2000     }
2002     for (GSList *i = nodepaths; i; i = i->next) {
2003         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2004         // different nodepaths will give us one undo event per nodepath
2005         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2007         // if the entire nodepath is removed, delete the selected object.
2008         if (nodepath->subpaths == NULL ||
2009             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2010             //at least 2
2011             sp_nodepath_get_node_count(nodepath) < 2) {
2012             SPDocument *document = sp_desktop_document (nodepath->desktop);
2013             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2014             //delete this nodepath's object, not the entire selection! (though at this time, this
2015             //does not matter)
2016             sp_selection_delete();
2017             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2018                               _("Delete nodes"));
2019         } else {
2020             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2021             sp_nodepath_update_statusbar(nodepath);
2022         }
2023     }
2025     g_slist_free (nodepaths);
2028 /**
2029  * Delete one or more selected nodes.
2030  */
2031 void sp_node_selected_delete()
2033     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2034     if (!nodepath) return;
2035     if (!nodepath->selected) return;
2037     /** \todo fixme: do it the right way */
2038     while (nodepath->selected) {
2039        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2040         sp_nodepath_node_destroy(node);
2041     }
2044     //clean up the nodepath (such as for trivial subpaths)
2045     sp_nodepath_cleanup(nodepath);
2047     sp_nodepath_update_handles(nodepath);
2049     // if the entire nodepath is removed, delete the selected object.
2050     if (nodepath->subpaths == NULL ||
2051         sp_nodepath_get_node_count(nodepath) < 2) {
2052         SPDocument *document = sp_desktop_document (nodepath->desktop);
2053         sp_selection_delete();
2054         sp_document_done (document, SP_VERB_CONTEXT_NODE, 
2055                           _("Delete nodes"));
2056         return;
2057     }
2059     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2061     sp_nodepath_update_statusbar(nodepath);
2064 /**
2065  * Delete one or more segments between two selected nodes.
2066  * This is the code for 'split'.
2067  */
2068 void
2069 sp_node_selected_delete_segment(void)
2071    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2072    Inkscape::NodePath::Node *curr, *next;     //Iterators
2074     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2075     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2077     if (g_list_length(nodepath->selected) != 2) {
2078         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2079                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2080         return;
2081     }
2083     //Selected nodes, not inclusive
2084    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2085    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2087     if ( ( a==b)                       ||  //same node
2088          (a->subpath  != b->subpath )  ||  //not the same path
2089          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2090          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2091     {
2092         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2093                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2094         return;
2095     }
2097     //###########################################
2098     //# BEGIN EDITS
2099     //###########################################
2100     //##################################
2101     //# CLOSED PATH
2102     //##################################
2103     if (a->subpath->closed) {
2106         gboolean reversed = FALSE;
2108         //Since we can go in a circle, we need to find the shorter distance.
2109         //  a->b or b->a
2110         start = end = NULL;
2111         int distance    = 0;
2112         int minDistance = 0;
2113         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2114             if (curr==b) {
2115                 //printf("a to b:%d\n", distance);
2116                 start = a;//go from a to b
2117                 end   = b;
2118                 minDistance = distance;
2119                 //printf("A to B :\n");
2120                 break;
2121             }
2122             distance++;
2123         }
2125         //try again, the other direction
2126         distance = 0;
2127         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2128             if (curr==a) {
2129                 //printf("b to a:%d\n", distance);
2130                 if (distance < minDistance) {
2131                     start    = b;  //we go from b to a
2132                     end      = a;
2133                     reversed = TRUE;
2134                     //printf("B to A\n");
2135                 }
2136                 break;
2137             }
2138             distance++;
2139         }
2142         //Copy everything from 'end' to 'start' to a new subpath
2143        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2144         for (curr=end ; curr ; curr=curr->n.other) {
2145             NRPathcode code = (NRPathcode) curr->code;
2146             if (curr == end)
2147                 code = NR_MOVETO;
2148             sp_nodepath_node_new(t, NULL,
2149                                  (Inkscape::NodePath::NodeType)curr->type, code,
2150                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2151             if (curr == start)
2152                 break;
2153         }
2154         sp_nodepath_subpath_destroy(a->subpath);
2157     }
2161     //##################################
2162     //# OPEN PATH
2163     //##################################
2164     else {
2166         //We need to get the direction of the list between A and B
2167         //Can we walk from a to b?
2168         start = end = NULL;
2169         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2170             if (curr==b) {
2171                 start = a;  //did it!  we go from a to b
2172                 end   = b;
2173                 //printf("A to B\n");
2174                 break;
2175             }
2176         }
2177         if (!start) {//didn't work?  let's try the other direction
2178             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2179                 if (curr==a) {
2180                     start = b;  //did it!  we go from b to a
2181                     end   = a;
2182                     //printf("B to A\n");
2183                     break;
2184                 }
2185             }
2186         }
2187         if (!start) {
2188             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2189                                                      _("Cannot find path between nodes."));
2190             return;
2191         }
2195         //Copy everything after 'end' to a new subpath
2196        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2197         for (curr=end ; curr ; curr=curr->n.other) {
2198             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
2199                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2200         }
2202         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2203         for (curr = start->n.other ; curr  ; curr=next) {
2204             next = curr->n.other;
2205             sp_nodepath_node_destroy(curr);
2206         }
2208     }
2209     //###########################################
2210     //# END EDITS
2211     //###########################################
2213     //clean up the nodepath (such as for trivial subpaths)
2214     sp_nodepath_cleanup(nodepath);
2216     sp_nodepath_update_handles(nodepath);
2218     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2220     sp_nodepath_update_statusbar(nodepath);
2223 /**
2224  * Call sp_nodepath_set_line() for all selected segments.
2225  */
2226 void
2227 sp_node_selected_set_line_type(NRPathcode code)
2229     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2230     if (nodepath == NULL) return;
2232     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2233        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2234         g_assert(n->selected);
2235         if (n->p.other && n->p.other->selected) {
2236             sp_nodepath_set_line_type(n, code);
2237         }
2238     }
2240     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2243 /**
2244  * Call sp_nodepath_convert_node_type() for all selected nodes.
2245  */
2246 void
2247 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
2249     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2250     if (nodepath == NULL) return;
2252     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2253         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2254     }
2256     sp_nodepath_update_repr(nodepath, _("Change node type"));
2259 /**
2260  * Change select status of node, update its own and neighbour handles.
2261  */
2262 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2264     node->selected = selected;
2266     if (selected) {
2267         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2268         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2269         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2270         sp_knot_update_ctrl(node->knot);
2271     } else {
2272         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2273         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2274         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2275         sp_knot_update_ctrl(node->knot);
2276     }
2278     sp_node_update_handles(node);
2279     if (node->n.other) sp_node_update_handles(node->n.other);
2280     if (node->p.other) sp_node_update_handles(node->p.other);
2283 /**
2284 \brief Select a node
2285 \param node     The node to select
2286 \param incremental   If true, add to selection, otherwise deselect others
2287 \param override   If true, always select this node, otherwise toggle selected status
2288 */
2289 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2291     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2293     if (incremental) {
2294         if (override) {
2295             if (!g_list_find(nodepath->selected, node)) {
2296                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2297             }
2298             sp_node_set_selected(node, TRUE);
2299         } else { // toggle
2300             if (node->selected) {
2301                 g_assert(g_list_find(nodepath->selected, node));
2302                 nodepath->selected = g_list_remove(nodepath->selected, node);
2303             } else {
2304                 g_assert(!g_list_find(nodepath->selected, node));
2305                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2306             }
2307             sp_node_set_selected(node, !node->selected);
2308         }
2309     } else {
2310         sp_nodepath_deselect(nodepath);
2311         nodepath->selected = g_list_prepend(nodepath->selected, node);
2312         sp_node_set_selected(node, TRUE);
2313     }
2315     sp_nodepath_update_statusbar(nodepath);
2319 /**
2320 \brief Deselect all nodes in the nodepath
2321 */
2322 void
2323 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2325     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2327     while (nodepath->selected) {
2328         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2329         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2330     }
2331     sp_nodepath_update_statusbar(nodepath);
2334 /**
2335 \brief Select or invert selection of all nodes in the nodepath
2336 */
2337 void
2338 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2340     if (!nodepath) return;
2342     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2343        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2344         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2345            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2346            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2347         }
2348     }
2351 /**
2352  * If nothing selected, does the same as sp_nodepath_select_all();
2353  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2354  * (i.e., similar to "select all in layer", with the "selected" subpaths
2355  * being treated as "layers" in the path).
2356  */
2357 void
2358 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2360     if (!nodepath) return;
2362     if (g_list_length (nodepath->selected) == 0) {
2363         sp_nodepath_select_all (nodepath, invert);
2364         return;
2365     }
2367     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2368     GSList *subpaths = NULL;
2370     for (GList *l = copy; l != NULL; l = l->next) {
2371         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2372         Inkscape::NodePath::SubPath *subpath = n->subpath;
2373         if (!g_slist_find (subpaths, subpath))
2374             subpaths = g_slist_prepend (subpaths, subpath);
2375     }
2377     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2378         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2379         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2380             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2381             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2382         }
2383     }
2385     g_slist_free (subpaths);
2386     g_list_free (copy);
2389 /**
2390  * \brief Select the node after the last selected; if none is selected,
2391  * select the first within path.
2392  */
2393 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2395     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2397    Inkscape::NodePath::Node *last = NULL;
2398     if (nodepath->selected) {
2399         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2400            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2401             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2402             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2403                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2404                 if (node->selected) {
2405                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2406                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2407                             if (spl->next) { // there's a next subpath
2408                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2409                                 last = subpath_next->first;
2410                             } else if (spl->prev) { // there's a previous subpath
2411                                 last = NULL; // to be set later to the first node of first subpath
2412                             } else {
2413                                 last = node->n.other;
2414                             }
2415                         } else {
2416                             last = node->n.other;
2417                         }
2418                     } else {
2419                         if (node->n.other) {
2420                             last = node->n.other;
2421                         } else {
2422                             if (spl->next) { // there's a next subpath
2423                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2424                                 last = subpath_next->first;
2425                             } else if (spl->prev) { // there's a previous subpath
2426                                 last = NULL; // to be set later to the first node of first subpath
2427                             } else {
2428                                 last = (Inkscape::NodePath::Node *) subpath->first;
2429                             }
2430                         }
2431                     }
2432                 }
2433             }
2434         }
2435         sp_nodepath_deselect(nodepath);
2436     }
2438     if (last) { // there's at least one more node after selected
2439         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2440     } else { // no more nodes, select the first one in first subpath
2441        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2442         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2443     }
2446 /**
2447  * \brief Select the node before the first selected; if none is selected,
2448  * select the last within path
2449  */
2450 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2452     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2454    Inkscape::NodePath::Node *last = NULL;
2455     if (nodepath->selected) {
2456         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2457            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2458             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2459                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2460                 if (node->selected) {
2461                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2462                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2463                             if (spl->prev) { // there's a prev subpath
2464                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2465                                 last = subpath_prev->last;
2466                             } else if (spl->next) { // there's a next subpath
2467                                 last = NULL; // to be set later to the last node of last subpath
2468                             } else {
2469                                 last = node->p.other;
2470                             }
2471                         } else {
2472                             last = node->p.other;
2473                         }
2474                     } else {
2475                         if (node->p.other) {
2476                             last = node->p.other;
2477                         } else {
2478                             if (spl->prev) { // there's a prev subpath
2479                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2480                                 last = subpath_prev->last;
2481                             } else if (spl->next) { // there's a next subpath
2482                                 last = NULL; // to be set later to the last node of last subpath
2483                             } else {
2484                                 last = (Inkscape::NodePath::Node *) subpath->last;
2485                             }
2486                         }
2487                     }
2488                 }
2489             }
2490         }
2491         sp_nodepath_deselect(nodepath);
2492     }
2494     if (last) { // there's at least one more node before selected
2495         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2496     } else { // no more nodes, select the last one in last subpath
2497         GList *spl = g_list_last(nodepath->subpaths);
2498        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2499         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2500     }
2503 /**
2504  * \brief Select all nodes that are within the rectangle.
2505  */
2506 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2508     if (!incremental) {
2509         sp_nodepath_deselect(nodepath);
2510     }
2512     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2513        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2514         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2515            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2517             if (b.contains(node->pos)) {
2518                 sp_nodepath_node_select(node, TRUE, TRUE);
2519             }
2520         }
2521     }
2525 void
2526 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2528     g_assert (n);
2529     g_assert (nodepath);
2530     g_assert (n->subpath->nodepath == nodepath);
2532     if (g_list_length (nodepath->selected) == 0) {
2533         if (grow > 0) {
2534             sp_nodepath_node_select(n, TRUE, TRUE);
2535         }
2536         return;
2537     }
2539     if (g_list_length (nodepath->selected) == 1) {
2540         if (grow < 0) {
2541             sp_nodepath_deselect (nodepath);
2542             return;
2543         }
2544     }
2546         double n_sel_range = 0, p_sel_range = 0;
2547             Inkscape::NodePath::Node *farthest_n_node = n;
2548             Inkscape::NodePath::Node *farthest_p_node = n;
2550         // Calculate ranges
2551         {
2552             double n_range = 0, p_range = 0;
2553             bool n_going = true, p_going = true;
2554             Inkscape::NodePath::Node *n_node = n;
2555             Inkscape::NodePath::Node *p_node = n;
2556             do {
2557                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2558                 if (n_node && n_going)
2559                     n_node = n_node->n.other;
2560                 if (n_node == NULL) {
2561                     n_going = false;
2562                 } else {
2563                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2564                     if (n_node->selected) {
2565                         n_sel_range = n_range;
2566                         farthest_n_node = n_node;
2567                     }
2568                     if (n_node == p_node) {
2569                         n_going = false;
2570                         p_going = false;
2571                     }
2572                 }
2573                 if (p_node && p_going)
2574                     p_node = p_node->p.other;
2575                 if (p_node == NULL) {
2576                     p_going = false;
2577                 } else {
2578                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2579                     if (p_node->selected) {
2580                         p_sel_range = p_range;
2581                         farthest_p_node = p_node;
2582                     }
2583                     if (p_node == n_node) {
2584                         n_going = false;
2585                         p_going = false;
2586                     }
2587                 }
2588             } while (n_going || p_going);
2589         }
2591     if (grow > 0) {
2592         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2593                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2594         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2595                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2596         }
2597     } else {
2598         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2599                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2600         } else if (farthest_p_node && farthest_p_node->selected) {
2601                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2602         }
2603     }
2606 void
2607 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2609     g_assert (n);
2610     g_assert (nodepath);
2611     g_assert (n->subpath->nodepath == nodepath);
2613     if (g_list_length (nodepath->selected) == 0) {
2614         if (grow > 0) {
2615             sp_nodepath_node_select(n, TRUE, TRUE);
2616         }
2617         return;
2618     }
2620     if (g_list_length (nodepath->selected) == 1) {
2621         if (grow < 0) {
2622             sp_nodepath_deselect (nodepath);
2623             return;
2624         }
2625     }
2627     Inkscape::NodePath::Node *farthest_selected = NULL;
2628     double farthest_dist = 0;
2630     Inkscape::NodePath::Node *closest_unselected = NULL;
2631     double closest_dist = NR_HUGE;
2633     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2634        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2635         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2636            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2637            if (node == n)
2638                continue;
2639            if (node->selected) {
2640                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2641                    farthest_dist = NR::L2(node->pos - n->pos);
2642                    farthest_selected = node;
2643                }
2644            } else {
2645                if (NR::L2(node->pos - n->pos) < closest_dist) {
2646                    closest_dist = NR::L2(node->pos - n->pos);
2647                    closest_unselected = node;
2648                }
2649            }
2650         }
2651     }
2653     if (grow > 0) {
2654         if (closest_unselected) {
2655             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2656         }
2657     } else {
2658         if (farthest_selected) {
2659             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2660         }
2661     }
2665 /**
2666 \brief  Saves all nodes' and handles' current positions in their origin members
2667 */
2668 void
2669 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2671     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2672        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2673         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2674            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2675            n->origin = n->pos;
2676            n->p.origin = n->p.pos;
2677            n->n.origin = n->n.pos;
2678         }
2679     }
2680
2682 /**
2683 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2684 */
2685 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2687     if (!nodepath->selected) {
2688         return NULL;
2689     }
2691     GList *r = NULL;
2692     guint i = 0;
2693     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2694        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2695         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2696            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2697             i++;
2698             if (node->selected) {
2699                 r = g_list_append(r, GINT_TO_POINTER(i));
2700             }
2701         }
2702     }
2703     return r;
2706 /**
2707 \brief  Restores selection by selecting nodes whose positions are in the list
2708 */
2709 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2711     sp_nodepath_deselect(nodepath);
2713     guint i = 0;
2714     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2715        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2716         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2717            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2718             i++;
2719             if (g_list_find(r, GINT_TO_POINTER(i))) {
2720                 sp_nodepath_node_select(node, TRUE, TRUE);
2721             }
2722         }
2723     }
2727 /**
2728 \brief Adjusts handle according to node type and line code.
2729 */
2730 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2732     double len, otherlen, linelen;
2734     g_assert(node);
2736    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2737    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2739     /** \todo fixme: */
2740     if (me->other == NULL) return;
2741     if (other->other == NULL) return;
2743     /* I have line */
2745     NRPathcode mecode, ocode;
2746     if (which_adjust == 1) {
2747         mecode = (NRPathcode)me->other->code;
2748         ocode = (NRPathcode)node->code;
2749     } else {
2750         mecode = (NRPathcode)node->code;
2751         ocode = (NRPathcode)other->other->code;
2752     }
2754     if (mecode == NR_LINETO) return;
2756     /* I am curve */
2758     if (other->other == NULL) return;
2760     /* Other has line */
2762     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2764     NR::Point delta;
2765     if (ocode == NR_LINETO) {
2766         /* other is lineto, we are either smooth or symm */
2767        Inkscape::NodePath::Node *othernode = other->other;
2768         len = NR::L2(me->pos - node->pos);
2769         delta = node->pos - othernode->pos;
2770         linelen = NR::L2(delta);
2771         if (linelen < 1e-18) 
2772             return;
2773         me->pos = node->pos + (len / linelen)*delta;
2774         return;
2775     }
2777     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2779         me->pos = 2 * node->pos - other->pos;
2780         return;
2781     }
2783     /* We are smooth */
2785     len = NR::L2(me->pos - node->pos);
2786     delta = other->pos - node->pos;
2787     otherlen = NR::L2(delta);
2788     if (otherlen < 1e-18) return;
2790     me->pos = node->pos - (len / otherlen) * delta;
2793 /**
2794  \brief Adjusts both handles according to node type and line code
2795  */
2796 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2798     g_assert(node);
2800     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2802     /* we are either smooth or symm */
2804     if (node->p.other == NULL) return;
2806     if (node->n.other == NULL) return;
2808     if (node->code == NR_LINETO) {
2809         if (node->n.other->code == NR_LINETO) return;
2810         sp_node_adjust_handle(node, 1);
2811         return;
2812     }
2814     if (node->n.other->code == NR_LINETO) {
2815         if (node->code == NR_LINETO) return;
2816         sp_node_adjust_handle(node, -1);
2817         return;
2818     }
2820     /* both are curves */
2821     NR::Point const delta( node->n.pos - node->p.pos );
2823     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2824         node->p.pos = node->pos - delta / 2;
2825         node->n.pos = node->pos + delta / 2;
2826         return;
2827     }
2829     /* We are smooth */
2830     double plen = NR::L2(node->p.pos - node->pos);
2831     if (plen < 1e-18) return;
2832     double nlen = NR::L2(node->n.pos - node->pos);
2833     if (nlen < 1e-18) return;
2834     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2835     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2838 /**
2839  * Node event callback.
2840  */
2841 static gboolean node_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n)
2843     gboolean ret = FALSE;
2844     switch (event->type) {
2845         case GDK_ENTER_NOTIFY:
2846             active_node = n;
2847             break;
2848         case GDK_LEAVE_NOTIFY:
2849             active_node = NULL;
2850             break;
2851         case GDK_KEY_PRESS:
2852             switch (get_group0_keyval (&event->key)) {
2853                 case GDK_space:
2854                     if (event->key.state & GDK_BUTTON1_MASK) {
2855                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2856                         stamp_repr(nodepath);
2857                         ret = TRUE;
2858                     }
2859                     break;
2860                 case GDK_Page_Up:
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                 case GDK_Page_Down:
2868                     if (event->key.state & GDK_CONTROL_MASK) {
2869                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2870                     } else {
2871                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2872                     }
2873                     break;
2874                 default:
2875                     break;
2876             }
2877             break;
2878         default:
2879             break;
2880     }
2882     return ret;
2885 /**
2886  * Handle keypress on node; directly called.
2887  */
2888 gboolean node_key(GdkEvent *event)
2890     Inkscape::NodePath::Path *np;
2892     // there is no way to verify nodes so set active_node to nil when deleting!!
2893     if (active_node == NULL) return FALSE;
2895     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2896         gint ret = FALSE;
2897         switch (get_group0_keyval (&event->key)) {
2898             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2899             case GDK_BackSpace:
2900                 np = active_node->subpath->nodepath;
2901                 sp_nodepath_node_destroy(active_node);
2902                 sp_nodepath_update_repr(np, _("Delete node"));
2903                 active_node = NULL;
2904                 ret = TRUE;
2905                 break;
2906             case GDK_c:
2907                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2908                 ret = TRUE;
2909                 break;
2910             case GDK_s:
2911                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2912                 ret = TRUE;
2913                 break;
2914             case GDK_y:
2915                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2916                 ret = TRUE;
2917                 break;
2918             case GDK_b:
2919                 sp_nodepath_node_break(active_node);
2920                 ret = TRUE;
2921                 break;
2922         }
2923         return ret;
2924     }
2925     return FALSE;
2928 /**
2929  * Mouseclick on node callback.
2930  */
2931 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2933    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2935     if (state & GDK_CONTROL_MASK) {
2936         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2938         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2939             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2940                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2941             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2942                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2943             } else {
2944                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2945             }
2946             sp_nodepath_update_repr(nodepath, _("Change node type"));
2947             sp_nodepath_update_statusbar(nodepath);
2949         } else { //ctrl+alt+click: delete node
2950             GList *node_to_delete = NULL;
2951             node_to_delete = g_list_append(node_to_delete, n);
2952             sp_node_delete_preserve(node_to_delete);
2953         }
2955     } else {
2956         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2957     }
2960 /**
2961  * Mouse grabbed node callback.
2962  */
2963 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2965    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2967     if (!n->selected) {
2968         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2969     }
2971     sp_nodepath_remember_origins (n->subpath->nodepath);
2974 /**
2975  * Mouse ungrabbed node callback.
2976  */
2977 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
2979    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2981    n->dragging_out = NULL;
2983    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
2986 /**
2987  * The point on a line, given by its angle, closest to the given point.
2988  * \param p  A point.
2989  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2990  * \param closest  Pointer to the point struct where the result is stored.
2991  * \todo FIXME: use dot product perhaps?
2992  */
2993 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
2995     if (a == HUGE_VAL) { // vertical
2996         *closest = NR::Point(0, (*p)[NR::Y]);
2997     } else {
2998         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
2999         (*closest)[NR::Y] = a * (*closest)[NR::X];
3000     }
3003 /**
3004  * Distance from the point to a line given by its angle.
3005  * \param p  A point.
3006  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3007  */
3008 static double point_line_distance(NR::Point *p, double a)
3010     NR::Point c;
3011     point_line_closest(p, a, &c);
3012     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]));
3015 /**
3016  * Callback for node "request" signal.
3017  * \todo fixme: This goes to "moved" event? (lauris)
3018  */
3019 static gboolean
3020 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3022     double yn, xn, yp, xp;
3023     double an, ap, na, pa;
3024     double d_an, d_ap, d_na, d_pa;
3025     gboolean collinear = FALSE;
3026     NR::Point c;
3027     NR::Point pr;
3029    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3031    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3032    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
3034        NR::Point mouse = (*p);
3036        if (!n->dragging_out) {
3037            // This is the first drag-out event; find out which handle to drag out
3038            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3039            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3041            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3042                return FALSE;
3044            Inkscape::NodePath::NodeSide *opposite;
3045            if (appr_p > appr_n) { // closer to p
3046                n->dragging_out = &n->p;
3047                opposite = &n->n;
3048                n->code = NR_CURVETO;
3049            } else if (appr_p < appr_n) { // closer to n
3050                n->dragging_out = &n->n;
3051                opposite = &n->p;
3052                n->n.other->code = NR_CURVETO;
3053            } else { // p and n nodes are the same
3054                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3055                    n->dragging_out = &n->p;
3056                    opposite = &n->n;
3057                    n->code = NR_CURVETO;
3058                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3059                    n->dragging_out = &n->n;
3060                    opposite = &n->p;
3061                    n->n.other->code = NR_CURVETO;
3062                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3063                    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);
3064                    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);
3065                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3066                        n->dragging_out = &n->n;
3067                        opposite = &n->p;
3068                        n->n.other->code = NR_CURVETO;
3069                    } else { // closer to other's n handle
3070                        n->dragging_out = &n->p;
3071                        opposite = &n->n;
3072                        n->code = NR_CURVETO;
3073                    }
3074                }
3075            }
3077            // if there's another handle, make sure the one we drag out starts parallel to it
3078            if (opposite->pos != n->pos) {
3079                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3080            }
3082            // knots might not be created yet!
3083            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3084            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3085        }
3087        // pass this on to the handle-moved callback
3088        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3089        sp_node_update_handles(n);
3090        return TRUE;
3091    }
3093     if (state & GDK_CONTROL_MASK) { // constrained motion
3095         // calculate relative distances of handles
3096         // n handle:
3097         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3098         xn = n->n.pos[NR::X] - n->pos[NR::X];
3099         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3100         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3101             if (n->n.other) { // if there is the next point
3102                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3103                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3104                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3105             }
3106         }
3107         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3108         if (yn < 0) { xn = -xn; yn = -yn; }
3110         // p handle:
3111         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3112         xp = n->p.pos[NR::X] - n->pos[NR::X];
3113         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3114         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3115             if (n->p.other) {
3116                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3117                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3118                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3119             }
3120         }
3121         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3122         if (yp < 0) { xp = -xp; yp = -yp; }
3124         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3125             // sliding on handles, only if at least one of the handles is non-vertical
3126             // (otherwise it's the same as ctrl+drag anyway)
3128             // calculate angles of the handles
3129             if (xn == 0) {
3130                 if (yn == 0) { // no handle, consider it the continuation of the other one
3131                     an = 0;
3132                     collinear = TRUE;
3133                 }
3134                 else an = 0; // vertical; set the angle to horizontal
3135             } else an = yn/xn;
3137             if (xp == 0) {
3138                 if (yp == 0) { // no handle, consider it the continuation of the other one
3139                     ap = an;
3140                 }
3141                 else ap = 0; // vertical; set the angle to horizontal
3142             } else  ap = yp/xp;
3144             if (collinear) an = ap;
3146             // angles of the perpendiculars; HUGE_VAL means vertical
3147             if (an == 0) na = HUGE_VAL; else na = -1/an;
3148             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3150             // mouse point relative to the node's original pos
3151             pr = (*p) - n->origin;
3153             // distances to the four lines (two handles and two perpendiculars)
3154             d_an = point_line_distance(&pr, an);
3155             d_na = point_line_distance(&pr, na);
3156             d_ap = point_line_distance(&pr, ap);
3157             d_pa = point_line_distance(&pr, pa);
3159             // find out which line is the closest, save its closest point in c
3160             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3161                 point_line_closest(&pr, an, &c);
3162             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3163                 point_line_closest(&pr, ap, &c);
3164             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3165                 point_line_closest(&pr, na, &c);
3166             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3167                 point_line_closest(&pr, pa, &c);
3168             }
3170             // move the node to the closest point
3171             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3172                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3173                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3175         } else {  // constraining to hor/vert
3177             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3178                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3179             } else { // snap to vert
3180                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3181             }
3182         }
3183     } else { // move freely
3184         if (state & GDK_MOD1_MASK) { // sculpt
3185             sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3186         } else {
3187             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3188                                         (*p)[NR::X] - n->pos[NR::X],
3189                                         (*p)[NR::Y] - n->pos[NR::Y],
3190                                         (state & GDK_SHIFT_MASK) == 0);
3191         }
3192     }
3194     n->subpath->nodepath->desktop->scroll_to_point(p);
3196     return TRUE;
3199 /**
3200  * Node handle clicked callback.
3201  */
3202 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3204    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3206     if (state & GDK_CONTROL_MASK) { // "delete" handle
3207         if (n->p.knot == knot) {
3208             n->p.pos = n->pos;
3209         } else if (n->n.knot == knot) {
3210             n->n.pos = n->pos;
3211         }
3212         sp_node_update_handles(n);
3213         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3214         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3215         sp_nodepath_update_statusbar(nodepath);
3217     } else { // just select or add to selection, depending in Shift
3218         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3219     }
3222 /**
3223  * Node handle grabbed callback.
3224  */
3225 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3227    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3229     if (!n->selected) {
3230         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3231     }
3233     // remember the origin point of the handle
3234     if (n->p.knot == knot) {
3235         n->p.origin_radial = n->p.pos - n->pos;
3236     } else if (n->n.knot == knot) {
3237         n->n.origin_radial = n->n.pos - n->pos;
3238     } else {
3239         g_assert_not_reached();
3240     }
3244 /**
3245  * Node handle ungrabbed callback.
3246  */
3247 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3249    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3251     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3252     if (n->p.knot == knot) {
3253         n->p.origin_radial.a = 0;
3254         sp_knot_set_position(knot, &n->p.pos, state);
3255     } else if (n->n.knot == knot) {
3256         n->n.origin_radial.a = 0;
3257         sp_knot_set_position(knot, &n->n.pos, state);
3258     } else {
3259         g_assert_not_reached();
3260     }
3262     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3265 /**
3266  * Node handle "request" signal callback.
3267  */
3268 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3270     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3272     Inkscape::NodePath::NodeSide *me, *opposite;
3273     gint which;
3274     if (n->p.knot == knot) {
3275         me = &n->p;
3276         opposite = &n->n;
3277         which = -1;
3278     } else if (n->n.knot == knot) {
3279         me = &n->n;
3280         opposite = &n->p;
3281         which = 1;
3282     } else {
3283         me = opposite = NULL;
3284         which = 0;
3285         g_assert_not_reached();
3286     }
3288     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3290     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3292     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3293         /* We are smooth node adjacent with line */
3294         NR::Point const delta = *p - n->pos;
3295         NR::Coord const len = NR::L2(delta);
3296         Inkscape::NodePath::Node *othernode = opposite->other;
3297         NR::Point const ndelta = n->pos - othernode->pos;
3298         NR::Coord const linelen = NR::L2(ndelta);
3299         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3300             NR::Coord const scal = dot(delta, ndelta) / linelen;
3301             (*p) = n->pos + (scal / linelen) * ndelta;
3302         }
3303         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
3304     } else {
3305         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
3306     }
3308     sp_node_adjust_handle(n, -which);
3310     return FALSE;
3313 /**
3314  * Node handle moved callback.
3315  */
3316 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3318    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3320    Inkscape::NodePath::NodeSide *me;
3321    Inkscape::NodePath::NodeSide *other;
3322     if (n->p.knot == knot) {
3323         me = &n->p;
3324         other = &n->n;
3325     } else if (n->n.knot == knot) {
3326         me = &n->n;
3327         other = &n->p;
3328     } else {
3329         me = NULL;
3330         other = NULL;
3331         g_assert_not_reached();
3332     }
3334     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3335     Radial rme(me->pos - n->pos);
3336     Radial rother(other->pos - n->pos);
3337     Radial rnew(*p - n->pos);
3339     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3340         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3341         /* 0 interpreted as "no snapping". */
3343         // The closest PI/snaps angle, starting from zero.
3344         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3345         if (me->origin_radial.a == HUGE_VAL) {
3346             // ortho doesn't exist: original handle was zero length.
3347             rnew.a = a_snapped;
3348         } else {
3349             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3350              * its opposite and perpendiculars). */
3351             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3353             // Snap to the closest.
3354             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3355                        ? a_snapped
3356                        : a_ortho );
3357         }
3358     }
3360     if (state & GDK_MOD1_MASK) {
3361         // lock handle length
3362         rnew.r = me->origin_radial.r;
3363     }
3365     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3366         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
3367         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3368         rother.a += rnew.a - rme.a;
3369         other->pos = NR::Point(rother) + n->pos;
3370         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3371         sp_knot_set_position(other->knot, &other->pos, 0);
3372     }
3374     me->pos = NR::Point(rnew) + n->pos;
3375     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3377     // this is what sp_knot_set_position does, but without emitting the signal:
3378     // we cannot emit a "moved" signal because we're now processing it
3379     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
3381     knot->desktop->set_coordinate_status(me->pos);
3383     update_object(n->subpath->nodepath);
3385     /* status text */
3386     SPDesktop *desktop = n->subpath->nodepath->desktop;
3387     if (!desktop) return;
3388     SPEventContext *ec = desktop->event_context;
3389     if (!ec) return;
3390     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3391     if (!mc) return;
3393     double degrees = 180 / M_PI * rnew.a;
3394     if (degrees > 180) degrees -= 360;
3395     if (degrees < -180) degrees += 360;
3396     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3397         degrees = angle_to_compass (degrees);
3399     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3401     mc->setF(Inkscape::NORMAL_MESSAGE,
3402          _("<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);
3404     g_string_free(length, TRUE);
3407 /**
3408  * Node handle event callback.
3409  */
3410 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3412     gboolean ret = FALSE;
3413     switch (event->type) {
3414         case GDK_KEY_PRESS:
3415             switch (get_group0_keyval (&event->key)) {
3416                 case GDK_space:
3417                     if (event->key.state & GDK_BUTTON1_MASK) {
3418                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3419                         stamp_repr(nodepath);
3420                         ret = TRUE;
3421                     }
3422                     break;
3423                 default:
3424                     break;
3425             }
3426             break;
3427         default:
3428             break;
3429     }
3431     return ret;
3434 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3435                                  Radial &rme, Radial &rother, gboolean const both)
3437     rme.a += angle;
3438     if ( both
3439          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3440          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3441     {
3442         rother.a += angle;
3443     }
3446 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3447                                         Radial &rme, Radial &rother, gboolean const both)
3449     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3451     gdouble r;
3452     if ( both
3453          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3454          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3455     {
3456         r = MAX(rme.r, rother.r);
3457     } else {
3458         r = rme.r;
3459     }
3461     gdouble const weird_angle = atan2(norm_angle, r);
3462 /* Bulia says norm_angle is just the visible distance that the
3463  * object's end must travel on the screen.  Left as 'angle' for want of
3464  * a better name.*/
3466     rme.a += weird_angle;
3467     if ( both
3468          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3469          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3470     {
3471         rother.a += weird_angle;
3472     }
3475 /**
3476  * Rotate one node.
3477  */
3478 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3480     Inkscape::NodePath::NodeSide *me, *other;
3481     bool both = false;
3483     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3484     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3486     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3487         me = &(n->p);
3488         other = &(n->n);
3489     } else if (!n->p.other) {
3490         me = &(n->n);
3491         other = &(n->p);
3492     } else {
3493         if (which > 0) { // right handle
3494             if (xn > xp) {
3495                 me = &(n->n);
3496                 other = &(n->p);
3497             } else {
3498                 me = &(n->p);
3499                 other = &(n->n);
3500             }
3501         } else if (which < 0){ // left handle
3502             if (xn <= xp) {
3503                 me = &(n->n);
3504                 other = &(n->p);
3505             } else {
3506                 me = &(n->p);
3507                 other = &(n->n);
3508             }
3509         } else { // both handles
3510             me = &(n->n);
3511             other = &(n->p);
3512             both = true;
3513         }
3514     }
3516     Radial rme(me->pos - n->pos);
3517     Radial rother(other->pos - n->pos);
3519     if (screen) {
3520         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3521     } else {
3522         node_rotate_one_internal (*n, angle, rme, rother, both);
3523     }
3525     me->pos = n->pos + NR::Point(rme);
3527     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3528         other->pos =  n->pos + NR::Point(rother);
3529     }
3531     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3532     // so here we just move all the knots without emitting move signals, for speed
3533     sp_node_update_handles(n, false);
3536 /**
3537  * Rotate selected nodes.
3538  */
3539 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3541     if (!nodepath || !nodepath->selected) return;
3543     if (g_list_length(nodepath->selected) == 1) {
3544        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3545         node_rotate_one (n, angle, which, screen);
3546     } else {
3547        // rotate as an object:
3549         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3550         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3551         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3552             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3553             box.expandTo (n->pos); // contain all selected nodes
3554         }
3556         gdouble rot;
3557         if (screen) {
3558             gdouble const zoom = nodepath->desktop->current_zoom();
3559             gdouble const zmove = angle / zoom;
3560             gdouble const r = NR::L2(box.max() - box.midpoint());
3561             rot = atan2(zmove, r);
3562         } else {
3563             rot = angle;
3564         }
3566         NR::Matrix t =
3567             NR::Matrix (NR::translate(-box.midpoint())) *
3568             NR::Matrix (NR::rotate(rot)) *
3569             NR::Matrix (NR::translate(box.midpoint()));
3571         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3572             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3573             n->pos *= t;
3574             n->n.pos *= t;
3575             n->p.pos *= t;
3576             sp_node_update_handles(n, false);
3577         }
3578     }
3580     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3583 /**
3584  * Scale one node.
3585  */
3586 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3588     bool both = false;
3589     Inkscape::NodePath::NodeSide *me, *other;
3591     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3592     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3594     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3595         me = &(n->p);
3596         other = &(n->n);
3597         n->code = NR_CURVETO;
3598     } else if (!n->p.other) {
3599         me = &(n->n);
3600         other = &(n->p);
3601         if (n->n.other)
3602             n->n.other->code = NR_CURVETO;
3603     } else {
3604         if (which > 0) { // right handle
3605             if (xn > xp) {
3606                 me = &(n->n);
3607                 other = &(n->p);
3608                 if (n->n.other)
3609                     n->n.other->code = NR_CURVETO;
3610             } else {
3611                 me = &(n->p);
3612                 other = &(n->n);
3613                 n->code = NR_CURVETO;
3614             }
3615         } else if (which < 0){ // left handle
3616             if (xn <= xp) {
3617                 me = &(n->n);
3618                 other = &(n->p);
3619                 if (n->n.other)
3620                     n->n.other->code = NR_CURVETO;
3621             } else {
3622                 me = &(n->p);
3623                 other = &(n->n);
3624                 n->code = NR_CURVETO;
3625             }
3626         } else { // both handles
3627             me = &(n->n);
3628             other = &(n->p);
3629             both = true;
3630             n->code = NR_CURVETO;
3631             if (n->n.other)
3632                 n->n.other->code = NR_CURVETO;
3633         }
3634     }
3636     Radial rme(me->pos - n->pos);
3637     Radial rother(other->pos - n->pos);
3639     rme.r += grow;
3640     if (rme.r < 0) rme.r = 0;
3641     if (rme.a == HUGE_VAL) {
3642         if (me->other) { // if direction is unknown, initialize it towards the next node
3643             Radial rme_next(me->other->pos - n->pos);
3644             rme.a = rme_next.a;
3645         } else { // if there's no next, initialize to 0
3646             rme.a = 0;
3647         }
3648     }
3649     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3650         rother.r += grow;
3651         if (rother.r < 0) rother.r = 0;
3652         if (rother.a == HUGE_VAL) {
3653             rother.a = rme.a + M_PI;
3654         }
3655     }
3657     me->pos = n->pos + NR::Point(rme);
3659     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3660         other->pos = n->pos + NR::Point(rother);
3661     }
3663     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3664     // so here we just move all the knots without emitting move signals, for speed
3665     sp_node_update_handles(n, false);
3668 /**
3669  * Scale selected nodes.
3670  */
3671 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3673     if (!nodepath || !nodepath->selected) return;
3675     if (g_list_length(nodepath->selected) == 1) {
3676         // scale handles of the single selected node
3677         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3678         node_scale_one (n, grow, which);
3679     } else {
3680         // scale nodes as an "object":
3682         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3683         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3684         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3685             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3686             box.expandTo (n->pos); // contain all selected nodes
3687         }
3689         double scale = (box.maxExtent() + grow)/box.maxExtent();
3691         NR::Matrix t =
3692             NR::Matrix (NR::translate(-box.midpoint())) *
3693             NR::Matrix (NR::scale(scale, scale)) *
3694             NR::Matrix (NR::translate(box.midpoint()));
3696         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3697             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3698             n->pos *= t;
3699             n->n.pos *= t;
3700             n->p.pos *= t;
3701             sp_node_update_handles(n, false);
3702         }
3703     }
3705     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
3708 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3710     if (!nodepath) return;
3711     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3714 /**
3715  * Flip selected nodes horizontally/vertically.
3716  */
3717 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3719     if (!nodepath || !nodepath->selected) return;
3721     if (g_list_length(nodepath->selected) == 1) {
3722         // flip handles of the single selected node
3723         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3724         double temp = n->p.pos[axis];
3725         n->p.pos[axis] = n->n.pos[axis];
3726         n->n.pos[axis] = temp;
3727         sp_node_update_handles(n, false);
3728     } else {
3729         // scale nodes as an "object":
3731         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3732         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3733         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3734             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3735             box.expandTo (n->pos); // contain all selected nodes
3736         }
3738         NR::Matrix t =
3739             NR::Matrix (NR::translate(-box.midpoint())) *
3740             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3741             NR::Matrix (NR::translate(box.midpoint()));
3743         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3744             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3745             n->pos *= t;
3746             n->n.pos *= t;
3747             n->p.pos *= t;
3748             sp_node_update_handles(n, false);
3749         }
3750     }
3752     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
3755 //-----------------------------------------------
3756 /**
3757  * Return new subpath under given nodepath.
3758  */
3759 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3761     g_assert(nodepath);
3762     g_assert(nodepath->desktop);
3764    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3766     s->nodepath = nodepath;
3767     s->closed = FALSE;
3768     s->nodes = NULL;
3769     s->first = NULL;
3770     s->last = NULL;
3772     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3773     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3774     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3776     return s;
3779 /**
3780  * Destroy nodes in subpath, then subpath itself.
3781  */
3782 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3784     g_assert(subpath);
3785     g_assert(subpath->nodepath);
3786     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3788     while (subpath->nodes) {
3789         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3790     }
3792     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3794     g_free(subpath);
3797 /**
3798  * Link head to tail in subpath.
3799  */
3800 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3802     g_assert(!sp->closed);
3803     g_assert(sp->last != sp->first);
3804     g_assert(sp->first->code == NR_MOVETO);
3806     sp->closed = TRUE;
3808     //Link the head to the tail
3809     sp->first->p.other = sp->last;
3810     sp->last->n.other  = sp->first;
3811     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3812     sp->first          = sp->last;
3814     //Remove the extra end node
3815     sp_nodepath_node_destroy(sp->last->n.other);
3818 /**
3819  * Open closed (loopy) subpath at node.
3820  */
3821 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3823     g_assert(sp->closed);
3824     g_assert(n->subpath == sp);
3825     g_assert(sp->first == sp->last);
3827     /* We create new startpoint, current node will become last one */
3829    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3830                                                 &n->pos, &n->pos, &n->n.pos);
3833     sp->closed        = FALSE;
3835     //Unlink to make a head and tail
3836     sp->first         = new_path;
3837     sp->last          = n;
3838     n->n.other        = NULL;
3839     new_path->p.other = NULL;
3842 /**
3843  * Returns area in triangle given by points; may be negative.
3844  */
3845 inline double
3846 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3848     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]);
3851 /**
3852  * Return new node in subpath with given properties.
3853  * \param pos Position of node.
3854  * \param ppos Handle position in previous direction
3855  * \param npos Handle position in previous direction
3856  */
3857 Inkscape::NodePath::Node *
3858 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)
3860     g_assert(sp);
3861     g_assert(sp->nodepath);
3862     g_assert(sp->nodepath->desktop);
3864     if (nodechunk == NULL)
3865         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3867     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3869     n->subpath  = sp;
3871     if (type != Inkscape::NodePath::NODE_NONE) {
3872         // use the type from sodipodi:nodetypes
3873         n->type = type;
3874     } else {
3875         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3876             // points are (almost) collinear
3877             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3878                 // endnode, or a node with a retracted handle
3879                 n->type = Inkscape::NodePath::NODE_CUSP;
3880             } else {
3881                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3882             }
3883         } else {
3884             n->type = Inkscape::NodePath::NODE_CUSP;
3885         }
3886     }
3888     n->code     = code;
3889     n->selected = FALSE;
3890     n->pos      = *pos;
3891     n->p.pos    = *ppos;
3892     n->n.pos    = *npos;
3894     n->dragging_out = NULL;
3896     Inkscape::NodePath::Node *prev;
3897     if (next) {
3898         //g_assert(g_list_find(sp->nodes, next));
3899         prev = next->p.other;
3900     } else {
3901         prev = sp->last;
3902     }
3904     if (prev)
3905         prev->n.other = n;
3906     else
3907         sp->first = n;
3909     if (next)
3910         next->p.other = n;
3911     else
3912         sp->last = n;
3914     n->p.other = prev;
3915     n->n.other = next;
3917     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"));
3918     sp_knot_set_position(n->knot, pos, 0);
3920     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3921     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3922     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3923     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3924     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3925     sp_knot_update_ctrl(n->knot);
3927     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3928     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3929     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3930     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3931     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3932     sp_knot_show(n->knot);
3934     // We only create handle knots and lines on demand
3935     n->p.knot = NULL;
3936     n->p.line = NULL;
3937     n->n.knot = NULL;
3938     n->n.line = NULL;
3940     sp->nodes = g_list_prepend(sp->nodes, n);
3942     return n;
3945 /**
3946  * Destroy node and its knots, link neighbors in subpath.
3947  */
3948 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3950     g_assert(node);
3951     g_assert(node->subpath);
3952     g_assert(SP_IS_KNOT(node->knot));
3954    Inkscape::NodePath::SubPath *sp = node->subpath;
3956     if (node->selected) { // first, deselect
3957         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3958         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3959     }
3961     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3963     g_object_unref(G_OBJECT(node->knot));
3964     if (node->p.knot)
3965         g_object_unref(G_OBJECT(node->p.knot));
3966     if (node->n.knot)
3967         g_object_unref(G_OBJECT(node->n.knot));
3969     if (node->p.line)
3970         gtk_object_destroy(GTK_OBJECT(node->p.line));
3971     if (node->n.line)
3972         gtk_object_destroy(GTK_OBJECT(node->n.line));
3974     if (sp->nodes) { // there are others nodes on the subpath
3975         if (sp->closed) {
3976             if (sp->first == node) {
3977                 g_assert(sp->last == node);
3978                 sp->first = node->n.other;
3979                 sp->last = sp->first;
3980             }
3981             node->p.other->n.other = node->n.other;
3982             node->n.other->p.other = node->p.other;
3983         } else {
3984             if (sp->first == node) {
3985                 sp->first = node->n.other;
3986                 sp->first->code = NR_MOVETO;
3987             }
3988             if (sp->last == node) sp->last = node->p.other;
3989             if (node->p.other) node->p.other->n.other = node->n.other;
3990             if (node->n.other) node->n.other->p.other = node->p.other;
3991         }
3992     } else { // this was the last node on subpath
3993         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
3994     }
3996     g_mem_chunk_free(nodechunk, node);
3999 /**
4000  * Returns one of the node's two sides.
4001  * \param which Indicates which side.
4002  * \return Pointer to previous node side if which==-1, next if which==1.
4003  */
4004 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4006     g_assert(node);
4008     switch (which) {
4009         case -1:
4010             return &node->p;
4011         case 1:
4012             return &node->n;
4013         default:
4014             break;
4015     }
4017     g_assert_not_reached();
4019     return NULL;
4022 /**
4023  * Return the other side of the node, given one of its sides.
4024  */
4025 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4027     g_assert(node);
4029     if (me == &node->p) return &node->n;
4030     if (me == &node->n) return &node->p;
4032     g_assert_not_reached();
4034     return NULL;
4037 /**
4038  * Return NRPathcode on the given side of the node.
4039  */
4040 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4042     g_assert(node);
4044     if (me == &node->p) {
4045         if (node->p.other) return (NRPathcode)node->code;
4046         return NR_MOVETO;
4047     }
4049     if (me == &node->n) {
4050         if (node->n.other) return (NRPathcode)node->n.other->code;
4051         return NR_MOVETO;
4052     }
4054     g_assert_not_reached();
4056     return NR_END;
4059 /**
4060  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
4061  */
4062 Inkscape::NodePath::Node *
4063 sp_nodepath_get_node_by_index(int index)
4065     Inkscape::NodePath::Node *e = NULL;
4067     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4068     if (!nodepath) {
4069         return e;
4070     }
4072     //find segment
4073     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4075         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4076         int n = g_list_length(sp->nodes);
4077         if (sp->closed) {
4078             n++;
4079         }
4081         //if the piece belongs to this subpath grab it
4082         //otherwise move onto the next subpath
4083         if (index < n) {
4084             e = sp->first;
4085             for (int i = 0; i < index; ++i) {
4086                 e = e->n.other;
4087             }
4088             break;
4089         } else {
4090             if (sp->closed) {
4091                 index -= (n+1);
4092             } else {
4093                 index -= n;
4094             }
4095         }
4096     }
4098     return e;
4101 /**
4102  * Returns plain text meaning of node type.
4103  */
4104 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4106     unsigned retracted = 0;
4107     bool endnode = false;
4109     for (int which = -1; which <= 1; which += 2) {
4110         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4111         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4112             retracted ++;
4113         if (!side->other)
4114             endnode = true;
4115     }
4117     if (retracted == 0) {
4118         if (endnode) {
4119                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4120                 return _("end node");
4121         } else {
4122             switch (node->type) {
4123                 case Inkscape::NodePath::NODE_CUSP:
4124                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4125                     return _("cusp");
4126                 case Inkscape::NodePath::NODE_SMOOTH:
4127                     // TRANSLATORS: "smooth" is an adjective here
4128                     return _("smooth");
4129                 case Inkscape::NodePath::NODE_SYMM:
4130                     return _("symmetric");
4131             }
4132         }
4133     } else if (retracted == 1) {
4134         if (endnode) {
4135             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4136             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4137         } else {
4138             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4139         }
4140     } else {
4141         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4142     }
4144     return NULL;
4147 /**
4148  * Handles content of statusbar as long as node tool is active.
4149  */
4150 void
4151 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
4153     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");
4154     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4156     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4157     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4158     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4159     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4161     SPDesktop *desktop = NULL;
4162     if (nodepath) {
4163         desktop = nodepath->desktop;
4164     } else {
4165         desktop = SP_ACTIVE_DESKTOP;
4166     }
4168     SPEventContext *ec = desktop->event_context;
4169     if (!ec) return;
4170     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4171     if (!mc) return;
4173     if (selected_nodes == 0) {
4174         Inkscape::Selection *sel = desktop->selection;
4175         if (!sel || sel->isEmpty()) {
4176             mc->setF(Inkscape::NORMAL_MESSAGE,
4177                      _("Select a single object to edit its nodes or handles."));
4178         } else {
4179             if (nodepath) {
4180             mc->setF(Inkscape::NORMAL_MESSAGE,
4181                      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.",
4182                               "<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.",
4183                               total_nodes),
4184                      total_nodes);
4185             } else {
4186                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4187                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4188                 } else {
4189                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4190                 }
4191             }
4192         }
4193     } else if (nodepath && selected_nodes == 1) {
4194         mc->setF(Inkscape::NORMAL_MESSAGE,
4195                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4196                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4197                           total_nodes),
4198                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4199     } else {
4200         if (selected_subpaths > 1) {
4201             mc->setF(Inkscape::NORMAL_MESSAGE,
4202                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4203                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4204                               total_nodes),
4205                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4206         } else {
4207             mc->setF(Inkscape::NORMAL_MESSAGE,
4208                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4209                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4210                               total_nodes),
4211                      selected_nodes, total_nodes, when_selected);
4212         }
4213     }
4217 /*
4218   Local Variables:
4219   mode:c++
4220   c-file-style:"stroustrup"
4221   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4222   indent-tabs-mode:nil
4223   fill-column:99
4224   End:
4225 */
4226 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :