Code

12de70eda760e5b856dcefd627f0171d09b518bc
[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_canvas_force_full_redraws(np->desktop->canvas, 2);
445     
446     sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
448     sp_curve_unref(curve);
451 /**
452  * Update XML path node with data from path object.
453  */
454 static void update_repr_internal(Inkscape::NodePath::Path *np)
456     g_assert(np);
458     Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
460     SPCurve *curve = create_curve(np);
461     gchar *typestr = create_typestr(np);
462     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
464     if (repr->attribute("d") == NULL || strcmp(svgpath, repr->attribute("d"))) { // d changed
465         np->local_change++;
466         repr->setAttribute("d", svgpath);
467     }
469     if (repr->attribute("sodipodi:nodetypes") == NULL || strcmp(typestr, repr->attribute("sodipodi:nodetypes"))) { // nodetypes changed
470         np->local_change++;
471         repr->setAttribute("sodipodi:nodetypes", typestr);
472     }
474     g_free(svgpath);
475     g_free(typestr);
476     sp_curve_unref(curve);
479 /**
480  * Update XML path node with data from path object, commit changes forever.
481  */
482 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
484     //fixme: np can be NULL, so check before proceeding
485     g_return_if_fail(np != NULL);
487     update_repr_internal(np);
488     sp_canvas_clear_forced_full_redraws(np->desktop->canvas);
489     
490     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE, 
491                      annotation);
493     if (np->livarot_path) {
494         delete np->livarot_path;
495         np->livarot_path = NULL;
496     }
498     if (np->path && SP_IS_ITEM(np->path)) {
499         np->livarot_path = Path_for_item (np->path, true, true);
500         if (np->livarot_path)
501             np->livarot_path->ConvertWithBackData(0.01);
502     }
506 /**
507  * Update XML path node with data from path object, commit changes with undo.
508  */
509 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
511     update_repr_internal(np);
512     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE, 
513                            annotation);
515     if (np->livarot_path) {
516         delete np->livarot_path;
517         np->livarot_path = NULL;
518     }
520     if (np->path && SP_IS_ITEM(np->path)) {
521         np->livarot_path = Path_for_item (np->path, true, true);
522         if (np->livarot_path)
523             np->livarot_path->ConvertWithBackData(0.01);
524     }
527 /**
528  * Make duplicate of path, replace corresponding XML node in tree, commit.
529  */
530 static void stamp_repr(Inkscape::NodePath::Path *np)
532     g_assert(np);
534     Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
535     Inkscape::XML::Node *new_repr = old_repr->duplicate();
537     // remember the position of the item
538     gint pos = old_repr->position();
539     // remember parent
540     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
542     SPCurve *curve = create_curve(np);
543     gchar *typestr = create_typestr(np);
545     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
547     new_repr->setAttribute("d", svgpath);
548     new_repr->setAttribute("sodipodi:nodetypes", typestr);
550     // add the new repr to the parent
551     parent->appendChild(new_repr);
552     // move to the saved position
553     new_repr->setPosition(pos > 0 ? pos : 0);
555     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
556                      _("Stamp"));
558     Inkscape::GC::release(new_repr);
559     g_free(svgpath);
560     g_free(typestr);
561     sp_curve_unref(curve);
564 /**
565  * Create curve from path.
566  */
567 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
569     SPCurve *curve = sp_curve_new();
571     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
572        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
573         sp_curve_moveto(curve,
574                         sp->first->pos * np->d2i);
575        Inkscape::NodePath::Node *n = sp->first->n.other;
576         while (n) {
577             NR::Point const end_pt = n->pos * np->d2i;
578             switch (n->code) {
579                 case NR_LINETO:
580                     sp_curve_lineto(curve, end_pt);
581                     break;
582                 case NR_CURVETO:
583                     sp_curve_curveto(curve,
584                                      n->p.other->n.pos * np->d2i,
585                                      n->p.pos * np->d2i,
586                                      end_pt);
587                     break;
588                 default:
589                     g_assert_not_reached();
590                     break;
591             }
592             if (n != sp->last) {
593                 n = n->n.other;
594             } else {
595                 n = NULL;
596             }
597         }
598         if (sp->closed) {
599             sp_curve_closepath(curve);
600         }
601     }
603     return curve;
606 /**
607  * Convert path type string to sodipodi:nodetypes style.
608  */
609 static gchar *create_typestr(Inkscape::NodePath::Path *np)
611     gchar *typestr = g_new(gchar, 32);
612     gint len = 32;
613     gint pos = 0;
615     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
616        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
618         if (pos >= len) {
619             typestr = g_renew(gchar, typestr, len + 32);
620             len += 32;
621         }
623         typestr[pos++] = 'c';
625        Inkscape::NodePath::Node *n;
626         n = sp->first->n.other;
627         while (n) {
628             gchar code;
630             switch (n->type) {
631                 case Inkscape::NodePath::NODE_CUSP:
632                     code = 'c';
633                     break;
634                 case Inkscape::NodePath::NODE_SMOOTH:
635                     code = 's';
636                     break;
637                 case Inkscape::NodePath::NODE_SYMM:
638                     code = 'z';
639                     break;
640                 default:
641                     g_assert_not_reached();
642                     code = '\0';
643                     break;
644             }
646             if (pos >= len) {
647                 typestr = g_renew(gchar, typestr, len + 32);
648                 len += 32;
649             }
651             typestr[pos++] = code;
653             if (n != sp->last) {
654                 n = n->n.other;
655             } else {
656                 n = NULL;
657             }
658         }
659     }
661     if (pos >= len) {
662         typestr = g_renew(gchar, typestr, len + 1);
663         len += 1;
664     }
666     typestr[pos++] = '\0';
668     return typestr;
671 /**
672  * Returns current path in context.
673  */
674 static Inkscape::NodePath::Path *sp_nodepath_current()
676     if (!SP_ACTIVE_DESKTOP) {
677         return NULL;
678     }
680     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
682     if (!SP_IS_NODE_CONTEXT(event_context)) {
683         return NULL;
684     }
686     return SP_NODE_CONTEXT(event_context)->nodepath;
691 /**
692  \brief Fills node and handle positions for three nodes, splitting line
693   marked by end at distance t.
694  */
695 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
697     g_assert(new_path != NULL);
698     g_assert(end      != NULL);
700     g_assert(end->p.other == new_path);
701    Inkscape::NodePath::Node *start = new_path->p.other;
702     g_assert(start);
704     if (end->code == NR_LINETO) {
705         new_path->type =Inkscape::NodePath::NODE_CUSP;
706         new_path->code = NR_LINETO;
707         new_path->pos  = (t * start->pos + (1 - t) * end->pos);
708     } else {
709         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
710         new_path->code = NR_CURVETO;
711         gdouble s      = 1 - t;
712         for (int dim = 0; dim < 2; dim++) {
713             NR::Coord const f000 = start->pos[dim];
714             NR::Coord const f001 = start->n.pos[dim];
715             NR::Coord const f011 = end->p.pos[dim];
716             NR::Coord const f111 = end->pos[dim];
717             NR::Coord const f00t = s * f000 + t * f001;
718             NR::Coord const f01t = s * f001 + t * f011;
719             NR::Coord const f11t = s * f011 + t * f111;
720             NR::Coord const f0tt = s * f00t + t * f01t;
721             NR::Coord const f1tt = s * f01t + t * f11t;
722             NR::Coord const fttt = s * f0tt + t * f1tt;
723             start->n.pos[dim]    = f00t;
724             new_path->p.pos[dim] = f0tt;
725             new_path->pos[dim]   = fttt;
726             new_path->n.pos[dim] = f1tt;
727             end->p.pos[dim]      = f11t;
728         }
729     }
732 /**
733  * Adds new node on direct line between two nodes, activates handles of all
734  * three nodes.
735  */
736 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
738     g_assert(end);
739     g_assert(end->subpath);
740     g_assert(g_list_find(end->subpath->nodes, end));
742    Inkscape::NodePath::Node *start = end->p.other;
743     g_assert( start->n.other == end );
744    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
745                                                end,
746                                               Inkscape::NodePath::NODE_SMOOTH,
747                                                (NRPathcode)end->code,
748                                                &start->pos, &start->pos, &start->n.pos);
749     sp_nodepath_line_midpoint(newnode, end, t);
751     sp_node_update_handles(start);
752     sp_node_update_handles(newnode);
753     sp_node_update_handles(end);
755     return newnode;
758 /**
759 \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
760 */
761 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
763     g_assert(node);
764     g_assert(node->subpath);
765     g_assert(g_list_find(node->subpath->nodes, node));
767    Inkscape::NodePath::SubPath *sp = node->subpath;
768     Inkscape::NodePath::Path *np    = sp->nodepath;
770     if (sp->closed) {
771         sp_nodepath_subpath_open(sp, node);
772         return sp->first;
773     } else {
774         // no break for end nodes
775         if (node == sp->first) return NULL;
776         if (node == sp->last ) return NULL;
778         // create a new subpath
779        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
781         // duplicate the break node as start of the new subpath
782        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
784         while (node->n.other) { // copy the remaining nodes into the new subpath
785            Inkscape::NodePath::Node *n  = node->n.other;
786            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);
787             if (n->selected) {
788                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
789             }
790             sp_nodepath_node_destroy(n); // remove the point on the original subpath
791         }
793         return newnode;
794     }
797 /**
798  * Duplicate node and connect to neighbours.
799  */
800 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
802     g_assert(node);
803     g_assert(node->subpath);
804     g_assert(g_list_find(node->subpath->nodes, node));
806    Inkscape::NodePath::SubPath *sp = node->subpath;
808     NRPathcode code = (NRPathcode) node->code;
809     if (code == NR_MOVETO) { // if node is the endnode,
810         node->code = NR_LINETO; // new one is inserted before it, so change that to line
811     }
813     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
815     if (!node->n.other || !node->p.other) // if node is an endnode, select it
816         return node;
817     else
818         return newnode; // otherwise select the newly created node
821 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
823     node->p.pos = (node->pos + (node->pos - node->n.pos));
826 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
828     node->n.pos = (node->pos + (node->pos - node->p.pos));
831 /**
832  * Change line type at node, with side effects on neighbours.
833  */
834 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
836     g_assert(end);
837     g_assert(end->subpath);
838     g_assert(end->p.other);
840     if (end->code == static_cast< guint > ( code ) )
841         return;
843    Inkscape::NodePath::Node *start = end->p.other;
845     end->code = code;
847     if (code == NR_LINETO) {
848         if (start->code == NR_LINETO) start->type =Inkscape::NodePath::NODE_CUSP;
849         if (end->n.other) {
850             if (end->n.other->code == NR_LINETO) end->type =Inkscape::NodePath::NODE_CUSP;
851         }
852         sp_node_adjust_handle(start, -1);
853         sp_node_adjust_handle(end, 1);
854     } else {
855         NR::Point delta = end->pos - start->pos;
856         start->n.pos = start->pos + delta / 3;
857         end->p.pos = end->pos - delta / 3;
858         sp_node_adjust_handle(start, 1);
859         sp_node_adjust_handle(end, -1);
860     }
862     sp_node_update_handles(start);
863     sp_node_update_handles(end);
866 /**
867  * Change node type, and its handles accordingly.
868  */
869 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
871     g_assert(node);
872     g_assert(node->subpath);
874     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
875         return node;
877     if ((node->p.other != NULL) && (node->n.other != NULL)) {
878         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
879             type =Inkscape::NodePath::NODE_CUSP;
880         }
881     }
883     node->type = type;
885     if (node->type == Inkscape::NodePath::NODE_CUSP) {
886         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
887         node->knot->setSize (node->selected? 11 : 9);
888         sp_knot_update_ctrl(node->knot);
889     } else {
890         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
891         node->knot->setSize (node->selected? 9 : 7);
892         sp_knot_update_ctrl(node->knot);
893     }
895     // if one of handles is mouseovered, preserve its position
896     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
897         sp_node_adjust_handle(node, 1);
898     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
899         sp_node_adjust_handle(node, -1);
900     } else {
901         sp_node_adjust_handles(node);
902     }
904     sp_node_update_handles(node);
906     sp_nodepath_update_statusbar(node->subpath->nodepath);
908     return node;
911 /**
912  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
913  * adjacent segments from lines to curves.
914 */
915 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
917     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
918         if ((node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos)) {
919             // convert adjacent segment BEFORE to curve
920             node->code = NR_CURVETO;
921             NR::Point delta;
922             if (node->n.other != NULL)
923                 delta = node->n.other->pos - node->p.other->pos;
924             else
925                 delta = node->pos - node->p.other->pos;
926             node->p.pos = node->pos - delta / 4;
927             sp_node_update_handles(node);
928         }
930         if ((node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos)) {
931             // convert adjacent segment AFTER to curve
932             node->n.other->code = NR_CURVETO;
933             NR::Point delta;
934             if (node->p.other != NULL)
935                 delta = node->p.other->pos - node->n.other->pos;
936             else
937                 delta = node->pos - node->n.other->pos;
938             node->n.pos = node->pos - delta / 4;
939             sp_node_update_handles(node);
940         }
941     }
943     sp_nodepath_set_node_type (node, type);
946 /**
947  * Move node to point, and adjust its and neighbouring handles.
948  */
949 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
951     NR::Point delta = p - node->pos;
952     node->pos = p;
954     node->p.pos += delta;
955     node->n.pos += delta;
957     if (node->p.other) {
958         if (node->code == NR_LINETO) {
959             sp_node_adjust_handle(node, 1);
960             sp_node_adjust_handle(node->p.other, -1);
961         }
962     }
963     if (node->n.other) {
964         if (node->n.other->code == NR_LINETO) {
965             sp_node_adjust_handle(node, -1);
966             sp_node_adjust_handle(node->n.other, 1);
967         }
968     }
970     // this function is only called from batch movers that will update display at the end
971     // themselves, so here we just move all the knots without emitting move signals, for speed
972     sp_node_update_handles(node, false);
975 /**
976  * Call sp_node_moveto() for node selection and handle possible snapping.
977  */
978 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
979                                             bool const snap = true)
981     NR::Coord best = NR_HUGE;
982     NR::Point delta(dx, dy);
983     NR::Point best_pt = delta;
985     if (snap) {
986         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
987         
988         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
989             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
990             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAP_POINT, n->pos + delta, NULL);
991             if (s.getDistance() < best) {
992                 best = s.getDistance();
993                 best_pt = s.getPoint() - n->pos;
994             }
995         }
996     }
998     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
999         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1000         sp_node_moveto(n, n->pos + best_pt);
1001     }
1003     // do not update repr here so that node dragging is acceptably fast
1004     update_object(nodepath);
1007 /**
1008 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1009 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1010 near x = 0.
1011  */
1012 double
1013 sculpt_profile (double x, double alpha, guint profile)
1015     if (x >= 1)
1016         return 0;
1017     if (x <= 0)
1018         return 1;
1020     switch (profile) {
1021         case SCULPT_PROFILE_LINEAR:
1022         return 1 - x;
1023         case SCULPT_PROFILE_BELL:
1024         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1025         case SCULPT_PROFILE_ELLIPTIC:
1026         return sqrt(1 - x*x);
1027     }
1029     return 1;
1032 double
1033 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1035     // extremely primitive for now, don't have time to look for the real one
1036     double lower = NR::L2(b - a);
1037     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1038     return (lower + upper)/2;
1041 void
1042 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1044     n->pos = n->origin + delta;
1045     n->n.pos = n->n.origin + delta_n;
1046     n->p.pos = n->p.origin + delta_p;
1047     sp_node_adjust_handles(n);
1048     sp_node_update_handles(n, false);
1051 /**
1052  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1053  * on how far they are from the dragged node n.
1054  */
1055 static void 
1056 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1058     g_assert (n);
1059     g_assert (nodepath);
1060     g_assert (n->subpath->nodepath == nodepath);
1062     double pressure = n->knot->pressure;
1063     if (pressure == 0)
1064         pressure = 0.5; // default
1065     pressure = CLAMP (pressure, 0.2, 0.8);
1067     // map pressure to alpha = 1/5 ... 5
1068     double alpha = 1 - 2 * fabs(pressure - 0.5);
1069     if (pressure > 0.5)
1070         alpha = 1/alpha;
1072     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1074     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1075         // Only one subpath has selected nodes:
1076         // use linear mode, where the distance from n to node being dragged is calculated along the path
1078         double n_sel_range = 0, p_sel_range = 0;
1079         guint n_nodes = 0, p_nodes = 0;
1080         guint n_sel_nodes = 0, p_sel_nodes = 0;
1082         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1083         {
1084             double n_range = 0, p_range = 0;
1085             bool n_going = true, p_going = true;
1086             Inkscape::NodePath::Node *n_node = n;
1087             Inkscape::NodePath::Node *p_node = n;
1088             do {
1089                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1090                 if (n_node && n_going)
1091                     n_node = n_node->n.other;
1092                 if (n_node == NULL) {
1093                     n_going = false;
1094                 } else {
1095                     n_nodes ++;
1096                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1097                     if (n_node->selected) {
1098                         n_sel_nodes ++;
1099                         n_sel_range = n_range;
1100                     }
1101                     if (n_node == p_node) {
1102                         n_going = false;
1103                         p_going = false;
1104                     }
1105                 }
1106                 if (p_node && p_going)
1107                     p_node = p_node->p.other;
1108                 if (p_node == NULL) {
1109                     p_going = false;
1110                 } else {
1111                     p_nodes ++;
1112                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1113                     if (p_node->selected) {
1114                         p_sel_nodes ++;
1115                         p_sel_range = p_range;
1116                     }
1117                     if (p_node == n_node) {
1118                         n_going = false;
1119                         p_going = false;
1120                     }
1121                 }
1122             } while (n_going || p_going);
1123         }
1125         // Second pass: actually move nodes in this subpath
1126         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1127         {
1128             double n_range = 0, p_range = 0;
1129             bool n_going = true, p_going = true;
1130             Inkscape::NodePath::Node *n_node = n;
1131             Inkscape::NodePath::Node *p_node = n;
1132             do {
1133                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1134                 if (n_node && n_going)
1135                     n_node = n_node->n.other;
1136                 if (n_node == NULL) {
1137                     n_going = false;
1138                 } else {
1139                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1140                     if (n_node->selected) {
1141                         sp_nodepath_move_node_and_handles (n_node, 
1142                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1143                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1144                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1145                     }
1146                     if (n_node == p_node) {
1147                         n_going = false;
1148                         p_going = false;
1149                     }
1150                 }
1151                 if (p_node && p_going)
1152                     p_node = p_node->p.other;
1153                 if (p_node == NULL) {
1154                     p_going = false;
1155                 } else {
1156                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1157                     if (p_node->selected) {
1158                         sp_nodepath_move_node_and_handles (p_node, 
1159                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1160                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1161                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1162                     }
1163                     if (p_node == n_node) {
1164                         n_going = false;
1165                         p_going = false;
1166                     }
1167                 }
1168             } while (n_going || p_going);
1169         }
1171     } else {
1172         // Multiple subpaths have selected nodes:
1173         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1174         // TODO: correct these distances taking into account their angle relative to the bisector, so as to 
1175         // fix the pear-like shape when sculpting e.g. a ring
1177         // First pass: calculate range
1178         gdouble direct_range = 0;
1179         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1180             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1181             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1182                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1183                 if (node->selected) {
1184                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1185                 }
1186             }
1187         }
1189         // Second pass: actually move nodes
1190         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1191             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1192             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1193                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1194                 if (node->selected) {
1195                     if (direct_range > 1e-6) {
1196                         sp_nodepath_move_node_and_handles (node,
1197                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1198                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1199                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1200                     } else {
1201                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1202                     }
1204                 }
1205             }
1206         }
1207     }
1209     // do not update repr here so that node dragging is acceptably fast
1210     update_object(nodepath);
1214 /**
1215  * Move node selection to point, adjust its and neighbouring handles,
1216  * handle possible snapping, and commit the change with possible undo.
1217  */
1218 void
1219 sp_node_selected_move(gdouble dx, gdouble dy)
1221     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1222     if (!nodepath) return;
1224     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1226     if (dx == 0) {
1227         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1228     } else if (dy == 0) {
1229         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1230     } else {
1231         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1232     }
1235 /**
1236  * Move node selection off screen and commit the change.
1237  */
1238 void
1239 sp_node_selected_move_screen(gdouble dx, gdouble dy)
1241     // borrowed from sp_selection_move_screen in selection-chemistry.c
1242     // we find out the current zoom factor and divide deltas by it
1243     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1245     gdouble zoom = desktop->current_zoom();
1246     gdouble zdx = dx / zoom;
1247     gdouble zdy = dy / zoom;
1249     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1250     if (!nodepath) return;
1252     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1254     if (dx == 0) {
1255         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1256     } else if (dy == 0) {
1257         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1258     } else {
1259         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1260     }
1263 /** If they don't yet exist, creates knot and line for the given side of the node */
1264 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1266     if (!side->knot) {
1267         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"));
1269         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1270         side->knot->setSize (7);
1271         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1272         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1273         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1274         sp_knot_update_ctrl(side->knot);
1276         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1277         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1278         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1279         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1280         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1281         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1282     }
1284     if (!side->line) {
1285         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1286                                         SP_TYPE_CTRLLINE, NULL);
1287     }
1290 /**
1291  * Ensure the given handle of the node is visible/invisible, update its screen position
1292  */
1293 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1295     g_assert(node != NULL);
1297    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1298     NRPathcode code = sp_node_path_code_from_side(node, side);
1300     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1302     if (show_handle) {
1303         if (!side->knot) { // No handle knot at all
1304             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1305             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1306             side->knot->pos = side->pos;
1307             if (side->knot->item) 
1308                 SP_CTRL(side->knot->item)->moveto(side->pos);
1309             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1310             sp_knot_show(side->knot);
1311         } else {
1312             if (side->knot->pos != side->pos) { // only if it's really moved
1313                 if (fire_move_signals) {
1314                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1315                 } else {
1316                     sp_knot_moveto(side->knot, &side->pos);
1317                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1318                 }
1319             }
1320             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1321                 sp_knot_show(side->knot);
1322             }
1323         }
1324         sp_canvas_item_show(side->line);
1325     } else {
1326         if (side->knot) {
1327             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1328                 sp_knot_hide(side->knot);
1329             }
1330         }
1331         if (side->line) {
1332             sp_canvas_item_hide(side->line);
1333         }
1334     }
1337 /**
1338  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1339  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1340  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1341  * updated; otherwise, just move the knots silently (used in batch moves).
1342  */
1343 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1345     g_assert(node != NULL);
1347     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1348         sp_knot_show(node->knot);
1349     }
1351     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1352         if (fire_move_signals)
1353             sp_knot_set_position(node->knot, &node->pos, 0);
1354         else 
1355             sp_knot_moveto(node->knot, &node->pos);
1356     }
1358     gboolean show_handles = node->selected;
1359     if (node->p.other != NULL) {
1360         if (node->p.other->selected) show_handles = TRUE;
1361     }
1362     if (node->n.other != NULL) {
1363         if (node->n.other->selected) show_handles = TRUE;
1364     }
1366     if (node->subpath->nodepath->show_handles == false)
1367         show_handles = FALSE;
1369     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1370     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1373 /**
1374  * Call sp_node_update_handles() for all nodes on subpath.
1375  */
1376 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1378     g_assert(subpath != NULL);
1380     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1381         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1382     }
1385 /**
1386  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1387  */
1388 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1390     g_assert(nodepath != NULL);
1392     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1393         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1394     }
1397 void
1398 sp_nodepath_show_handles(bool show)
1400     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1401     if (nodepath == NULL) return;
1403     nodepath->show_handles = show;
1404     sp_nodepath_update_handles(nodepath);
1407 /**
1408  * Adds all selected nodes in nodepath to list.
1409  */
1410 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1412     StlConv<Node *>::list(l, selected);
1413 /// \todo this adds a copying, rework when the selection becomes a stl list
1416 /**
1417  * Align selected nodes on the specified axis.
1418  */
1419 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1421     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1422         return;
1423     }
1425     if ( !nodepath->selected->next ) { // only one node selected
1426         return;
1427     }
1428    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1429     NR::Point dest(pNode->pos);
1430     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1431         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1432         if (pNode) {
1433             dest[axis] = pNode->pos[axis];
1434             sp_node_moveto(pNode, dest);
1435         }
1436     }
1438     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1441 /// Helper struct.
1442 struct NodeSort
1444    Inkscape::NodePath::Node *_node;
1445     NR::Coord _coord;
1446     /// \todo use vectorof pointers instead of calling copy ctor
1447     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1448         _node(node), _coord(node->pos[axis])
1449     {}
1451 };
1453 static bool operator<(NodeSort const &a, NodeSort const &b)
1455     return (a._coord < b._coord);
1458 /**
1459  * Distribute selected nodes on the specified axis.
1460  */
1461 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1463     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1464         return;
1465     }
1467     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1468         return;
1469     }
1471    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1472     std::vector<NodeSort> sorted;
1473     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1474         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1475         if (pNode) {
1476             NodeSort n(pNode, axis);
1477             sorted.push_back(n);
1478             //dest[axis] = pNode->pos[axis];
1479             //sp_node_moveto(pNode, dest);
1480         }
1481     }
1482     std::sort(sorted.begin(), sorted.end());
1483     unsigned int len = sorted.size();
1484     //overall bboxes span
1485     float dist = (sorted.back()._coord -
1486                   sorted.front()._coord);
1487     //new distance between each bbox
1488     float step = (dist) / (len - 1);
1489     float pos = sorted.front()._coord;
1490     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1491           it < sorted.end();
1492           it ++ )
1493     {
1494         NR::Point dest((*it)._node->pos);
1495         dest[axis] = pos;
1496         sp_node_moveto((*it)._node, dest);
1497         pos += step;
1498     }
1500     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1504 /**
1505  * Call sp_nodepath_line_add_node() for all selected segments.
1506  */
1507 void
1508 sp_node_selected_add_node(void)
1510     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1511     if (!nodepath) {
1512         return;
1513     }
1515     GList *nl = NULL;
1517     int n_added = 0;
1519     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1520        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1521         g_assert(t->selected);
1522         if (t->p.other && t->p.other->selected) {
1523             nl = g_list_prepend(nl, t);
1524         }
1525     }
1527     while (nl) {
1528        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1529        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1530        sp_nodepath_node_select(n, TRUE, FALSE);
1531        n_added ++;
1532        nl = g_list_remove(nl, t);
1533     }
1535     /** \todo fixme: adjust ? */
1536     sp_nodepath_update_handles(nodepath);
1538     if (n_added > 1) {
1539         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1540     } else if (n_added > 0) {
1541         sp_nodepath_update_repr(nodepath, _("Add node"));
1542     }
1544     sp_nodepath_update_statusbar(nodepath);
1547 /**
1548  * Select segment nearest to point
1549  */
1550 void
1551 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1553     if (!nodepath) {
1554         return;
1555     }
1557     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1559     //find segment to segment
1560     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1562     //fixme: this can return NULL, so check before proceeding.
1563     g_return_if_fail(e != NULL);
1564     
1565     gboolean force = FALSE;
1566     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1567         force = TRUE;
1568     }
1569     sp_nodepath_node_select(e, (gboolean) toggle, force);
1570     if (e->p.other)
1571         sp_nodepath_node_select(e->p.other, TRUE, force);
1573     sp_nodepath_update_handles(nodepath);
1575     sp_nodepath_update_statusbar(nodepath);
1578 /**
1579  * Add a node nearest to point
1580  */
1581 void
1582 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1584     if (!nodepath) {
1585         return;
1586     }
1588     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1590     //find segment to split
1591     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1593     //don't know why but t seems to flip for lines
1594     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1595         position.t = 1.0 - position.t;
1596     }
1597     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1598     sp_nodepath_node_select(n, FALSE, TRUE);
1600     /* fixme: adjust ? */
1601     sp_nodepath_update_handles(nodepath);
1603     sp_nodepath_update_repr(nodepath, _("Add node"));
1605     sp_nodepath_update_statusbar(nodepath);
1608 /*
1609  * Adjusts a segment so that t moves by a certain delta for dragging
1610  * converts lines to curves
1611  *
1612  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1613  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1614  */
1615 void
1616 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta)
1618     //fixme: e and e->p can be NULL, so check for those before proceeding
1619     g_return_if_fail(e != NULL);
1620     g_return_if_fail(&e->p != NULL);
1621     
1622     /* feel good is an arbitrary parameter that distributes the delta between handles
1623      * if t of the drag point is less than 1/6 distance form the endpoint only
1624      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1625      */
1626     double feel_good;
1627     if (t <= 1.0 / 6.0)
1628         feel_good = 0;
1629     else if (t <= 0.5)
1630         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1631     else if (t <= 5.0 / 6.0)
1632         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1633     else
1634         feel_good = 1;
1636     //if we're dragging a line convert it to a curve
1637     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1638         sp_nodepath_set_line_type(e, NR_CURVETO);
1639     }
1641     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1642     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1643     e->p.other->n.pos += offsetcoord0;
1644     e->p.pos += offsetcoord1;
1646     // adjust handles of adjacent nodes where necessary
1647     sp_node_adjust_handle(e,1);
1648     sp_node_adjust_handle(e->p.other,-1);
1650     sp_nodepath_update_handles(e->subpath->nodepath);
1652     update_object(e->subpath->nodepath);
1654     sp_nodepath_update_statusbar(e->subpath->nodepath);
1658 /**
1659  * Call sp_nodepath_break() for all selected segments.
1660  */
1661 void sp_node_selected_break()
1663     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1664     if (!nodepath) return;
1666     GList *temp = NULL;
1667     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1668        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1669        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1670         if (nn == NULL) continue; // no break, no new node
1671         temp = g_list_prepend(temp, nn);
1672     }
1674     if (temp) {
1675         sp_nodepath_deselect(nodepath);
1676     }
1677     for (GList *l = temp; l != NULL; l = l->next) {
1678         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1679     }
1681     sp_nodepath_update_handles(nodepath);
1683     sp_nodepath_update_repr(nodepath, _("Break path"));
1686 /**
1687  * Duplicate the selected node(s).
1688  */
1689 void sp_node_selected_duplicate()
1691     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1692     if (!nodepath) {
1693         return;
1694     }
1696     GList *temp = NULL;
1697     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1698        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1699        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1700         if (nn == NULL) continue; // could not duplicate
1701         temp = g_list_prepend(temp, nn);
1702     }
1704     if (temp) {
1705         sp_nodepath_deselect(nodepath);
1706     }
1707     for (GList *l = temp; l != NULL; l = l->next) {
1708         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1709     }
1711     sp_nodepath_update_handles(nodepath);
1713     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1716 /**
1717  *  Join two nodes by merging them into one.
1718  */
1719 void sp_node_selected_join()
1721     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1722     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1724     if (g_list_length(nodepath->selected) != 2) {
1725         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1726         return;
1727     }
1729    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1730    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1732     g_assert(a != b);
1733     g_assert(a->p.other || a->n.other);
1734     g_assert(b->p.other || b->n.other);
1736     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1737         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1738         return;
1739     }
1741     /* a and b are endpoints */
1743     NR::Point c;
1744     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1745         c = a->pos;
1746     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1747         c = b->pos;
1748     } else {
1749         c = (a->pos + b->pos) / 2;
1750     }
1752     if (a->subpath == b->subpath) {
1753        Inkscape::NodePath::SubPath *sp = a->subpath;
1754         sp_nodepath_subpath_close(sp);
1755         sp_node_moveto (sp->first, c);
1757         sp_nodepath_update_handles(sp->nodepath);
1758         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1759         return;
1760     }
1762     /* a and b are separate subpaths */
1763    Inkscape::NodePath::SubPath *sa = a->subpath;
1764    Inkscape::NodePath::SubPath *sb = b->subpath;
1765     NR::Point p;
1766    Inkscape::NodePath::Node *n;
1767     NRPathcode code;
1768     if (a == sa->first) {
1769         p = sa->first->n.pos;
1770         code = (NRPathcode)sa->first->n.other->code;
1771        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1772         n = sa->last;
1773         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1774         n = n->p.other;
1775         while (n) {
1776             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1777             n = n->p.other;
1778             if (n == sa->first) n = NULL;
1779         }
1780         sp_nodepath_subpath_destroy(sa);
1781         sa = t;
1782     } else if (a == sa->last) {
1783         p = sa->last->p.pos;
1784         code = (NRPathcode)sa->last->code;
1785         sp_nodepath_node_destroy(sa->last);
1786     } else {
1787         code = NR_END;
1788         g_assert_not_reached();
1789     }
1791     if (b == sb->first) {
1792         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1793         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1794             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1795         }
1796     } else if (b == sb->last) {
1797         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1798         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1799             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1800         }
1801     } else {
1802         g_assert_not_reached();
1803     }
1804     /* and now destroy sb */
1806     sp_nodepath_subpath_destroy(sb);
1808     sp_nodepath_update_handles(sa->nodepath);
1810     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1812     sp_nodepath_update_statusbar(nodepath);
1815 /**
1816  *  Join two nodes by adding a segment between them.
1817  */
1818 void sp_node_selected_join_segment()
1820     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1821     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1823     if (g_list_length(nodepath->selected) != 2) {
1824         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1825         return;
1826     }
1828    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1829    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1831     g_assert(a != b);
1832     g_assert(a->p.other || a->n.other);
1833     g_assert(b->p.other || b->n.other);
1835     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1836         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1837         return;
1838     }
1840     if (a->subpath == b->subpath) {
1841        Inkscape::NodePath::SubPath *sp = a->subpath;
1843         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1844         sp->closed = TRUE;
1846         sp->first->p.other = sp->last;
1847         sp->last->n.other  = sp->first;
1849         sp_node_handle_mirror_p_to_n(sp->last);
1850         sp_node_handle_mirror_n_to_p(sp->first);
1852         sp->first->code = sp->last->code;
1853         sp->first       = sp->last;
1855         sp_nodepath_update_handles(sp->nodepath);
1857         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
1859         return;
1860     }
1862     /* a and b are separate subpaths */
1863    Inkscape::NodePath::SubPath *sa = a->subpath;
1864    Inkscape::NodePath::SubPath *sb = b->subpath;
1866    Inkscape::NodePath::Node *n;
1867     NR::Point p;
1868     NRPathcode code;
1869     if (a == sa->first) {
1870         code = (NRPathcode) sa->first->n.other->code;
1871        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1872         n = sa->last;
1873         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1874         for (n = n->p.other; n != NULL; n = n->p.other) {
1875             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1876         }
1877         sp_nodepath_subpath_destroy(sa);
1878         sa = t;
1879     } else if (a == sa->last) {
1880         code = (NRPathcode)sa->last->code;
1881     } else {
1882         code = NR_END;
1883         g_assert_not_reached();
1884     }
1886     if (b == sb->first) {
1887         n = sb->first;
1888         sp_node_handle_mirror_p_to_n(sa->last);
1889         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1890         sp_node_handle_mirror_n_to_p(sa->last);
1891         for (n = n->n.other; n != NULL; n = n->n.other) {
1892             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1893         }
1894     } else if (b == sb->last) {
1895         n = sb->last;
1896         sp_node_handle_mirror_p_to_n(sa->last);
1897         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1898         sp_node_handle_mirror_n_to_p(sa->last);
1899         for (n = n->p.other; n != NULL; n = n->p.other) {
1900             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1901         }
1902     } else {
1903         g_assert_not_reached();
1904     }
1905     /* and now destroy sb */
1907     sp_nodepath_subpath_destroy(sb);
1909     sp_nodepath_update_handles(sa->nodepath);
1911     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
1914 /**
1915  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1916  */
1917 void sp_node_delete_preserve(GList *nodes_to_delete)
1919     GSList *nodepaths = NULL;
1920     
1921     while (nodes_to_delete) {
1922         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1923         Inkscape::NodePath::SubPath *sp = node->subpath;
1924         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1925         Inkscape::NodePath::Node *sample_cursor = NULL;
1926         Inkscape::NodePath::Node *sample_end = NULL;
1927         Inkscape::NodePath::Node *delete_cursor = node;
1928         bool just_delete = false;
1929         
1930         //find the start of this contiguous selection
1931         //move left to the first node that is not selected
1932         //or the start of the non-closed path
1933         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1934             delete_cursor = curr;
1935         }
1937         //just delete at the beginning of an open path
1938         if (!delete_cursor->p.other) {
1939             sample_cursor = delete_cursor;
1940             just_delete = true;
1941         } else {
1942             sample_cursor = delete_cursor->p.other;
1943         }
1944         
1945         //calculate points for each segment
1946         int rate = 5;
1947         float period = 1.0 / rate;
1948         std::vector<NR::Point> data;
1949         if (!just_delete) {
1950             data.push_back(sample_cursor->pos);
1951             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1952                 //just delete at the end of an open path
1953                 if (!sp->closed && curr == sp->last) {
1954                     just_delete = true;
1955                     break;
1956                 }
1957                 
1958                 //sample points on the contiguous selected segment
1959                 NR::Point *bez;
1960                 bez = new NR::Point [4];
1961                 bez[0] = curr->pos;
1962                 bez[1] = curr->n.pos;
1963                 bez[2] = curr->n.other->p.pos;
1964                 bez[3] = curr->n.other->pos;
1965                 for (int i=1; i<rate; i++) {
1966                     gdouble t = i * period;
1967                     NR::Point p = bezier_pt(3, bez, t);
1968                     data.push_back(p);
1969                 }
1970                 data.push_back(curr->n.other->pos);
1972                 sample_end = curr->n.other;
1973                 //break if we've come full circle or hit the end of the selection
1974                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
1975                     break;
1976                 }
1977             }
1978         }
1980         if (!just_delete) {
1981             //calculate the best fitting single segment and adjust the endpoints
1982             NR::Point *adata;
1983             adata = new NR::Point [data.size()];
1984             copy(data.begin(), data.end(), adata);
1985             
1986             NR::Point *bez;
1987             bez = new NR::Point [4];
1988             //would decreasing error create a better fitting approximation?
1989             gdouble error = 1.0;
1990             gint ret;
1991             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
1993             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
1994             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
1995             //the resulting nodes behave as expected.
1996             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
1997             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
1998             
1999             //adjust endpoints
2000             sample_cursor->n.pos = bez[1];
2001             sample_end->p.pos = bez[2];
2002         }
2003        
2004         //destroy this contiguous selection
2005         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2006             Inkscape::NodePath::Node *temp = delete_cursor;
2007             if (delete_cursor->n.other == delete_cursor) {
2008                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2009                 delete_cursor = NULL; 
2010             } else {
2011                 delete_cursor = delete_cursor->n.other;
2012             }
2013             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2014             sp_nodepath_node_destroy(temp);
2015         }
2017         sp_nodepath_update_handles(nodepath);
2019         if (!g_slist_find(nodepaths, nodepath))
2020             nodepaths = g_slist_prepend (nodepaths, nodepath);
2021     }
2023     for (GSList *i = nodepaths; i; i = i->next) {
2024         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2025         // different nodepaths will give us one undo event per nodepath
2026         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2028         // if the entire nodepath is removed, delete the selected object.
2029         if (nodepath->subpaths == NULL ||
2030             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2031             //at least 2
2032             sp_nodepath_get_node_count(nodepath) < 2) {
2033             SPDocument *document = sp_desktop_document (nodepath->desktop);
2034             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2035             //delete this nodepath's object, not the entire selection! (though at this time, this
2036             //does not matter)
2037             sp_selection_delete();
2038             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2039                               _("Delete nodes"));
2040         } else {
2041             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2042             sp_nodepath_update_statusbar(nodepath);
2043         }
2044     }
2046     g_slist_free (nodepaths);
2049 /**
2050  * Delete one or more selected nodes.
2051  */
2052 void sp_node_selected_delete()
2054     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2055     if (!nodepath) return;
2056     if (!nodepath->selected) return;
2058     /** \todo fixme: do it the right way */
2059     while (nodepath->selected) {
2060        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2061         sp_nodepath_node_destroy(node);
2062     }
2065     //clean up the nodepath (such as for trivial subpaths)
2066     sp_nodepath_cleanup(nodepath);
2068     sp_nodepath_update_handles(nodepath);
2070     // if the entire nodepath is removed, delete the selected object.
2071     if (nodepath->subpaths == NULL ||
2072         sp_nodepath_get_node_count(nodepath) < 2) {
2073         SPDocument *document = sp_desktop_document (nodepath->desktop);
2074         sp_selection_delete();
2075         sp_document_done (document, SP_VERB_CONTEXT_NODE, 
2076                           _("Delete nodes"));
2077         return;
2078     }
2080     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2082     sp_nodepath_update_statusbar(nodepath);
2085 /**
2086  * Delete one or more segments between two selected nodes.
2087  * This is the code for 'split'.
2088  */
2089 void
2090 sp_node_selected_delete_segment(void)
2092    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2093    Inkscape::NodePath::Node *curr, *next;     //Iterators
2095     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2096     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2098     if (g_list_length(nodepath->selected) != 2) {
2099         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2100                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2101         return;
2102     }
2104     //Selected nodes, not inclusive
2105    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2106    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2108     if ( ( a==b)                       ||  //same node
2109          (a->subpath  != b->subpath )  ||  //not the same path
2110          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2111          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2112     {
2113         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2114                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2115         return;
2116     }
2118     //###########################################
2119     //# BEGIN EDITS
2120     //###########################################
2121     //##################################
2122     //# CLOSED PATH
2123     //##################################
2124     if (a->subpath->closed) {
2127         gboolean reversed = FALSE;
2129         //Since we can go in a circle, we need to find the shorter distance.
2130         //  a->b or b->a
2131         start = end = NULL;
2132         int distance    = 0;
2133         int minDistance = 0;
2134         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2135             if (curr==b) {
2136                 //printf("a to b:%d\n", distance);
2137                 start = a;//go from a to b
2138                 end   = b;
2139                 minDistance = distance;
2140                 //printf("A to B :\n");
2141                 break;
2142             }
2143             distance++;
2144         }
2146         //try again, the other direction
2147         distance = 0;
2148         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2149             if (curr==a) {
2150                 //printf("b to a:%d\n", distance);
2151                 if (distance < minDistance) {
2152                     start    = b;  //we go from b to a
2153                     end      = a;
2154                     reversed = TRUE;
2155                     //printf("B to A\n");
2156                 }
2157                 break;
2158             }
2159             distance++;
2160         }
2163         //Copy everything from 'end' to 'start' to a new subpath
2164        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2165         for (curr=end ; curr ; curr=curr->n.other) {
2166             NRPathcode code = (NRPathcode) curr->code;
2167             if (curr == end)
2168                 code = NR_MOVETO;
2169             sp_nodepath_node_new(t, NULL,
2170                                  (Inkscape::NodePath::NodeType)curr->type, code,
2171                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2172             if (curr == start)
2173                 break;
2174         }
2175         sp_nodepath_subpath_destroy(a->subpath);
2178     }
2182     //##################################
2183     //# OPEN PATH
2184     //##################################
2185     else {
2187         //We need to get the direction of the list between A and B
2188         //Can we walk from a to b?
2189         start = end = NULL;
2190         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2191             if (curr==b) {
2192                 start = a;  //did it!  we go from a to b
2193                 end   = b;
2194                 //printf("A to B\n");
2195                 break;
2196             }
2197         }
2198         if (!start) {//didn't work?  let's try the other direction
2199             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2200                 if (curr==a) {
2201                     start = b;  //did it!  we go from b to a
2202                     end   = a;
2203                     //printf("B to A\n");
2204                     break;
2205                 }
2206             }
2207         }
2208         if (!start) {
2209             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2210                                                      _("Cannot find path between nodes."));
2211             return;
2212         }
2216         //Copy everything after 'end' to a new subpath
2217        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2218         for (curr=end ; curr ; curr=curr->n.other) {
2219             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
2220                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2221         }
2223         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2224         for (curr = start->n.other ; curr  ; curr=next) {
2225             next = curr->n.other;
2226             sp_nodepath_node_destroy(curr);
2227         }
2229     }
2230     //###########################################
2231     //# END EDITS
2232     //###########################################
2234     //clean up the nodepath (such as for trivial subpaths)
2235     sp_nodepath_cleanup(nodepath);
2237     sp_nodepath_update_handles(nodepath);
2239     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2241     sp_nodepath_update_statusbar(nodepath);
2244 /**
2245  * Call sp_nodepath_set_line() for all selected segments.
2246  */
2247 void
2248 sp_node_selected_set_line_type(NRPathcode code)
2250     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2251     if (nodepath == NULL) return;
2253     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2254        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2255         g_assert(n->selected);
2256         if (n->p.other && n->p.other->selected) {
2257             sp_nodepath_set_line_type(n, code);
2258         }
2259     }
2261     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2264 /**
2265  * Call sp_nodepath_convert_node_type() for all selected nodes.
2266  */
2267 void
2268 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
2270     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2271     if (nodepath == NULL) return;
2273     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2274         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2275     }
2277     sp_nodepath_update_repr(nodepath, _("Change node type"));
2280 /**
2281  * Change select status of node, update its own and neighbour handles.
2282  */
2283 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2285     node->selected = selected;
2287     if (selected) {
2288         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2289         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2290         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2291         sp_knot_update_ctrl(node->knot);
2292     } else {
2293         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2294         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2295         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2296         sp_knot_update_ctrl(node->knot);
2297     }
2299     sp_node_update_handles(node);
2300     if (node->n.other) sp_node_update_handles(node->n.other);
2301     if (node->p.other) sp_node_update_handles(node->p.other);
2304 /**
2305 \brief Select a node
2306 \param node     The node to select
2307 \param incremental   If true, add to selection, otherwise deselect others
2308 \param override   If true, always select this node, otherwise toggle selected status
2309 */
2310 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2312     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2314     if (incremental) {
2315         if (override) {
2316             if (!g_list_find(nodepath->selected, node)) {
2317                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2318             }
2319             sp_node_set_selected(node, TRUE);
2320         } else { // toggle
2321             if (node->selected) {
2322                 g_assert(g_list_find(nodepath->selected, node));
2323                 nodepath->selected = g_list_remove(nodepath->selected, node);
2324             } else {
2325                 g_assert(!g_list_find(nodepath->selected, node));
2326                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2327             }
2328             sp_node_set_selected(node, !node->selected);
2329         }
2330     } else {
2331         sp_nodepath_deselect(nodepath);
2332         nodepath->selected = g_list_prepend(nodepath->selected, node);
2333         sp_node_set_selected(node, TRUE);
2334     }
2336     sp_nodepath_update_statusbar(nodepath);
2340 /**
2341 \brief Deselect all nodes in the nodepath
2342 */
2343 void
2344 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2346     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2348     while (nodepath->selected) {
2349         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2350         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2351     }
2352     sp_nodepath_update_statusbar(nodepath);
2355 /**
2356 \brief Select or invert selection of all nodes in the nodepath
2357 */
2358 void
2359 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2361     if (!nodepath) return;
2363     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2364        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2365         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2366            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2367            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2368         }
2369     }
2372 /**
2373  * If nothing selected, does the same as sp_nodepath_select_all();
2374  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2375  * (i.e., similar to "select all in layer", with the "selected" subpaths
2376  * being treated as "layers" in the path).
2377  */
2378 void
2379 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2381     if (!nodepath) return;
2383     if (g_list_length (nodepath->selected) == 0) {
2384         sp_nodepath_select_all (nodepath, invert);
2385         return;
2386     }
2388     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2389     GSList *subpaths = NULL;
2391     for (GList *l = copy; l != NULL; l = l->next) {
2392         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2393         Inkscape::NodePath::SubPath *subpath = n->subpath;
2394         if (!g_slist_find (subpaths, subpath))
2395             subpaths = g_slist_prepend (subpaths, subpath);
2396     }
2398     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2399         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2400         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2401             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2402             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2403         }
2404     }
2406     g_slist_free (subpaths);
2407     g_list_free (copy);
2410 /**
2411  * \brief Select the node after the last selected; if none is selected,
2412  * select the first within path.
2413  */
2414 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2416     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2418    Inkscape::NodePath::Node *last = NULL;
2419     if (nodepath->selected) {
2420         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2421            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2422             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2423             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2424                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2425                 if (node->selected) {
2426                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2427                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2428                             if (spl->next) { // there's a next subpath
2429                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2430                                 last = subpath_next->first;
2431                             } else if (spl->prev) { // there's a previous subpath
2432                                 last = NULL; // to be set later to the first node of first subpath
2433                             } else {
2434                                 last = node->n.other;
2435                             }
2436                         } else {
2437                             last = node->n.other;
2438                         }
2439                     } else {
2440                         if (node->n.other) {
2441                             last = node->n.other;
2442                         } else {
2443                             if (spl->next) { // there's a next subpath
2444                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2445                                 last = subpath_next->first;
2446                             } else if (spl->prev) { // there's a previous subpath
2447                                 last = NULL; // to be set later to the first node of first subpath
2448                             } else {
2449                                 last = (Inkscape::NodePath::Node *) subpath->first;
2450                             }
2451                         }
2452                     }
2453                 }
2454             }
2455         }
2456         sp_nodepath_deselect(nodepath);
2457     }
2459     if (last) { // there's at least one more node after selected
2460         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2461     } else { // no more nodes, select the first one in first subpath
2462        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2463         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2464     }
2467 /**
2468  * \brief Select the node before the first selected; if none is selected,
2469  * select the last within path
2470  */
2471 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2473     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2475    Inkscape::NodePath::Node *last = NULL;
2476     if (nodepath->selected) {
2477         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2478            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2479             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2480                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2481                 if (node->selected) {
2482                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2483                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2484                             if (spl->prev) { // there's a prev subpath
2485                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2486                                 last = subpath_prev->last;
2487                             } else if (spl->next) { // there's a next subpath
2488                                 last = NULL; // to be set later to the last node of last subpath
2489                             } else {
2490                                 last = node->p.other;
2491                             }
2492                         } else {
2493                             last = node->p.other;
2494                         }
2495                     } else {
2496                         if (node->p.other) {
2497                             last = node->p.other;
2498                         } else {
2499                             if (spl->prev) { // there's a prev subpath
2500                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2501                                 last = subpath_prev->last;
2502                             } else if (spl->next) { // there's a next subpath
2503                                 last = NULL; // to be set later to the last node of last subpath
2504                             } else {
2505                                 last = (Inkscape::NodePath::Node *) subpath->last;
2506                             }
2507                         }
2508                     }
2509                 }
2510             }
2511         }
2512         sp_nodepath_deselect(nodepath);
2513     }
2515     if (last) { // there's at least one more node before selected
2516         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2517     } else { // no more nodes, select the last one in last subpath
2518         GList *spl = g_list_last(nodepath->subpaths);
2519        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2520         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2521     }
2524 /**
2525  * \brief Select all nodes that are within the rectangle.
2526  */
2527 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2529     if (!incremental) {
2530         sp_nodepath_deselect(nodepath);
2531     }
2533     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2534        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2535         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2536            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2538             if (b.contains(node->pos)) {
2539                 sp_nodepath_node_select(node, TRUE, TRUE);
2540             }
2541         }
2542     }
2546 void
2547 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2549     g_assert (n);
2550     g_assert (nodepath);
2551     g_assert (n->subpath->nodepath == nodepath);
2553     if (g_list_length (nodepath->selected) == 0) {
2554         if (grow > 0) {
2555             sp_nodepath_node_select(n, TRUE, TRUE);
2556         }
2557         return;
2558     }
2560     if (g_list_length (nodepath->selected) == 1) {
2561         if (grow < 0) {
2562             sp_nodepath_deselect (nodepath);
2563             return;
2564         }
2565     }
2567         double n_sel_range = 0, p_sel_range = 0;
2568             Inkscape::NodePath::Node *farthest_n_node = n;
2569             Inkscape::NodePath::Node *farthest_p_node = n;
2571         // Calculate ranges
2572         {
2573             double n_range = 0, p_range = 0;
2574             bool n_going = true, p_going = true;
2575             Inkscape::NodePath::Node *n_node = n;
2576             Inkscape::NodePath::Node *p_node = n;
2577             do {
2578                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2579                 if (n_node && n_going)
2580                     n_node = n_node->n.other;
2581                 if (n_node == NULL) {
2582                     n_going = false;
2583                 } else {
2584                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2585                     if (n_node->selected) {
2586                         n_sel_range = n_range;
2587                         farthest_n_node = n_node;
2588                     }
2589                     if (n_node == p_node) {
2590                         n_going = false;
2591                         p_going = false;
2592                     }
2593                 }
2594                 if (p_node && p_going)
2595                     p_node = p_node->p.other;
2596                 if (p_node == NULL) {
2597                     p_going = false;
2598                 } else {
2599                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2600                     if (p_node->selected) {
2601                         p_sel_range = p_range;
2602                         farthest_p_node = p_node;
2603                     }
2604                     if (p_node == n_node) {
2605                         n_going = false;
2606                         p_going = false;
2607                     }
2608                 }
2609             } while (n_going || p_going);
2610         }
2612     if (grow > 0) {
2613         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2614                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2615         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2616                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2617         }
2618     } else {
2619         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2620                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2621         } else if (farthest_p_node && farthest_p_node->selected) {
2622                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2623         }
2624     }
2627 void
2628 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2630     g_assert (n);
2631     g_assert (nodepath);
2632     g_assert (n->subpath->nodepath == nodepath);
2634     if (g_list_length (nodepath->selected) == 0) {
2635         if (grow > 0) {
2636             sp_nodepath_node_select(n, TRUE, TRUE);
2637         }
2638         return;
2639     }
2641     if (g_list_length (nodepath->selected) == 1) {
2642         if (grow < 0) {
2643             sp_nodepath_deselect (nodepath);
2644             return;
2645         }
2646     }
2648     Inkscape::NodePath::Node *farthest_selected = NULL;
2649     double farthest_dist = 0;
2651     Inkscape::NodePath::Node *closest_unselected = NULL;
2652     double closest_dist = NR_HUGE;
2654     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2655        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2656         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2657            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2658            if (node == n)
2659                continue;
2660            if (node->selected) {
2661                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2662                    farthest_dist = NR::L2(node->pos - n->pos);
2663                    farthest_selected = node;
2664                }
2665            } else {
2666                if (NR::L2(node->pos - n->pos) < closest_dist) {
2667                    closest_dist = NR::L2(node->pos - n->pos);
2668                    closest_unselected = node;
2669                }
2670            }
2671         }
2672     }
2674     if (grow > 0) {
2675         if (closest_unselected) {
2676             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2677         }
2678     } else {
2679         if (farthest_selected) {
2680             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2681         }
2682     }
2686 /**
2687 \brief  Saves all nodes' and handles' current positions in their origin members
2688 */
2689 void
2690 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2692     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2693        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2694         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2695            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2696            n->origin = n->pos;
2697            n->p.origin = n->p.pos;
2698            n->n.origin = n->n.pos;
2699         }
2700     }
2701
2703 /**
2704 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2705 */
2706 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2708     if (!nodepath->selected) {
2709         return NULL;
2710     }
2712     GList *r = NULL;
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 (node->selected) {
2720                 r = g_list_append(r, GINT_TO_POINTER(i));
2721             }
2722         }
2723     }
2724     return r;
2727 /**
2728 \brief  Restores selection by selecting nodes whose positions are in the list
2729 */
2730 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2732     sp_nodepath_deselect(nodepath);
2734     guint i = 0;
2735     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2736        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2737         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2738            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2739             i++;
2740             if (g_list_find(r, GINT_TO_POINTER(i))) {
2741                 sp_nodepath_node_select(node, TRUE, TRUE);
2742             }
2743         }
2744     }
2748 /**
2749 \brief Adjusts handle according to node type and line code.
2750 */
2751 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2753     double len, otherlen, linelen;
2755     g_assert(node);
2757    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2758    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2760     /** \todo fixme: */
2761     if (me->other == NULL) return;
2762     if (other->other == NULL) return;
2764     /* I have line */
2766     NRPathcode mecode, ocode;
2767     if (which_adjust == 1) {
2768         mecode = (NRPathcode)me->other->code;
2769         ocode = (NRPathcode)node->code;
2770     } else {
2771         mecode = (NRPathcode)node->code;
2772         ocode = (NRPathcode)other->other->code;
2773     }
2775     if (mecode == NR_LINETO) return;
2777     /* I am curve */
2779     if (other->other == NULL) return;
2781     /* Other has line */
2783     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2785     NR::Point delta;
2786     if (ocode == NR_LINETO) {
2787         /* other is lineto, we are either smooth or symm */
2788        Inkscape::NodePath::Node *othernode = other->other;
2789         len = NR::L2(me->pos - node->pos);
2790         delta = node->pos - othernode->pos;
2791         linelen = NR::L2(delta);
2792         if (linelen < 1e-18) 
2793             return;
2794         me->pos = node->pos + (len / linelen)*delta;
2795         return;
2796     }
2798     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2800         me->pos = 2 * node->pos - other->pos;
2801         return;
2802     }
2804     /* We are smooth */
2806     len = NR::L2(me->pos - node->pos);
2807     delta = other->pos - node->pos;
2808     otherlen = NR::L2(delta);
2809     if (otherlen < 1e-18) return;
2811     me->pos = node->pos - (len / otherlen) * delta;
2814 /**
2815  \brief Adjusts both handles according to node type and line code
2816  */
2817 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2819     g_assert(node);
2821     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2823     /* we are either smooth or symm */
2825     if (node->p.other == NULL) return;
2827     if (node->n.other == NULL) return;
2829     if (node->code == NR_LINETO) {
2830         if (node->n.other->code == NR_LINETO) return;
2831         sp_node_adjust_handle(node, 1);
2832         return;
2833     }
2835     if (node->n.other->code == NR_LINETO) {
2836         if (node->code == NR_LINETO) return;
2837         sp_node_adjust_handle(node, -1);
2838         return;
2839     }
2841     /* both are curves */
2842     NR::Point const delta( node->n.pos - node->p.pos );
2844     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2845         node->p.pos = node->pos - delta / 2;
2846         node->n.pos = node->pos + delta / 2;
2847         return;
2848     }
2850     /* We are smooth */
2851     double plen = NR::L2(node->p.pos - node->pos);
2852     if (plen < 1e-18) return;
2853     double nlen = NR::L2(node->n.pos - node->pos);
2854     if (nlen < 1e-18) return;
2855     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2856     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2859 /**
2860  * Node event callback.
2861  */
2862 static gboolean node_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n)
2864     gboolean ret = FALSE;
2865     switch (event->type) {
2866         case GDK_ENTER_NOTIFY:
2867             active_node = n;
2868             break;
2869         case GDK_LEAVE_NOTIFY:
2870             active_node = NULL;
2871             break;
2872         case GDK_KEY_PRESS:
2873             switch (get_group0_keyval (&event->key)) {
2874                 case GDK_space:
2875                     if (event->key.state & GDK_BUTTON1_MASK) {
2876                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2877                         stamp_repr(nodepath);
2878                         ret = TRUE;
2879                     }
2880                     break;
2881                 case GDK_Page_Up:
2882                     if (event->key.state & GDK_CONTROL_MASK) {
2883                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2884                     } else {
2885                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2886                     }
2887                     break;
2888                 case GDK_Page_Down:
2889                     if (event->key.state & GDK_CONTROL_MASK) {
2890                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2891                     } else {
2892                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2893                     }
2894                     break;
2895                 default:
2896                     break;
2897             }
2898             break;
2899         default:
2900             break;
2901     }
2903     return ret;
2906 /**
2907  * Handle keypress on node; directly called.
2908  */
2909 gboolean node_key(GdkEvent *event)
2911     Inkscape::NodePath::Path *np;
2913     // there is no way to verify nodes so set active_node to nil when deleting!!
2914     if (active_node == NULL) return FALSE;
2916     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2917         gint ret = FALSE;
2918         switch (get_group0_keyval (&event->key)) {
2919             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2920             case GDK_BackSpace:
2921                 np = active_node->subpath->nodepath;
2922                 sp_nodepath_node_destroy(active_node);
2923                 sp_nodepath_update_repr(np, _("Delete node"));
2924                 active_node = NULL;
2925                 ret = TRUE;
2926                 break;
2927             case GDK_c:
2928                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2929                 ret = TRUE;
2930                 break;
2931             case GDK_s:
2932                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2933                 ret = TRUE;
2934                 break;
2935             case GDK_y:
2936                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2937                 ret = TRUE;
2938                 break;
2939             case GDK_b:
2940                 sp_nodepath_node_break(active_node);
2941                 ret = TRUE;
2942                 break;
2943         }
2944         return ret;
2945     }
2946     return FALSE;
2949 /**
2950  * Mouseclick on node callback.
2951  */
2952 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2954    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2956     if (state & GDK_CONTROL_MASK) {
2957         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2959         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2960             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2961                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2962             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2963                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2964             } else {
2965                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2966             }
2967             sp_nodepath_update_repr(nodepath, _("Change node type"));
2968             sp_nodepath_update_statusbar(nodepath);
2970         } else { //ctrl+alt+click: delete node
2971             GList *node_to_delete = NULL;
2972             node_to_delete = g_list_append(node_to_delete, n);
2973             sp_node_delete_preserve(node_to_delete);
2974         }
2976     } else {
2977         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2978     }
2981 /**
2982  * Mouse grabbed node callback.
2983  */
2984 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2986    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2988     if (!n->selected) {
2989         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2990     }
2992     n->is_dragging = true;
2994     sp_nodepath_remember_origins (n->subpath->nodepath);
2997 /**
2998  * Mouse ungrabbed node callback.
2999  */
3000 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
3002    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3004    n->dragging_out = NULL;
3005    n->is_dragging = false;
3007    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3010 /**
3011  * The point on a line, given by its angle, closest to the given point.
3012  * \param p  A point.
3013  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3014  * \param closest  Pointer to the point struct where the result is stored.
3015  * \todo FIXME: use dot product perhaps?
3016  */
3017 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3019     if (a == HUGE_VAL) { // vertical
3020         *closest = NR::Point(0, (*p)[NR::Y]);
3021     } else {
3022         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3023         (*closest)[NR::Y] = a * (*closest)[NR::X];
3024     }
3027 /**
3028  * Distance from the point to a line given by its angle.
3029  * \param p  A point.
3030  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3031  */
3032 static double point_line_distance(NR::Point *p, double a)
3034     NR::Point c;
3035     point_line_closest(p, a, &c);
3036     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]));
3039 /**
3040  * Callback for node "request" signal.
3041  * \todo fixme: This goes to "moved" event? (lauris)
3042  */
3043 static gboolean
3044 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3046     double yn, xn, yp, xp;
3047     double an, ap, na, pa;
3048     double d_an, d_ap, d_na, d_pa;
3049     gboolean collinear = FALSE;
3050     NR::Point c;
3051     NR::Point pr;
3053    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3055    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3056    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
3058        NR::Point mouse = (*p);
3060        if (!n->dragging_out) {
3061            // This is the first drag-out event; find out which handle to drag out
3062            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3063            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3065            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3066                return FALSE;
3068            Inkscape::NodePath::NodeSide *opposite;
3069            if (appr_p > appr_n) { // closer to p
3070                n->dragging_out = &n->p;
3071                opposite = &n->n;
3072                n->code = NR_CURVETO;
3073            } else if (appr_p < appr_n) { // closer to n
3074                n->dragging_out = &n->n;
3075                opposite = &n->p;
3076                n->n.other->code = NR_CURVETO;
3077            } else { // p and n nodes are the same
3078                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3079                    n->dragging_out = &n->p;
3080                    opposite = &n->n;
3081                    n->code = NR_CURVETO;
3082                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3083                    n->dragging_out = &n->n;
3084                    opposite = &n->p;
3085                    n->n.other->code = NR_CURVETO;
3086                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3087                    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);
3088                    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);
3089                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3090                        n->dragging_out = &n->n;
3091                        opposite = &n->p;
3092                        n->n.other->code = NR_CURVETO;
3093                    } else { // closer to other's n handle
3094                        n->dragging_out = &n->p;
3095                        opposite = &n->n;
3096                        n->code = NR_CURVETO;
3097                    }
3098                }
3099            }
3101            // if there's another handle, make sure the one we drag out starts parallel to it
3102            if (opposite->pos != n->pos) {
3103                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3104            }
3106            // knots might not be created yet!
3107            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3108            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3109        }
3111        // pass this on to the handle-moved callback
3112        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3113        sp_node_update_handles(n);
3114        return TRUE;
3115    }
3117     if (state & GDK_CONTROL_MASK) { // constrained motion
3119         // calculate relative distances of handles
3120         // n handle:
3121         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3122         xn = n->n.pos[NR::X] - n->pos[NR::X];
3123         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3124         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3125             if (n->n.other) { // if there is the next point
3126                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3127                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3128                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3129             }
3130         }
3131         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3132         if (yn < 0) { xn = -xn; yn = -yn; }
3134         // p handle:
3135         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3136         xp = n->p.pos[NR::X] - n->pos[NR::X];
3137         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3138         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3139             if (n->p.other) {
3140                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3141                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3142                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3143             }
3144         }
3145         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3146         if (yp < 0) { xp = -xp; yp = -yp; }
3148         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3149             // sliding on handles, only if at least one of the handles is non-vertical
3150             // (otherwise it's the same as ctrl+drag anyway)
3152             // calculate angles of the handles
3153             if (xn == 0) {
3154                 if (yn == 0) { // no handle, consider it the continuation of the other one
3155                     an = 0;
3156                     collinear = TRUE;
3157                 }
3158                 else an = 0; // vertical; set the angle to horizontal
3159             } else an = yn/xn;
3161             if (xp == 0) {
3162                 if (yp == 0) { // no handle, consider it the continuation of the other one
3163                     ap = an;
3164                 }
3165                 else ap = 0; // vertical; set the angle to horizontal
3166             } else  ap = yp/xp;
3168             if (collinear) an = ap;
3170             // angles of the perpendiculars; HUGE_VAL means vertical
3171             if (an == 0) na = HUGE_VAL; else na = -1/an;
3172             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3174             // mouse point relative to the node's original pos
3175             pr = (*p) - n->origin;
3177             // distances to the four lines (two handles and two perpendiculars)
3178             d_an = point_line_distance(&pr, an);
3179             d_na = point_line_distance(&pr, na);
3180             d_ap = point_line_distance(&pr, ap);
3181             d_pa = point_line_distance(&pr, pa);
3183             // find out which line is the closest, save its closest point in c
3184             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3185                 point_line_closest(&pr, an, &c);
3186             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3187                 point_line_closest(&pr, ap, &c);
3188             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3189                 point_line_closest(&pr, na, &c);
3190             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3191                 point_line_closest(&pr, pa, &c);
3192             }
3194             // move the node to the closest point
3195             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3196                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3197                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3199         } else {  // constraining to hor/vert
3201             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3202                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3203             } else { // snap to vert
3204                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3205             }
3206         }
3207     } else { // move freely
3208         if (n->is_dragging) {
3209             if (state & GDK_MOD1_MASK) { // sculpt
3210                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3211             } else {
3212                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3213                                             (*p)[NR::X] - n->pos[NR::X],
3214                                             (*p)[NR::Y] - n->pos[NR::Y],
3215                                             (state & GDK_SHIFT_MASK) == 0);
3216             }
3217         }
3218     }
3220     n->subpath->nodepath->desktop->scroll_to_point(p);
3222     return TRUE;
3225 /**
3226  * Node handle clicked callback.
3227  */
3228 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3230    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3232     if (state & GDK_CONTROL_MASK) { // "delete" handle
3233         if (n->p.knot == knot) {
3234             n->p.pos = n->pos;
3235         } else if (n->n.knot == knot) {
3236             n->n.pos = n->pos;
3237         }
3238         sp_node_update_handles(n);
3239         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3240         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3241         sp_nodepath_update_statusbar(nodepath);
3243     } else { // just select or add to selection, depending in Shift
3244         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3245     }
3248 /**
3249  * Node handle grabbed callback.
3250  */
3251 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3253    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3255     if (!n->selected) {
3256         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3257     }
3259     // remember the origin point of the handle
3260     if (n->p.knot == knot) {
3261         n->p.origin_radial = n->p.pos - n->pos;
3262     } else if (n->n.knot == knot) {
3263         n->n.origin_radial = n->n.pos - n->pos;
3264     } else {
3265         g_assert_not_reached();
3266     }
3270 /**
3271  * Node handle ungrabbed callback.
3272  */
3273 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3275    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3277     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3278     if (n->p.knot == knot) {
3279         n->p.origin_radial.a = 0;
3280         sp_knot_set_position(knot, &n->p.pos, state);
3281     } else if (n->n.knot == knot) {
3282         n->n.origin_radial.a = 0;
3283         sp_knot_set_position(knot, &n->n.pos, state);
3284     } else {
3285         g_assert_not_reached();
3286     }
3288     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3291 /**
3292  * Node handle "request" signal callback.
3293  */
3294 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3296     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3298     Inkscape::NodePath::NodeSide *me, *opposite;
3299     gint which;
3300     if (n->p.knot == knot) {
3301         me = &n->p;
3302         opposite = &n->n;
3303         which = -1;
3304     } else if (n->n.knot == knot) {
3305         me = &n->n;
3306         opposite = &n->p;
3307         which = 1;
3308     } else {
3309         me = opposite = NULL;
3310         which = 0;
3311         g_assert_not_reached();
3312     }
3314     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3316     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3318     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3319         /* We are smooth node adjacent with line */
3320         NR::Point const delta = *p - n->pos;
3321         NR::Coord const len = NR::L2(delta);
3322         Inkscape::NodePath::Node *othernode = opposite->other;
3323         NR::Point const ndelta = n->pos - othernode->pos;
3324         NR::Coord const linelen = NR::L2(ndelta);
3325         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3326             NR::Coord const scal = dot(delta, ndelta) / linelen;
3327             (*p) = n->pos + (scal / linelen) * ndelta;
3328         }
3329         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
3330     } else {
3331         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
3332     }
3334     sp_node_adjust_handle(n, -which);
3336     return FALSE;
3339 /**
3340  * Node handle moved callback.
3341  */
3342 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3344    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3346    Inkscape::NodePath::NodeSide *me;
3347    Inkscape::NodePath::NodeSide *other;
3348     if (n->p.knot == knot) {
3349         me = &n->p;
3350         other = &n->n;
3351     } else if (n->n.knot == knot) {
3352         me = &n->n;
3353         other = &n->p;
3354     } else {
3355         me = NULL;
3356         other = NULL;
3357         g_assert_not_reached();
3358     }
3360     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3361     Radial rme(me->pos - n->pos);
3362     Radial rother(other->pos - n->pos);
3363     Radial rnew(*p - n->pos);
3365     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3366         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3367         /* 0 interpreted as "no snapping". */
3369         // The closest PI/snaps angle, starting from zero.
3370         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3371         if (me->origin_radial.a == HUGE_VAL) {
3372             // ortho doesn't exist: original handle was zero length.
3373             rnew.a = a_snapped;
3374         } else {
3375             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3376              * its opposite and perpendiculars). */
3377             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3379             // Snap to the closest.
3380             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3381                        ? a_snapped
3382                        : a_ortho );
3383         }
3384     }
3386     if (state & GDK_MOD1_MASK) {
3387         // lock handle length
3388         rnew.r = me->origin_radial.r;
3389     }
3391     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3392         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
3393         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3394         rother.a += rnew.a - rme.a;
3395         other->pos = NR::Point(rother) + n->pos;
3396         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3397         sp_knot_set_position(other->knot, &other->pos, 0);
3398     }
3400     me->pos = NR::Point(rnew) + n->pos;
3401     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3403     // this is what sp_knot_set_position does, but without emitting the signal:
3404     // we cannot emit a "moved" signal because we're now processing it
3405     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
3407     knot->desktop->set_coordinate_status(me->pos);
3409     update_object(n->subpath->nodepath);
3411     /* status text */
3412     SPDesktop *desktop = n->subpath->nodepath->desktop;
3413     if (!desktop) return;
3414     SPEventContext *ec = desktop->event_context;
3415     if (!ec) return;
3416     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3417     if (!mc) return;
3419     double degrees = 180 / M_PI * rnew.a;
3420     if (degrees > 180) degrees -= 360;
3421     if (degrees < -180) degrees += 360;
3422     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3423         degrees = angle_to_compass (degrees);
3425     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3427     mc->setF(Inkscape::NORMAL_MESSAGE,
3428          _("<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);
3430     g_string_free(length, TRUE);
3433 /**
3434  * Node handle event callback.
3435  */
3436 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3438     gboolean ret = FALSE;
3439     switch (event->type) {
3440         case GDK_KEY_PRESS:
3441             switch (get_group0_keyval (&event->key)) {
3442                 case GDK_space:
3443                     if (event->key.state & GDK_BUTTON1_MASK) {
3444                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3445                         stamp_repr(nodepath);
3446                         ret = TRUE;
3447                     }
3448                     break;
3449                 default:
3450                     break;
3451             }
3452             break;
3453         default:
3454             break;
3455     }
3457     return ret;
3460 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3461                                  Radial &rme, Radial &rother, gboolean const both)
3463     rme.a += angle;
3464     if ( both
3465          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3466          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3467     {
3468         rother.a += angle;
3469     }
3472 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3473                                         Radial &rme, Radial &rother, gboolean const both)
3475     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3477     gdouble r;
3478     if ( both
3479          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3480          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3481     {
3482         r = MAX(rme.r, rother.r);
3483     } else {
3484         r = rme.r;
3485     }
3487     gdouble const weird_angle = atan2(norm_angle, r);
3488 /* Bulia says norm_angle is just the visible distance that the
3489  * object's end must travel on the screen.  Left as 'angle' for want of
3490  * a better name.*/
3492     rme.a += weird_angle;
3493     if ( both
3494          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3495          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3496     {
3497         rother.a += weird_angle;
3498     }
3501 /**
3502  * Rotate one node.
3503  */
3504 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3506     Inkscape::NodePath::NodeSide *me, *other;
3507     bool both = false;
3509     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3510     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3512     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3513         me = &(n->p);
3514         other = &(n->n);
3515     } else if (!n->p.other) {
3516         me = &(n->n);
3517         other = &(n->p);
3518     } else {
3519         if (which > 0) { // right handle
3520             if (xn > xp) {
3521                 me = &(n->n);
3522                 other = &(n->p);
3523             } else {
3524                 me = &(n->p);
3525                 other = &(n->n);
3526             }
3527         } else if (which < 0){ // left handle
3528             if (xn <= xp) {
3529                 me = &(n->n);
3530                 other = &(n->p);
3531             } else {
3532                 me = &(n->p);
3533                 other = &(n->n);
3534             }
3535         } else { // both handles
3536             me = &(n->n);
3537             other = &(n->p);
3538             both = true;
3539         }
3540     }
3542     Radial rme(me->pos - n->pos);
3543     Radial rother(other->pos - n->pos);
3545     if (screen) {
3546         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3547     } else {
3548         node_rotate_one_internal (*n, angle, rme, rother, both);
3549     }
3551     me->pos = n->pos + NR::Point(rme);
3553     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3554         other->pos =  n->pos + NR::Point(rother);
3555     }
3557     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3558     // so here we just move all the knots without emitting move signals, for speed
3559     sp_node_update_handles(n, false);
3562 /**
3563  * Rotate selected nodes.
3564  */
3565 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3567     if (!nodepath || !nodepath->selected) return;
3569     if (g_list_length(nodepath->selected) == 1) {
3570        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3571         node_rotate_one (n, angle, which, screen);
3572     } else {
3573        // rotate as an object:
3575         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3576         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3577         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3578             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3579             box.expandTo (n->pos); // contain all selected nodes
3580         }
3582         gdouble rot;
3583         if (screen) {
3584             gdouble const zoom = nodepath->desktop->current_zoom();
3585             gdouble const zmove = angle / zoom;
3586             gdouble const r = NR::L2(box.max() - box.midpoint());
3587             rot = atan2(zmove, r);
3588         } else {
3589             rot = angle;
3590         }
3592         NR::Matrix t =
3593             NR::Matrix (NR::translate(-box.midpoint())) *
3594             NR::Matrix (NR::rotate(rot)) *
3595             NR::Matrix (NR::translate(box.midpoint()));
3597         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3598             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3599             n->pos *= t;
3600             n->n.pos *= t;
3601             n->p.pos *= t;
3602             sp_node_update_handles(n, false);
3603         }
3604     }
3606     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3609 /**
3610  * Scale one node.
3611  */
3612 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3614     bool both = false;
3615     Inkscape::NodePath::NodeSide *me, *other;
3617     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3618     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3620     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3621         me = &(n->p);
3622         other = &(n->n);
3623         n->code = NR_CURVETO;
3624     } else if (!n->p.other) {
3625         me = &(n->n);
3626         other = &(n->p);
3627         if (n->n.other)
3628             n->n.other->code = NR_CURVETO;
3629     } else {
3630         if (which > 0) { // right handle
3631             if (xn > xp) {
3632                 me = &(n->n);
3633                 other = &(n->p);
3634                 if (n->n.other)
3635                     n->n.other->code = NR_CURVETO;
3636             } else {
3637                 me = &(n->p);
3638                 other = &(n->n);
3639                 n->code = NR_CURVETO;
3640             }
3641         } else if (which < 0){ // left handle
3642             if (xn <= xp) {
3643                 me = &(n->n);
3644                 other = &(n->p);
3645                 if (n->n.other)
3646                     n->n.other->code = NR_CURVETO;
3647             } else {
3648                 me = &(n->p);
3649                 other = &(n->n);
3650                 n->code = NR_CURVETO;
3651             }
3652         } else { // both handles
3653             me = &(n->n);
3654             other = &(n->p);
3655             both = true;
3656             n->code = NR_CURVETO;
3657             if (n->n.other)
3658                 n->n.other->code = NR_CURVETO;
3659         }
3660     }
3662     Radial rme(me->pos - n->pos);
3663     Radial rother(other->pos - n->pos);
3665     rme.r += grow;
3666     if (rme.r < 0) rme.r = 0;
3667     if (rme.a == HUGE_VAL) {
3668         if (me->other) { // if direction is unknown, initialize it towards the next node
3669             Radial rme_next(me->other->pos - n->pos);
3670             rme.a = rme_next.a;
3671         } else { // if there's no next, initialize to 0
3672             rme.a = 0;
3673         }
3674     }
3675     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3676         rother.r += grow;
3677         if (rother.r < 0) rother.r = 0;
3678         if (rother.a == HUGE_VAL) {
3679             rother.a = rme.a + M_PI;
3680         }
3681     }
3683     me->pos = n->pos + NR::Point(rme);
3685     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3686         other->pos = n->pos + NR::Point(rother);
3687     }
3689     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3690     // so here we just move all the knots without emitting move signals, for speed
3691     sp_node_update_handles(n, false);
3694 /**
3695  * Scale selected nodes.
3696  */
3697 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3699     if (!nodepath || !nodepath->selected) return;
3701     if (g_list_length(nodepath->selected) == 1) {
3702         // scale handles of the single selected node
3703         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3704         node_scale_one (n, grow, which);
3705     } else {
3706         // scale nodes as an "object":
3708         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3709         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3710         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3711             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3712             box.expandTo (n->pos); // contain all selected nodes
3713         }
3715         double scale = (box.maxExtent() + grow)/box.maxExtent();
3717         NR::Matrix t =
3718             NR::Matrix (NR::translate(-box.midpoint())) *
3719             NR::Matrix (NR::scale(scale, scale)) *
3720             NR::Matrix (NR::translate(box.midpoint()));
3722         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3723             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3724             n->pos *= t;
3725             n->n.pos *= t;
3726             n->p.pos *= t;
3727             sp_node_update_handles(n, false);
3728         }
3729     }
3731     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
3734 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3736     if (!nodepath) return;
3737     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3740 /**
3741  * Flip selected nodes horizontally/vertically.
3742  */
3743 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3745     if (!nodepath || !nodepath->selected) return;
3747     if (g_list_length(nodepath->selected) == 1) {
3748         // flip handles of the single selected node
3749         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3750         double temp = n->p.pos[axis];
3751         n->p.pos[axis] = n->n.pos[axis];
3752         n->n.pos[axis] = temp;
3753         sp_node_update_handles(n, false);
3754     } else {
3755         // scale nodes as an "object":
3757         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3758         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3759         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3760             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3761             box.expandTo (n->pos); // contain all selected nodes
3762         }
3764         NR::Matrix t =
3765             NR::Matrix (NR::translate(-box.midpoint())) *
3766             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3767             NR::Matrix (NR::translate(box.midpoint()));
3769         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3770             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3771             n->pos *= t;
3772             n->n.pos *= t;
3773             n->p.pos *= t;
3774             sp_node_update_handles(n, false);
3775         }
3776     }
3778     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
3781 //-----------------------------------------------
3782 /**
3783  * Return new subpath under given nodepath.
3784  */
3785 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3787     g_assert(nodepath);
3788     g_assert(nodepath->desktop);
3790    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3792     s->nodepath = nodepath;
3793     s->closed = FALSE;
3794     s->nodes = NULL;
3795     s->first = NULL;
3796     s->last = NULL;
3798     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3799     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3800     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3802     return s;
3805 /**
3806  * Destroy nodes in subpath, then subpath itself.
3807  */
3808 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3810     g_assert(subpath);
3811     g_assert(subpath->nodepath);
3812     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3814     while (subpath->nodes) {
3815         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3816     }
3818     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3820     g_free(subpath);
3823 /**
3824  * Link head to tail in subpath.
3825  */
3826 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3828     g_assert(!sp->closed);
3829     g_assert(sp->last != sp->first);
3830     g_assert(sp->first->code == NR_MOVETO);
3832     sp->closed = TRUE;
3834     //Link the head to the tail
3835     sp->first->p.other = sp->last;
3836     sp->last->n.other  = sp->first;
3837     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3838     sp->first          = sp->last;
3840     //Remove the extra end node
3841     sp_nodepath_node_destroy(sp->last->n.other);
3844 /**
3845  * Open closed (loopy) subpath at node.
3846  */
3847 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3849     g_assert(sp->closed);
3850     g_assert(n->subpath == sp);
3851     g_assert(sp->first == sp->last);
3853     /* We create new startpoint, current node will become last one */
3855    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3856                                                 &n->pos, &n->pos, &n->n.pos);
3859     sp->closed        = FALSE;
3861     //Unlink to make a head and tail
3862     sp->first         = new_path;
3863     sp->last          = n;
3864     n->n.other        = NULL;
3865     new_path->p.other = NULL;
3868 /**
3869  * Returns area in triangle given by points; may be negative.
3870  */
3871 inline double
3872 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3874     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]);
3877 /**
3878  * Return new node in subpath with given properties.
3879  * \param pos Position of node.
3880  * \param ppos Handle position in previous direction
3881  * \param npos Handle position in previous direction
3882  */
3883 Inkscape::NodePath::Node *
3884 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)
3886     g_assert(sp);
3887     g_assert(sp->nodepath);
3888     g_assert(sp->nodepath->desktop);
3890     if (nodechunk == NULL)
3891         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3893     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3895     n->subpath  = sp;
3897     if (type != Inkscape::NodePath::NODE_NONE) {
3898         // use the type from sodipodi:nodetypes
3899         n->type = type;
3900     } else {
3901         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3902             // points are (almost) collinear
3903             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3904                 // endnode, or a node with a retracted handle
3905                 n->type = Inkscape::NodePath::NODE_CUSP;
3906             } else {
3907                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3908             }
3909         } else {
3910             n->type = Inkscape::NodePath::NODE_CUSP;
3911         }
3912     }
3914     n->code     = code;
3915     n->selected = FALSE;
3916     n->pos      = *pos;
3917     n->p.pos    = *ppos;
3918     n->n.pos    = *npos;
3920     n->dragging_out = NULL;
3922     Inkscape::NodePath::Node *prev;
3923     if (next) {
3924         //g_assert(g_list_find(sp->nodes, next));
3925         prev = next->p.other;
3926     } else {
3927         prev = sp->last;
3928     }
3930     if (prev)
3931         prev->n.other = n;
3932     else
3933         sp->first = n;
3935     if (next)
3936         next->p.other = n;
3937     else
3938         sp->last = n;
3940     n->p.other = prev;
3941     n->n.other = next;
3943     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"));
3944     sp_knot_set_position(n->knot, pos, 0);
3946     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3947     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3948     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3949     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3950     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3951     sp_knot_update_ctrl(n->knot);
3953     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3954     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3955     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3956     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3957     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3958     sp_knot_show(n->knot);
3960     // We only create handle knots and lines on demand
3961     n->p.knot = NULL;
3962     n->p.line = NULL;
3963     n->n.knot = NULL;
3964     n->n.line = NULL;
3966     sp->nodes = g_list_prepend(sp->nodes, n);
3968     return n;
3971 /**
3972  * Destroy node and its knots, link neighbors in subpath.
3973  */
3974 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3976     g_assert(node);
3977     g_assert(node->subpath);
3978     g_assert(SP_IS_KNOT(node->knot));
3980    Inkscape::NodePath::SubPath *sp = node->subpath;
3982     if (node->selected) { // first, deselect
3983         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3984         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3985     }
3987     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3989     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
3990     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
3991     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
3992     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
3993     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
3994     g_object_unref(G_OBJECT(node->knot));
3996     if (node->p.knot) {
3997         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
3998         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
3999         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4000         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4001         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4002         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4003         g_object_unref(G_OBJECT(node->p.knot));
4004     }
4006     if (node->n.knot) {
4007         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4008         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4009         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4010         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4011         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4012         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4013         g_object_unref(G_OBJECT(node->n.knot));
4014     }
4016     if (node->p.line)
4017         gtk_object_destroy(GTK_OBJECT(node->p.line));
4018     if (node->n.line)
4019         gtk_object_destroy(GTK_OBJECT(node->n.line));
4021     if (sp->nodes) { // there are others nodes on the subpath
4022         if (sp->closed) {
4023             if (sp->first == node) {
4024                 g_assert(sp->last == node);
4025                 sp->first = node->n.other;
4026                 sp->last = sp->first;
4027             }
4028             node->p.other->n.other = node->n.other;
4029             node->n.other->p.other = node->p.other;
4030         } else {
4031             if (sp->first == node) {
4032                 sp->first = node->n.other;
4033                 sp->first->code = NR_MOVETO;
4034             }
4035             if (sp->last == node) sp->last = node->p.other;
4036             if (node->p.other) node->p.other->n.other = node->n.other;
4037             if (node->n.other) node->n.other->p.other = node->p.other;
4038         }
4039     } else { // this was the last node on subpath
4040         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4041     }
4043     g_mem_chunk_free(nodechunk, node);
4046 /**
4047  * Returns one of the node's two sides.
4048  * \param which Indicates which side.
4049  * \return Pointer to previous node side if which==-1, next if which==1.
4050  */
4051 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4053     g_assert(node);
4055     switch (which) {
4056         case -1:
4057             return &node->p;
4058         case 1:
4059             return &node->n;
4060         default:
4061             break;
4062     }
4064     g_assert_not_reached();
4066     return NULL;
4069 /**
4070  * Return the other side of the node, given one of its sides.
4071  */
4072 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4074     g_assert(node);
4076     if (me == &node->p) return &node->n;
4077     if (me == &node->n) return &node->p;
4079     g_assert_not_reached();
4081     return NULL;
4084 /**
4085  * Return NRPathcode on the given side of the node.
4086  */
4087 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4089     g_assert(node);
4091     if (me == &node->p) {
4092         if (node->p.other) return (NRPathcode)node->code;
4093         return NR_MOVETO;
4094     }
4096     if (me == &node->n) {
4097         if (node->n.other) return (NRPathcode)node->n.other->code;
4098         return NR_MOVETO;
4099     }
4101     g_assert_not_reached();
4103     return NR_END;
4106 /**
4107  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
4108  */
4109 Inkscape::NodePath::Node *
4110 sp_nodepath_get_node_by_index(int index)
4112     Inkscape::NodePath::Node *e = NULL;
4114     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4115     if (!nodepath) {
4116         return e;
4117     }
4119     //find segment
4120     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4122         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4123         int n = g_list_length(sp->nodes);
4124         if (sp->closed) {
4125             n++;
4126         }
4128         //if the piece belongs to this subpath grab it
4129         //otherwise move onto the next subpath
4130         if (index < n) {
4131             e = sp->first;
4132             for (int i = 0; i < index; ++i) {
4133                 e = e->n.other;
4134             }
4135             break;
4136         } else {
4137             if (sp->closed) {
4138                 index -= (n+1);
4139             } else {
4140                 index -= n;
4141             }
4142         }
4143     }
4145     return e;
4148 /**
4149  * Returns plain text meaning of node type.
4150  */
4151 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4153     unsigned retracted = 0;
4154     bool endnode = false;
4156     for (int which = -1; which <= 1; which += 2) {
4157         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4158         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4159             retracted ++;
4160         if (!side->other)
4161             endnode = true;
4162     }
4164     if (retracted == 0) {
4165         if (endnode) {
4166                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4167                 return _("end node");
4168         } else {
4169             switch (node->type) {
4170                 case Inkscape::NodePath::NODE_CUSP:
4171                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4172                     return _("cusp");
4173                 case Inkscape::NodePath::NODE_SMOOTH:
4174                     // TRANSLATORS: "smooth" is an adjective here
4175                     return _("smooth");
4176                 case Inkscape::NodePath::NODE_SYMM:
4177                     return _("symmetric");
4178             }
4179         }
4180     } else if (retracted == 1) {
4181         if (endnode) {
4182             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4183             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4184         } else {
4185             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4186         }
4187     } else {
4188         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4189     }
4191     return NULL;
4194 /**
4195  * Handles content of statusbar as long as node tool is active.
4196  */
4197 void
4198 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
4200     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");
4201     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4203     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4204     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4205     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4206     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4208     SPDesktop *desktop = NULL;
4209     if (nodepath) {
4210         desktop = nodepath->desktop;
4211     } else {
4212         desktop = SP_ACTIVE_DESKTOP;
4213     }
4215     SPEventContext *ec = desktop->event_context;
4216     if (!ec) return;
4217     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4218     if (!mc) return;
4220     if (selected_nodes == 0) {
4221         Inkscape::Selection *sel = desktop->selection;
4222         if (!sel || sel->isEmpty()) {
4223             mc->setF(Inkscape::NORMAL_MESSAGE,
4224                      _("Select a single object to edit its nodes or handles."));
4225         } else {
4226             if (nodepath) {
4227             mc->setF(Inkscape::NORMAL_MESSAGE,
4228                      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.",
4229                               "<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.",
4230                               total_nodes),
4231                      total_nodes);
4232             } else {
4233                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4234                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4235                 } else {
4236                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4237                 }
4238             }
4239         }
4240     } else if (nodepath && selected_nodes == 1) {
4241         mc->setF(Inkscape::NORMAL_MESSAGE,
4242                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4243                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4244                           total_nodes),
4245                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4246     } else {
4247         if (selected_subpaths > 1) {
4248             mc->setF(Inkscape::NORMAL_MESSAGE,
4249                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4250                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4251                               total_nodes),
4252                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4253         } else {
4254             mc->setF(Inkscape::NORMAL_MESSAGE,
4255                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4256                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4257                               total_nodes),
4258                      selected_nodes, total_nodes, when_selected);
4259         }
4260     }
4264 /*
4265   Local Variables:
4266   mode:c++
4267   c-file-style:"stroustrup"
4268   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4269   indent-tabs-mode:nil
4270   fill-column:99
4271   End:
4272 */
4273 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :