Code

69827bd848552cf30a67afce908b5d2fad9f871a
[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     sp_nodepath_ensure_livarot_path(np);
216     return np;
219 /**
220  * Destroys nodepath's subpaths, then itself, also tell context about it.
221  */
222 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
224     if (!np)  //soft fail, like delete
225         return;
227     while (np->subpaths) {
228         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
229     }
231     //Inform the context that made me, if any, that I am gone.
232     if (np->nodeContext)
233         np->nodeContext->nodepath = NULL;
235     g_assert(!np->selected);
237     if (np->livarot_path) {
238         delete np->livarot_path;
239         np->livarot_path = NULL;
240     }
242     np->desktop = NULL;
244     g_free(np);
248 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
250     if (np && np->livarot_path == NULL && np->path && SP_IS_ITEM(np->path)) {
251         np->livarot_path = Path_for_item (np->path, true, true);
252         if (np->livarot_path)
253             np->livarot_path->ConvertWithBackData(0.01);
254     }
258 /**
259  *  Return the node count of a given NodeSubPath.
260  */
261 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
263     if (!subpath)
264         return 0;
265     gint nodeCount = g_list_length(subpath->nodes);
266     return nodeCount;
269 /**
270  *  Return the node count of a given NodePath.
271  */
272 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
274     if (!np)
275         return 0;
276     gint nodeCount = 0;
277     for (GList *item = np->subpaths ; item ; item=item->next) {
278        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
279         nodeCount += g_list_length(subpath->nodes);
280     }
281     return nodeCount;
284 /**
285  *  Return the subpath count of a given NodePath.
286  */
287 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
289     if (!np)
290         return 0;
291     return g_list_length (np->subpaths);
294 /**
295  *  Return the selected node count of a given NodePath.
296  */
297 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
299     if (!np)
300         return 0;
301     return g_list_length (np->selected);
304 /**
305  *  Return the number of subpaths where nodes are selected in a given NodePath.
306  */
307 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
309     if (!np)
310         return 0;
311     if (!np->selected)
312         return 0;
313     if (!np->selected->next)
314         return 1;
315     gint count = 0;
316     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
317         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
318         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
319             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
320             if (node->selected) {
321                 count ++;
322                 break;
323             }
324         }
325     }
326     return count;
328  
329 /**
330  * Clean up a nodepath after editing.
331  *
332  * Currently we are deleting trivial subpaths.
333  */
334 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
336     GList *badSubPaths = NULL;
338     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
339     for (GList *l = nodepath->subpaths; l ; l=l->next) {
340        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
341        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
342             badSubPaths = g_list_append(badSubPaths, sp);
343     }
345     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
346     //also removes the subpath from nodepath->subpaths
347     for (GList *l = badSubPaths; l ; l=l->next) {
348        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
349         sp_nodepath_subpath_destroy(sp);
350     }
352     g_list_free(badSubPaths);
355 /**
356  * Create new nodepath from b, make it subpath of np.
357  * \param t The node type.
358  * \todo Fixme: t should be a proper type, rather than gchar
359  */
360 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
362     NR::Point ppos, pos, npos;
364     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
366     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
367     bool const closed = (b->code == NR_MOVETO);
369     pos = NR::Point(b->x3, b->y3) * np->i2d;
370     if (b[1].code == NR_CURVETO) {
371         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
372     } else {
373         npos = pos;
374     }
375     Inkscape::NodePath::Node *n;
376     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
377     g_assert(sp->first == n);
378     g_assert(sp->last  == n);
380     b++;
381     t++;
382     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
383         pos = NR::Point(b->x3, b->y3) * np->i2d;
384         if (b->code == NR_CURVETO) {
385             ppos = NR::Point(b->x2, b->y2) * np->i2d;
386         } else {
387             ppos = pos;
388         }
389         if (b[1].code == NR_CURVETO) {
390             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
391         } else {
392             npos = pos;
393         }
394         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
395         b++;
396         t++;
397     }
399     if (closed) sp_nodepath_subpath_close(sp);
401     return b;
404 /**
405  * Convert from sodipodi:nodetypes to new style type string.
406  */
407 static gchar *parse_nodetypes(gchar const *types, gint length)
409     g_assert(length > 0);
411     gchar *typestr = g_new(gchar, length + 1);
413     gint pos = 0;
415     if (types) {
416         for (gint i = 0; types[i] && ( i < length ); i++) {
417             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
418             if (types[i] != '\0') {
419                 switch (types[i]) {
420                     case 's':
421                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
422                         break;
423                     case 'z':
424                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
425                         break;
426                     case 'c':
427                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
428                         break;
429                     default:
430                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
431                         break;
432                 }
433             }
434         }
435     }
437     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
439     return typestr;
442 /**
443  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
444  * updated but repr is not (for speed). Used during curve and node drag.
445  */
446 static void update_object(Inkscape::NodePath::Path *np)
448     g_assert(np);
450     SPCurve *curve = create_curve(np);
452     sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
454     sp_curve_unref(curve);
457 /**
458  * Update XML path node with data from path object.
459  */
460 static void update_repr_internal(Inkscape::NodePath::Path *np)
462     g_assert(np);
464     Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
466     SPCurve *curve = create_curve(np);
467     gchar *typestr = create_typestr(np);
468     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
470     if (repr->attribute("d") == NULL || strcmp(svgpath, repr->attribute("d"))) { // d changed
471         np->local_change++;
472         repr->setAttribute("d", svgpath);
473     }
475     if (repr->attribute("sodipodi:nodetypes") == NULL || strcmp(typestr, repr->attribute("sodipodi:nodetypes"))) { // nodetypes changed
476         np->local_change++;
477         repr->setAttribute("sodipodi:nodetypes", typestr);
478     }
480     g_free(svgpath);
481     g_free(typestr);
482     sp_curve_unref(curve);
485 /**
486  * Update XML path node with data from path object, commit changes forever.
487  */
488 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
490     //fixme: np can be NULL, so check before proceeding
491     g_return_if_fail(np != NULL);
493     if (np->livarot_path) {
494         delete np->livarot_path;
495         np->livarot_path = NULL;
496     }
498     update_repr_internal(np);
499     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
500     
501     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE, 
502                      annotation);
505 /**
506  * Update XML path node with data from path object, commit changes with undo.
507  */
508 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
510     if (np->livarot_path) {
511         delete np->livarot_path;
512         np->livarot_path = NULL;
513     }
515     update_repr_internal(np);
516     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE, 
517                            annotation);
520 /**
521  * Make duplicate of path, replace corresponding XML node in tree, commit.
522  */
523 static void stamp_repr(Inkscape::NodePath::Path *np)
525     g_assert(np);
527     Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
528     Inkscape::XML::Node *new_repr = old_repr->duplicate();
530     // remember the position of the item
531     gint pos = old_repr->position();
532     // remember parent
533     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
535     SPCurve *curve = create_curve(np);
536     gchar *typestr = create_typestr(np);
538     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
540     new_repr->setAttribute("d", svgpath);
541     new_repr->setAttribute("sodipodi:nodetypes", typestr);
543     // add the new repr to the parent
544     parent->appendChild(new_repr);
545     // move to the saved position
546     new_repr->setPosition(pos > 0 ? pos : 0);
548     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
549                      _("Stamp"));
551     Inkscape::GC::release(new_repr);
552     g_free(svgpath);
553     g_free(typestr);
554     sp_curve_unref(curve);
557 /**
558  * Create curve from path.
559  */
560 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
562     SPCurve *curve = sp_curve_new();
564     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
565        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
566         sp_curve_moveto(curve,
567                         sp->first->pos * np->d2i);
568        Inkscape::NodePath::Node *n = sp->first->n.other;
569         while (n) {
570             NR::Point const end_pt = n->pos * np->d2i;
571             switch (n->code) {
572                 case NR_LINETO:
573                     sp_curve_lineto(curve, end_pt);
574                     break;
575                 case NR_CURVETO:
576                     sp_curve_curveto(curve,
577                                      n->p.other->n.pos * np->d2i,
578                                      n->p.pos * np->d2i,
579                                      end_pt);
580                     break;
581                 default:
582                     g_assert_not_reached();
583                     break;
584             }
585             if (n != sp->last) {
586                 n = n->n.other;
587             } else {
588                 n = NULL;
589             }
590         }
591         if (sp->closed) {
592             sp_curve_closepath(curve);
593         }
594     }
596     return curve;
599 /**
600  * Convert path type string to sodipodi:nodetypes style.
601  */
602 static gchar *create_typestr(Inkscape::NodePath::Path *np)
604     gchar *typestr = g_new(gchar, 32);
605     gint len = 32;
606     gint pos = 0;
608     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
609        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
611         if (pos >= len) {
612             typestr = g_renew(gchar, typestr, len + 32);
613             len += 32;
614         }
616         typestr[pos++] = 'c';
618        Inkscape::NodePath::Node *n;
619         n = sp->first->n.other;
620         while (n) {
621             gchar code;
623             switch (n->type) {
624                 case Inkscape::NodePath::NODE_CUSP:
625                     code = 'c';
626                     break;
627                 case Inkscape::NodePath::NODE_SMOOTH:
628                     code = 's';
629                     break;
630                 case Inkscape::NodePath::NODE_SYMM:
631                     code = 'z';
632                     break;
633                 default:
634                     g_assert_not_reached();
635                     code = '\0';
636                     break;
637             }
639             if (pos >= len) {
640                 typestr = g_renew(gchar, typestr, len + 32);
641                 len += 32;
642             }
644             typestr[pos++] = code;
646             if (n != sp->last) {
647                 n = n->n.other;
648             } else {
649                 n = NULL;
650             }
651         }
652     }
654     if (pos >= len) {
655         typestr = g_renew(gchar, typestr, len + 1);
656         len += 1;
657     }
659     typestr[pos++] = '\0';
661     return typestr;
664 /**
665  * Returns current path in context.
666  */
667 static Inkscape::NodePath::Path *sp_nodepath_current()
669     if (!SP_ACTIVE_DESKTOP) {
670         return NULL;
671     }
673     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
675     if (!SP_IS_NODE_CONTEXT(event_context)) {
676         return NULL;
677     }
679     return SP_NODE_CONTEXT(event_context)->nodepath;
684 /**
685  \brief Fills node and handle positions for three nodes, splitting line
686   marked by end at distance t.
687  */
688 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
690     g_assert(new_path != NULL);
691     g_assert(end      != NULL);
693     g_assert(end->p.other == new_path);
694    Inkscape::NodePath::Node *start = new_path->p.other;
695     g_assert(start);
697     if (end->code == NR_LINETO) {
698         new_path->type =Inkscape::NodePath::NODE_CUSP;
699         new_path->code = NR_LINETO;
700         new_path->pos  = (t * start->pos + (1 - t) * end->pos);
701     } else {
702         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
703         new_path->code = NR_CURVETO;
704         gdouble s      = 1 - t;
705         for (int dim = 0; dim < 2; dim++) {
706             NR::Coord const f000 = start->pos[dim];
707             NR::Coord const f001 = start->n.pos[dim];
708             NR::Coord const f011 = end->p.pos[dim];
709             NR::Coord const f111 = end->pos[dim];
710             NR::Coord const f00t = s * f000 + t * f001;
711             NR::Coord const f01t = s * f001 + t * f011;
712             NR::Coord const f11t = s * f011 + t * f111;
713             NR::Coord const f0tt = s * f00t + t * f01t;
714             NR::Coord const f1tt = s * f01t + t * f11t;
715             NR::Coord const fttt = s * f0tt + t * f1tt;
716             start->n.pos[dim]    = f00t;
717             new_path->p.pos[dim] = f0tt;
718             new_path->pos[dim]   = fttt;
719             new_path->n.pos[dim] = f1tt;
720             end->p.pos[dim]      = f11t;
721         }
722     }
725 /**
726  * Adds new node on direct line between two nodes, activates handles of all
727  * three nodes.
728  */
729 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
731     g_assert(end);
732     g_assert(end->subpath);
733     g_assert(g_list_find(end->subpath->nodes, end));
735    Inkscape::NodePath::Node *start = end->p.other;
736     g_assert( start->n.other == end );
737    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
738                                                end,
739                                               Inkscape::NodePath::NODE_SMOOTH,
740                                                (NRPathcode)end->code,
741                                                &start->pos, &start->pos, &start->n.pos);
742     sp_nodepath_line_midpoint(newnode, end, t);
744     sp_node_update_handles(start);
745     sp_node_update_handles(newnode);
746     sp_node_update_handles(end);
748     return newnode;
751 /**
752 \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
753 */
754 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
756     g_assert(node);
757     g_assert(node->subpath);
758     g_assert(g_list_find(node->subpath->nodes, node));
760    Inkscape::NodePath::SubPath *sp = node->subpath;
761     Inkscape::NodePath::Path *np    = sp->nodepath;
763     if (sp->closed) {
764         sp_nodepath_subpath_open(sp, node);
765         return sp->first;
766     } else {
767         // no break for end nodes
768         if (node == sp->first) return NULL;
769         if (node == sp->last ) return NULL;
771         // create a new subpath
772        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
774         // duplicate the break node as start of the new subpath
775        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
777         while (node->n.other) { // copy the remaining nodes into the new subpath
778            Inkscape::NodePath::Node *n  = node->n.other;
779            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);
780             if (n->selected) {
781                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
782             }
783             sp_nodepath_node_destroy(n); // remove the point on the original subpath
784         }
786         return newnode;
787     }
790 /**
791  * Duplicate node and connect to neighbours.
792  */
793 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
795     g_assert(node);
796     g_assert(node->subpath);
797     g_assert(g_list_find(node->subpath->nodes, node));
799    Inkscape::NodePath::SubPath *sp = node->subpath;
801     NRPathcode code = (NRPathcode) node->code;
802     if (code == NR_MOVETO) { // if node is the endnode,
803         node->code = NR_LINETO; // new one is inserted before it, so change that to line
804     }
806     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
808     if (!node->n.other || !node->p.other) // if node is an endnode, select it
809         return node;
810     else
811         return newnode; // otherwise select the newly created node
814 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
816     node->p.pos = (node->pos + (node->pos - node->n.pos));
819 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
821     node->n.pos = (node->pos + (node->pos - node->p.pos));
824 /**
825  * Change line type at node, with side effects on neighbours.
826  */
827 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
829     g_assert(end);
830     g_assert(end->subpath);
831     g_assert(end->p.other);
833     if (end->code == static_cast< guint > ( code ) )
834         return;
836    Inkscape::NodePath::Node *start = end->p.other;
838     end->code = code;
840     if (code == NR_LINETO) {
841         if (start->code == NR_LINETO) start->type =Inkscape::NodePath::NODE_CUSP;
842         if (end->n.other) {
843             if (end->n.other->code == NR_LINETO) end->type =Inkscape::NodePath::NODE_CUSP;
844         }
845         sp_node_adjust_handle(start, -1);
846         sp_node_adjust_handle(end, 1);
847     } else {
848         NR::Point delta = end->pos - start->pos;
849         start->n.pos = start->pos + delta / 3;
850         end->p.pos = end->pos - delta / 3;
851         sp_node_adjust_handle(start, 1);
852         sp_node_adjust_handle(end, -1);
853     }
855     sp_node_update_handles(start);
856     sp_node_update_handles(end);
859 /**
860  * Change node type, and its handles accordingly.
861  */
862 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
864     g_assert(node);
865     g_assert(node->subpath);
867     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
868         return node;
870     if ((node->p.other != NULL) && (node->n.other != NULL)) {
871         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
872             type =Inkscape::NodePath::NODE_CUSP;
873         }
874     }
876     node->type = type;
878     if (node->type == Inkscape::NodePath::NODE_CUSP) {
879         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
880         node->knot->setSize (node->selected? 11 : 9);
881         sp_knot_update_ctrl(node->knot);
882     } else {
883         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
884         node->knot->setSize (node->selected? 9 : 7);
885         sp_knot_update_ctrl(node->knot);
886     }
888     // if one of handles is mouseovered, preserve its position
889     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
890         sp_node_adjust_handle(node, 1);
891     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
892         sp_node_adjust_handle(node, -1);
893     } else {
894         sp_node_adjust_handles(node);
895     }
897     sp_node_update_handles(node);
899     sp_nodepath_update_statusbar(node->subpath->nodepath);
901     return node;
904 /**
905  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
906  * adjacent segments from lines to curves.
907 */
908 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
910     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
911         if ((node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos)) {
912             // convert adjacent segment BEFORE to curve
913             node->code = NR_CURVETO;
914             NR::Point delta;
915             if (node->n.other != NULL)
916                 delta = node->n.other->pos - node->p.other->pos;
917             else
918                 delta = node->pos - node->p.other->pos;
919             node->p.pos = node->pos - delta / 4;
920             sp_node_update_handles(node);
921         }
923         if ((node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos)) {
924             // convert adjacent segment AFTER to curve
925             node->n.other->code = NR_CURVETO;
926             NR::Point delta;
927             if (node->p.other != NULL)
928                 delta = node->p.other->pos - node->n.other->pos;
929             else
930                 delta = node->pos - node->n.other->pos;
931             node->n.pos = node->pos - delta / 4;
932             sp_node_update_handles(node);
933         }
934     }
936     sp_nodepath_set_node_type (node, type);
939 /**
940  * Move node to point, and adjust its and neighbouring handles.
941  */
942 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
944     NR::Point delta = p - node->pos;
945     node->pos = p;
947     node->p.pos += delta;
948     node->n.pos += delta;
950     if (node->p.other) {
951         if (node->code == NR_LINETO) {
952             sp_node_adjust_handle(node, 1);
953             sp_node_adjust_handle(node->p.other, -1);
954         }
955     }
956     if (node->n.other) {
957         if (node->n.other->code == NR_LINETO) {
958             sp_node_adjust_handle(node, -1);
959             sp_node_adjust_handle(node->n.other, 1);
960         }
961     }
963     // this function is only called from batch movers that will update display at the end
964     // themselves, so here we just move all the knots without emitting move signals, for speed
965     sp_node_update_handles(node, false);
968 /**
969  * Call sp_node_moveto() for node selection and handle possible snapping.
970  */
971 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
972                                             bool const snap = true)
974     NR::Coord best = NR_HUGE;
975     NR::Point delta(dx, dy);
976     NR::Point best_pt = delta;
978     if (snap) {
979         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
980         
981         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
982             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
983             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAP_POINT, n->pos + delta, NULL);
984             if (s.getDistance() < best) {
985                 best = s.getDistance();
986                 best_pt = s.getPoint() - n->pos;
987             }
988         }
989     }
991     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
992         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
993         sp_node_moveto(n, n->pos + best_pt);
994     }
996     // do not update repr here so that node dragging is acceptably fast
997     update_object(nodepath);
1000 /**
1001 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1002 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1003 near x = 0.
1004  */
1005 double
1006 sculpt_profile (double x, double alpha, guint profile)
1008     if (x >= 1)
1009         return 0;
1010     if (x <= 0)
1011         return 1;
1013     switch (profile) {
1014         case SCULPT_PROFILE_LINEAR:
1015         return 1 - x;
1016         case SCULPT_PROFILE_BELL:
1017         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1018         case SCULPT_PROFILE_ELLIPTIC:
1019         return sqrt(1 - x*x);
1020     }
1022     return 1;
1025 double
1026 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1028     // extremely primitive for now, don't have time to look for the real one
1029     double lower = NR::L2(b - a);
1030     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1031     return (lower + upper)/2;
1034 void
1035 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1037     n->pos = n->origin + delta;
1038     n->n.pos = n->n.origin + delta_n;
1039     n->p.pos = n->p.origin + delta_p;
1040     sp_node_adjust_handles(n);
1041     sp_node_update_handles(n, false);
1044 /**
1045  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1046  * on how far they are from the dragged node n.
1047  */
1048 static void 
1049 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1051     g_assert (n);
1052     g_assert (nodepath);
1053     g_assert (n->subpath->nodepath == nodepath);
1055     double pressure = n->knot->pressure;
1056     if (pressure == 0)
1057         pressure = 0.5; // default
1058     pressure = CLAMP (pressure, 0.2, 0.8);
1060     // map pressure to alpha = 1/5 ... 5
1061     double alpha = 1 - 2 * fabs(pressure - 0.5);
1062     if (pressure > 0.5)
1063         alpha = 1/alpha;
1065     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1067     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1068         // Only one subpath has selected nodes:
1069         // use linear mode, where the distance from n to node being dragged is calculated along the path
1071         double n_sel_range = 0, p_sel_range = 0;
1072         guint n_nodes = 0, p_nodes = 0;
1073         guint n_sel_nodes = 0, p_sel_nodes = 0;
1075         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1076         {
1077             double n_range = 0, p_range = 0;
1078             bool n_going = true, p_going = true;
1079             Inkscape::NodePath::Node *n_node = n;
1080             Inkscape::NodePath::Node *p_node = n;
1081             do {
1082                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1083                 if (n_node && n_going)
1084                     n_node = n_node->n.other;
1085                 if (n_node == NULL) {
1086                     n_going = false;
1087                 } else {
1088                     n_nodes ++;
1089                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1090                     if (n_node->selected) {
1091                         n_sel_nodes ++;
1092                         n_sel_range = n_range;
1093                     }
1094                     if (n_node == p_node) {
1095                         n_going = false;
1096                         p_going = false;
1097                     }
1098                 }
1099                 if (p_node && p_going)
1100                     p_node = p_node->p.other;
1101                 if (p_node == NULL) {
1102                     p_going = false;
1103                 } else {
1104                     p_nodes ++;
1105                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1106                     if (p_node->selected) {
1107                         p_sel_nodes ++;
1108                         p_sel_range = p_range;
1109                     }
1110                     if (p_node == n_node) {
1111                         n_going = false;
1112                         p_going = false;
1113                     }
1114                 }
1115             } while (n_going || p_going);
1116         }
1118         // Second pass: actually move nodes in this subpath
1119         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1120         {
1121             double n_range = 0, p_range = 0;
1122             bool n_going = true, p_going = true;
1123             Inkscape::NodePath::Node *n_node = n;
1124             Inkscape::NodePath::Node *p_node = n;
1125             do {
1126                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1127                 if (n_node && n_going)
1128                     n_node = n_node->n.other;
1129                 if (n_node == NULL) {
1130                     n_going = false;
1131                 } else {
1132                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1133                     if (n_node->selected) {
1134                         sp_nodepath_move_node_and_handles (n_node, 
1135                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1136                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1137                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1138                     }
1139                     if (n_node == p_node) {
1140                         n_going = false;
1141                         p_going = false;
1142                     }
1143                 }
1144                 if (p_node && p_going)
1145                     p_node = p_node->p.other;
1146                 if (p_node == NULL) {
1147                     p_going = false;
1148                 } else {
1149                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1150                     if (p_node->selected) {
1151                         sp_nodepath_move_node_and_handles (p_node, 
1152                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1153                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1154                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1155                     }
1156                     if (p_node == n_node) {
1157                         n_going = false;
1158                         p_going = false;
1159                     }
1160                 }
1161             } while (n_going || p_going);
1162         }
1164     } else {
1165         // Multiple subpaths have selected nodes:
1166         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1167         // TODO: correct these distances taking into account their angle relative to the bisector, so as to 
1168         // fix the pear-like shape when sculpting e.g. a ring
1170         // First pass: calculate range
1171         gdouble direct_range = 0;
1172         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1173             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1174             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1175                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1176                 if (node->selected) {
1177                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1178                 }
1179             }
1180         }
1182         // Second pass: actually move nodes
1183         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1184             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1185             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1186                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1187                 if (node->selected) {
1188                     if (direct_range > 1e-6) {
1189                         sp_nodepath_move_node_and_handles (node,
1190                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1191                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1192                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1193                     } else {
1194                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1195                     }
1197                 }
1198             }
1199         }
1200     }
1202     // do not update repr here so that node dragging is acceptably fast
1203     update_object(nodepath);
1207 /**
1208  * Move node selection to point, adjust its and neighbouring handles,
1209  * handle possible snapping, and commit the change with possible undo.
1210  */
1211 void
1212 sp_node_selected_move(gdouble dx, gdouble dy)
1214     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1215     if (!nodepath) return;
1217     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1219     if (dx == 0) {
1220         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1221     } else if (dy == 0) {
1222         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1223     } else {
1224         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1225     }
1228 /**
1229  * Move node selection off screen and commit the change.
1230  */
1231 void
1232 sp_node_selected_move_screen(gdouble dx, gdouble dy)
1234     // borrowed from sp_selection_move_screen in selection-chemistry.c
1235     // we find out the current zoom factor and divide deltas by it
1236     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1238     gdouble zoom = desktop->current_zoom();
1239     gdouble zdx = dx / zoom;
1240     gdouble zdy = dy / zoom;
1242     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1243     if (!nodepath) return;
1245     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1247     if (dx == 0) {
1248         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1249     } else if (dy == 0) {
1250         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1251     } else {
1252         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1253     }
1256 /** If they don't yet exist, creates knot and line for the given side of the node */
1257 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1259     if (!side->knot) {
1260         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"));
1262         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1263         side->knot->setSize (7);
1264         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1265         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1266         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1267         sp_knot_update_ctrl(side->knot);
1269         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1270         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1271         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1272         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1273         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1274         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1275     }
1277     if (!side->line) {
1278         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1279                                         SP_TYPE_CTRLLINE, NULL);
1280     }
1283 /**
1284  * Ensure the given handle of the node is visible/invisible, update its screen position
1285  */
1286 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1288     g_assert(node != NULL);
1290    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1291     NRPathcode code = sp_node_path_code_from_side(node, side);
1293     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1295     if (show_handle) {
1296         if (!side->knot) { // No handle knot at all
1297             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1298             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1299             side->knot->pos = side->pos;
1300             if (side->knot->item) 
1301                 SP_CTRL(side->knot->item)->moveto(side->pos);
1302             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1303             sp_knot_show(side->knot);
1304         } else {
1305             if (side->knot->pos != side->pos) { // only if it's really moved
1306                 if (fire_move_signals) {
1307                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1308                 } else {
1309                     sp_knot_moveto(side->knot, &side->pos);
1310                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1311                 }
1312             }
1313             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1314                 sp_knot_show(side->knot);
1315             }
1316         }
1317         sp_canvas_item_show(side->line);
1318     } else {
1319         if (side->knot) {
1320             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1321                 sp_knot_hide(side->knot);
1322             }
1323         }
1324         if (side->line) {
1325             sp_canvas_item_hide(side->line);
1326         }
1327     }
1330 /**
1331  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1332  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1333  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1334  * updated; otherwise, just move the knots silently (used in batch moves).
1335  */
1336 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1338     g_assert(node != NULL);
1340     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1341         sp_knot_show(node->knot);
1342     }
1344     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1345         if (fire_move_signals)
1346             sp_knot_set_position(node->knot, &node->pos, 0);
1347         else 
1348             sp_knot_moveto(node->knot, &node->pos);
1349     }
1351     gboolean show_handles = node->selected;
1352     if (node->p.other != NULL) {
1353         if (node->p.other->selected) show_handles = TRUE;
1354     }
1355     if (node->n.other != NULL) {
1356         if (node->n.other->selected) show_handles = TRUE;
1357     }
1359     if (node->subpath->nodepath->show_handles == false)
1360         show_handles = FALSE;
1362     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1363     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1366 /**
1367  * Call sp_node_update_handles() for all nodes on subpath.
1368  */
1369 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1371     g_assert(subpath != NULL);
1373     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1374         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1375     }
1378 /**
1379  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1380  */
1381 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1383     g_assert(nodepath != NULL);
1385     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1386         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1387     }
1390 void
1391 sp_nodepath_show_handles(bool show)
1393     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1394     if (nodepath == NULL) return;
1396     nodepath->show_handles = show;
1397     sp_nodepath_update_handles(nodepath);
1400 /**
1401  * Adds all selected nodes in nodepath to list.
1402  */
1403 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1405     StlConv<Node *>::list(l, selected);
1406 /// \todo this adds a copying, rework when the selection becomes a stl list
1409 /**
1410  * Align selected nodes on the specified axis.
1411  */
1412 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1414     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1415         return;
1416     }
1418     if ( !nodepath->selected->next ) { // only one node selected
1419         return;
1420     }
1421    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1422     NR::Point dest(pNode->pos);
1423     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1424         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1425         if (pNode) {
1426             dest[axis] = pNode->pos[axis];
1427             sp_node_moveto(pNode, dest);
1428         }
1429     }
1431     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1434 /// Helper struct.
1435 struct NodeSort
1437    Inkscape::NodePath::Node *_node;
1438     NR::Coord _coord;
1439     /// \todo use vectorof pointers instead of calling copy ctor
1440     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1441         _node(node), _coord(node->pos[axis])
1442     {}
1444 };
1446 static bool operator<(NodeSort const &a, NodeSort const &b)
1448     return (a._coord < b._coord);
1451 /**
1452  * Distribute selected nodes on the specified axis.
1453  */
1454 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1456     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1457         return;
1458     }
1460     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1461         return;
1462     }
1464    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1465     std::vector<NodeSort> sorted;
1466     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1467         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1468         if (pNode) {
1469             NodeSort n(pNode, axis);
1470             sorted.push_back(n);
1471             //dest[axis] = pNode->pos[axis];
1472             //sp_node_moveto(pNode, dest);
1473         }
1474     }
1475     std::sort(sorted.begin(), sorted.end());
1476     unsigned int len = sorted.size();
1477     //overall bboxes span
1478     float dist = (sorted.back()._coord -
1479                   sorted.front()._coord);
1480     //new distance between each bbox
1481     float step = (dist) / (len - 1);
1482     float pos = sorted.front()._coord;
1483     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1484           it < sorted.end();
1485           it ++ )
1486     {
1487         NR::Point dest((*it)._node->pos);
1488         dest[axis] = pos;
1489         sp_node_moveto((*it)._node, dest);
1490         pos += step;
1491     }
1493     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1497 /**
1498  * Call sp_nodepath_line_add_node() for all selected segments.
1499  */
1500 void
1501 sp_node_selected_add_node(void)
1503     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1504     if (!nodepath) {
1505         return;
1506     }
1508     GList *nl = NULL;
1510     int n_added = 0;
1512     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1513        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1514         g_assert(t->selected);
1515         if (t->p.other && t->p.other->selected) {
1516             nl = g_list_prepend(nl, t);
1517         }
1518     }
1520     while (nl) {
1521        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1522        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1523        sp_nodepath_node_select(n, TRUE, FALSE);
1524        n_added ++;
1525        nl = g_list_remove(nl, t);
1526     }
1528     /** \todo fixme: adjust ? */
1529     sp_nodepath_update_handles(nodepath);
1531     if (n_added > 1) {
1532         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1533     } else if (n_added > 0) {
1534         sp_nodepath_update_repr(nodepath, _("Add node"));
1535     }
1537     sp_nodepath_update_statusbar(nodepath);
1540 /**
1541  * Select segment nearest to point
1542  */
1543 void
1544 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1546     if (!nodepath) {
1547         return;
1548     }
1550     sp_nodepath_ensure_livarot_path(nodepath);
1551     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1553     //find segment to segment
1554     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1556     //fixme: this can return NULL, so check before proceeding.
1557     g_return_if_fail(e != NULL);
1558     
1559     gboolean force = FALSE;
1560     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1561         force = TRUE;
1562     }
1563     sp_nodepath_node_select(e, (gboolean) toggle, force);
1564     if (e->p.other)
1565         sp_nodepath_node_select(e->p.other, TRUE, force);
1567     sp_nodepath_update_handles(nodepath);
1569     sp_nodepath_update_statusbar(nodepath);
1572 /**
1573  * Add a node nearest to point
1574  */
1575 void
1576 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1578     if (!nodepath) {
1579         return;
1580     }
1582     sp_nodepath_ensure_livarot_path(nodepath);
1583     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1585     //find segment to split
1586     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1588     //don't know why but t seems to flip for lines
1589     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1590         position.t = 1.0 - position.t;
1591     }
1592     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1593     sp_nodepath_node_select(n, FALSE, TRUE);
1595     /* fixme: adjust ? */
1596     sp_nodepath_update_handles(nodepath);
1598     sp_nodepath_update_repr(nodepath, _("Add node"));
1600     sp_nodepath_update_statusbar(nodepath);
1603 /*
1604  * Adjusts a segment so that t moves by a certain delta for dragging
1605  * converts lines to curves
1606  *
1607  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1608  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1609  */
1610 void
1611 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1613     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1615     //fixme: e and e->p can be NULL, so check for those before proceeding
1616     g_return_if_fail(e != NULL);
1617     g_return_if_fail(&e->p != NULL);
1618     
1619     /* feel good is an arbitrary parameter that distributes the delta between handles
1620      * if t of the drag point is less than 1/6 distance form the endpoint only
1621      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1622      */
1623     double feel_good;
1624     if (t <= 1.0 / 6.0)
1625         feel_good = 0;
1626     else if (t <= 0.5)
1627         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1628     else if (t <= 5.0 / 6.0)
1629         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1630     else
1631         feel_good = 1;
1633     //if we're dragging a line convert it to a curve
1634     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1635         sp_nodepath_set_line_type(e, NR_CURVETO);
1636     }
1638     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1639     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1640     e->p.other->n.pos += offsetcoord0;
1641     e->p.pos += offsetcoord1;
1643     // adjust handles of adjacent nodes where necessary
1644     sp_node_adjust_handle(e,1);
1645     sp_node_adjust_handle(e->p.other,-1);
1647     sp_nodepath_update_handles(e->subpath->nodepath);
1649     update_object(e->subpath->nodepath);
1651     sp_nodepath_update_statusbar(e->subpath->nodepath);
1655 /**
1656  * Call sp_nodepath_break() for all selected segments.
1657  */
1658 void sp_node_selected_break()
1660     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1661     if (!nodepath) return;
1663     GList *temp = NULL;
1664     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1665        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1666        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1667         if (nn == NULL) continue; // no break, no new node
1668         temp = g_list_prepend(temp, nn);
1669     }
1671     if (temp) {
1672         sp_nodepath_deselect(nodepath);
1673     }
1674     for (GList *l = temp; l != NULL; l = l->next) {
1675         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1676     }
1678     sp_nodepath_update_handles(nodepath);
1680     sp_nodepath_update_repr(nodepath, _("Break path"));
1683 /**
1684  * Duplicate the selected node(s).
1685  */
1686 void sp_node_selected_duplicate()
1688     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1689     if (!nodepath) {
1690         return;
1691     }
1693     GList *temp = NULL;
1694     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1695        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1696        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1697         if (nn == NULL) continue; // could not duplicate
1698         temp = g_list_prepend(temp, nn);
1699     }
1701     if (temp) {
1702         sp_nodepath_deselect(nodepath);
1703     }
1704     for (GList *l = temp; l != NULL; l = l->next) {
1705         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1706     }
1708     sp_nodepath_update_handles(nodepath);
1710     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1713 /**
1714  *  Join two nodes by merging them into one.
1715  */
1716 void sp_node_selected_join()
1718     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1719     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1721     if (g_list_length(nodepath->selected) != 2) {
1722         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1723         return;
1724     }
1726    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1727    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1729     g_assert(a != b);
1730     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) { 
1731         // someone tried to join an orphan node (i.e. a single-node subpath).
1732         // this is not worth an error message, just fail silently.
1733         return;
1734     }
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     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) { 
1833         // someone tried to join an orphan node (i.e. a single-node subpath).
1834         // this is not worth an error message, just fail silently.
1835         return;
1836     }
1838     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1839         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1840         return;
1841     }
1843     if (a->subpath == b->subpath) {
1844        Inkscape::NodePath::SubPath *sp = a->subpath;
1846         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1847         sp->closed = TRUE;
1849         sp->first->p.other = sp->last;
1850         sp->last->n.other  = sp->first;
1852         sp_node_handle_mirror_p_to_n(sp->last);
1853         sp_node_handle_mirror_n_to_p(sp->first);
1855         sp->first->code = sp->last->code;
1856         sp->first       = sp->last;
1858         sp_nodepath_update_handles(sp->nodepath);
1860         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
1862         return;
1863     }
1865     /* a and b are separate subpaths */
1866    Inkscape::NodePath::SubPath *sa = a->subpath;
1867    Inkscape::NodePath::SubPath *sb = b->subpath;
1869    Inkscape::NodePath::Node *n;
1870     NR::Point p;
1871     NRPathcode code;
1872     if (a == sa->first) {
1873         code = (NRPathcode) sa->first->n.other->code;
1874        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1875         n = sa->last;
1876         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1877         for (n = n->p.other; n != NULL; n = n->p.other) {
1878             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1879         }
1880         sp_nodepath_subpath_destroy(sa);
1881         sa = t;
1882     } else if (a == sa->last) {
1883         code = (NRPathcode)sa->last->code;
1884     } else {
1885         code = NR_END;
1886         g_assert_not_reached();
1887     }
1889     if (b == sb->first) {
1890         n = sb->first;
1891         sp_node_handle_mirror_p_to_n(sa->last);
1892         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1893         sp_node_handle_mirror_n_to_p(sa->last);
1894         for (n = n->n.other; n != NULL; n = n->n.other) {
1895             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1896         }
1897     } else if (b == sb->last) {
1898         n = sb->last;
1899         sp_node_handle_mirror_p_to_n(sa->last);
1900         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1901         sp_node_handle_mirror_n_to_p(sa->last);
1902         for (n = n->p.other; n != NULL; n = n->p.other) {
1903             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1904         }
1905     } else {
1906         g_assert_not_reached();
1907     }
1908     /* and now destroy sb */
1910     sp_nodepath_subpath_destroy(sb);
1912     sp_nodepath_update_handles(sa->nodepath);
1914     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
1917 /**
1918  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1919  */
1920 void sp_node_delete_preserve(GList *nodes_to_delete)
1922     GSList *nodepaths = NULL;
1923     
1924     while (nodes_to_delete) {
1925         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1926         Inkscape::NodePath::SubPath *sp = node->subpath;
1927         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1928         Inkscape::NodePath::Node *sample_cursor = NULL;
1929         Inkscape::NodePath::Node *sample_end = NULL;
1930         Inkscape::NodePath::Node *delete_cursor = node;
1931         bool just_delete = false;
1932         
1933         //find the start of this contiguous selection
1934         //move left to the first node that is not selected
1935         //or the start of the non-closed path
1936         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1937             delete_cursor = curr;
1938         }
1940         //just delete at the beginning of an open path
1941         if (!delete_cursor->p.other) {
1942             sample_cursor = delete_cursor;
1943             just_delete = true;
1944         } else {
1945             sample_cursor = delete_cursor->p.other;
1946         }
1947         
1948         //calculate points for each segment
1949         int rate = 5;
1950         float period = 1.0 / rate;
1951         std::vector<NR::Point> data;
1952         if (!just_delete) {
1953             data.push_back(sample_cursor->pos);
1954             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1955                 //just delete at the end of an open path
1956                 if (!sp->closed && curr == sp->last) {
1957                     just_delete = true;
1958                     break;
1959                 }
1960                 
1961                 //sample points on the contiguous selected segment
1962                 NR::Point *bez;
1963                 bez = new NR::Point [4];
1964                 bez[0] = curr->pos;
1965                 bez[1] = curr->n.pos;
1966                 bez[2] = curr->n.other->p.pos;
1967                 bez[3] = curr->n.other->pos;
1968                 for (int i=1; i<rate; i++) {
1969                     gdouble t = i * period;
1970                     NR::Point p = bezier_pt(3, bez, t);
1971                     data.push_back(p);
1972                 }
1973                 data.push_back(curr->n.other->pos);
1975                 sample_end = curr->n.other;
1976                 //break if we've come full circle or hit the end of the selection
1977                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
1978                     break;
1979                 }
1980             }
1981         }
1983         if (!just_delete) {
1984             //calculate the best fitting single segment and adjust the endpoints
1985             NR::Point *adata;
1986             adata = new NR::Point [data.size()];
1987             copy(data.begin(), data.end(), adata);
1988             
1989             NR::Point *bez;
1990             bez = new NR::Point [4];
1991             //would decreasing error create a better fitting approximation?
1992             gdouble error = 1.0;
1993             gint ret;
1994             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
1996             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
1997             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
1998             //the resulting nodes behave as expected.
1999             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2000             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2001             
2002             //adjust endpoints
2003             sample_cursor->n.pos = bez[1];
2004             sample_end->p.pos = bez[2];
2005         }
2006        
2007         //destroy this contiguous selection
2008         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2009             Inkscape::NodePath::Node *temp = delete_cursor;
2010             if (delete_cursor->n.other == delete_cursor) {
2011                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2012                 delete_cursor = NULL; 
2013             } else {
2014                 delete_cursor = delete_cursor->n.other;
2015             }
2016             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2017             sp_nodepath_node_destroy(temp);
2018         }
2020         sp_nodepath_update_handles(nodepath);
2022         if (!g_slist_find(nodepaths, nodepath))
2023             nodepaths = g_slist_prepend (nodepaths, nodepath);
2024     }
2026     for (GSList *i = nodepaths; i; i = i->next) {
2027         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2028         // different nodepaths will give us one undo event per nodepath
2029         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2031         // if the entire nodepath is removed, delete the selected object.
2032         if (nodepath->subpaths == NULL ||
2033             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2034             //at least 2
2035             sp_nodepath_get_node_count(nodepath) < 2) {
2036             SPDocument *document = sp_desktop_document (nodepath->desktop);
2037             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2038             //delete this nodepath's object, not the entire selection! (though at this time, this
2039             //does not matter)
2040             sp_selection_delete();
2041             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2042                               _("Delete nodes"));
2043         } else {
2044             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2045             sp_nodepath_update_statusbar(nodepath);
2046         }
2047     }
2049     g_slist_free (nodepaths);
2052 /**
2053  * Delete one or more selected nodes.
2054  */
2055 void sp_node_selected_delete()
2057     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2058     if (!nodepath) return;
2059     if (!nodepath->selected) return;
2061     /** \todo fixme: do it the right way */
2062     while (nodepath->selected) {
2063        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2064         sp_nodepath_node_destroy(node);
2065     }
2068     //clean up the nodepath (such as for trivial subpaths)
2069     sp_nodepath_cleanup(nodepath);
2071     sp_nodepath_update_handles(nodepath);
2073     // if the entire nodepath is removed, delete the selected object.
2074     if (nodepath->subpaths == NULL ||
2075         sp_nodepath_get_node_count(nodepath) < 2) {
2076         SPDocument *document = sp_desktop_document (nodepath->desktop);
2077         sp_selection_delete();
2078         sp_document_done (document, SP_VERB_CONTEXT_NODE, 
2079                           _("Delete nodes"));
2080         return;
2081     }
2083     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2085     sp_nodepath_update_statusbar(nodepath);
2088 /**
2089  * Delete one or more segments between two selected nodes.
2090  * This is the code for 'split'.
2091  */
2092 void
2093 sp_node_selected_delete_segment(void)
2095    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2096    Inkscape::NodePath::Node *curr, *next;     //Iterators
2098     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2099     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2101     if (g_list_length(nodepath->selected) != 2) {
2102         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2103                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2104         return;
2105     }
2107     //Selected nodes, not inclusive
2108    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2109    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2111     if ( ( a==b)                       ||  //same node
2112          (a->subpath  != b->subpath )  ||  //not the same path
2113          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2114          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2115     {
2116         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2117                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2118         return;
2119     }
2121     //###########################################
2122     //# BEGIN EDITS
2123     //###########################################
2124     //##################################
2125     //# CLOSED PATH
2126     //##################################
2127     if (a->subpath->closed) {
2130         gboolean reversed = FALSE;
2132         //Since we can go in a circle, we need to find the shorter distance.
2133         //  a->b or b->a
2134         start = end = NULL;
2135         int distance    = 0;
2136         int minDistance = 0;
2137         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2138             if (curr==b) {
2139                 //printf("a to b:%d\n", distance);
2140                 start = a;//go from a to b
2141                 end   = b;
2142                 minDistance = distance;
2143                 //printf("A to B :\n");
2144                 break;
2145             }
2146             distance++;
2147         }
2149         //try again, the other direction
2150         distance = 0;
2151         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2152             if (curr==a) {
2153                 //printf("b to a:%d\n", distance);
2154                 if (distance < minDistance) {
2155                     start    = b;  //we go from b to a
2156                     end      = a;
2157                     reversed = TRUE;
2158                     //printf("B to A\n");
2159                 }
2160                 break;
2161             }
2162             distance++;
2163         }
2166         //Copy everything from 'end' to 'start' to a new subpath
2167        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2168         for (curr=end ; curr ; curr=curr->n.other) {
2169             NRPathcode code = (NRPathcode) curr->code;
2170             if (curr == end)
2171                 code = NR_MOVETO;
2172             sp_nodepath_node_new(t, NULL,
2173                                  (Inkscape::NodePath::NodeType)curr->type, code,
2174                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2175             if (curr == start)
2176                 break;
2177         }
2178         sp_nodepath_subpath_destroy(a->subpath);
2181     }
2185     //##################################
2186     //# OPEN PATH
2187     //##################################
2188     else {
2190         //We need to get the direction of the list between A and B
2191         //Can we walk from a to b?
2192         start = end = NULL;
2193         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2194             if (curr==b) {
2195                 start = a;  //did it!  we go from a to b
2196                 end   = b;
2197                 //printf("A to B\n");
2198                 break;
2199             }
2200         }
2201         if (!start) {//didn't work?  let's try the other direction
2202             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2203                 if (curr==a) {
2204                     start = b;  //did it!  we go from b to a
2205                     end   = a;
2206                     //printf("B to A\n");
2207                     break;
2208                 }
2209             }
2210         }
2211         if (!start) {
2212             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2213                                                      _("Cannot find path between nodes."));
2214             return;
2215         }
2219         //Copy everything after 'end' to a new subpath
2220        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2221         for (curr=end ; curr ; curr=curr->n.other) {
2222             NRPathcode code = (NRPathcode) curr->code;
2223             if (curr == end)
2224                 code = NR_MOVETO;
2225             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2226                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2227         }
2229         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2230         for (curr = start->n.other ; curr  ; curr=next) {
2231             next = curr->n.other;
2232             sp_nodepath_node_destroy(curr);
2233         }
2235     }
2236     //###########################################
2237     //# END EDITS
2238     //###########################################
2240     //clean up the nodepath (such as for trivial subpaths)
2241     sp_nodepath_cleanup(nodepath);
2243     sp_nodepath_update_handles(nodepath);
2245     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2247     sp_nodepath_update_statusbar(nodepath);
2250 /**
2251  * Call sp_nodepath_set_line() for all selected segments.
2252  */
2253 void
2254 sp_node_selected_set_line_type(NRPathcode code)
2256     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2257     if (nodepath == NULL) return;
2259     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2260        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2261         g_assert(n->selected);
2262         if (n->p.other && n->p.other->selected) {
2263             sp_nodepath_set_line_type(n, code);
2264         }
2265     }
2267     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2270 /**
2271  * Call sp_nodepath_convert_node_type() for all selected nodes.
2272  */
2273 void
2274 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
2276     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2277     if (nodepath == NULL) return;
2279     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2280         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2281     }
2283     sp_nodepath_update_repr(nodepath, _("Change node type"));
2286 /**
2287  * Change select status of node, update its own and neighbour handles.
2288  */
2289 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2291     node->selected = selected;
2293     if (selected) {
2294         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2295         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2296         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2297         sp_knot_update_ctrl(node->knot);
2298     } else {
2299         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2300         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2301         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2302         sp_knot_update_ctrl(node->knot);
2303     }
2305     sp_node_update_handles(node);
2306     if (node->n.other) sp_node_update_handles(node->n.other);
2307     if (node->p.other) sp_node_update_handles(node->p.other);
2310 /**
2311 \brief Select a node
2312 \param node     The node to select
2313 \param incremental   If true, add to selection, otherwise deselect others
2314 \param override   If true, always select this node, otherwise toggle selected status
2315 */
2316 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2318     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2320     if (incremental) {
2321         if (override) {
2322             if (!g_list_find(nodepath->selected, node)) {
2323                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2324             }
2325             sp_node_set_selected(node, TRUE);
2326         } else { // toggle
2327             if (node->selected) {
2328                 g_assert(g_list_find(nodepath->selected, node));
2329                 nodepath->selected = g_list_remove(nodepath->selected, node);
2330             } else {
2331                 g_assert(!g_list_find(nodepath->selected, node));
2332                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2333             }
2334             sp_node_set_selected(node, !node->selected);
2335         }
2336     } else {
2337         sp_nodepath_deselect(nodepath);
2338         nodepath->selected = g_list_prepend(nodepath->selected, node);
2339         sp_node_set_selected(node, TRUE);
2340     }
2342     sp_nodepath_update_statusbar(nodepath);
2346 /**
2347 \brief Deselect all nodes in the nodepath
2348 */
2349 void
2350 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2352     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2354     while (nodepath->selected) {
2355         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2356         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2357     }
2358     sp_nodepath_update_statusbar(nodepath);
2361 /**
2362 \brief Select or invert selection of all nodes in the nodepath
2363 */
2364 void
2365 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2367     if (!nodepath) return;
2369     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2370        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2371         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2372            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2373            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2374         }
2375     }
2378 /**
2379  * If nothing selected, does the same as sp_nodepath_select_all();
2380  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2381  * (i.e., similar to "select all in layer", with the "selected" subpaths
2382  * being treated as "layers" in the path).
2383  */
2384 void
2385 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2387     if (!nodepath) return;
2389     if (g_list_length (nodepath->selected) == 0) {
2390         sp_nodepath_select_all (nodepath, invert);
2391         return;
2392     }
2394     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2395     GSList *subpaths = NULL;
2397     for (GList *l = copy; l != NULL; l = l->next) {
2398         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2399         Inkscape::NodePath::SubPath *subpath = n->subpath;
2400         if (!g_slist_find (subpaths, subpath))
2401             subpaths = g_slist_prepend (subpaths, subpath);
2402     }
2404     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2405         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2406         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2407             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2408             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2409         }
2410     }
2412     g_slist_free (subpaths);
2413     g_list_free (copy);
2416 /**
2417  * \brief Select the node after the last selected; if none is selected,
2418  * select the first within path.
2419  */
2420 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2422     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2424    Inkscape::NodePath::Node *last = NULL;
2425     if (nodepath->selected) {
2426         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2427            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2428             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2429             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2430                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2431                 if (node->selected) {
2432                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2433                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2434                             if (spl->next) { // there's a next subpath
2435                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2436                                 last = subpath_next->first;
2437                             } else if (spl->prev) { // there's a previous subpath
2438                                 last = NULL; // to be set later to the first node of first subpath
2439                             } else {
2440                                 last = node->n.other;
2441                             }
2442                         } else {
2443                             last = node->n.other;
2444                         }
2445                     } else {
2446                         if (node->n.other) {
2447                             last = node->n.other;
2448                         } else {
2449                             if (spl->next) { // there's a next subpath
2450                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2451                                 last = subpath_next->first;
2452                             } else if (spl->prev) { // there's a previous subpath
2453                                 last = NULL; // to be set later to the first node of first subpath
2454                             } else {
2455                                 last = (Inkscape::NodePath::Node *) subpath->first;
2456                             }
2457                         }
2458                     }
2459                 }
2460             }
2461         }
2462         sp_nodepath_deselect(nodepath);
2463     }
2465     if (last) { // there's at least one more node after selected
2466         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2467     } else { // no more nodes, select the first one in first subpath
2468        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2469         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2470     }
2473 /**
2474  * \brief Select the node before the first selected; if none is selected,
2475  * select the last within path
2476  */
2477 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2479     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2481    Inkscape::NodePath::Node *last = NULL;
2482     if (nodepath->selected) {
2483         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2484            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2485             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2486                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2487                 if (node->selected) {
2488                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2489                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2490                             if (spl->prev) { // there's a prev subpath
2491                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2492                                 last = subpath_prev->last;
2493                             } else if (spl->next) { // there's a next subpath
2494                                 last = NULL; // to be set later to the last node of last subpath
2495                             } else {
2496                                 last = node->p.other;
2497                             }
2498                         } else {
2499                             last = node->p.other;
2500                         }
2501                     } else {
2502                         if (node->p.other) {
2503                             last = node->p.other;
2504                         } else {
2505                             if (spl->prev) { // there's a prev subpath
2506                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2507                                 last = subpath_prev->last;
2508                             } else if (spl->next) { // there's a next subpath
2509                                 last = NULL; // to be set later to the last node of last subpath
2510                             } else {
2511                                 last = (Inkscape::NodePath::Node *) subpath->last;
2512                             }
2513                         }
2514                     }
2515                 }
2516             }
2517         }
2518         sp_nodepath_deselect(nodepath);
2519     }
2521     if (last) { // there's at least one more node before selected
2522         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2523     } else { // no more nodes, select the last one in last subpath
2524         GList *spl = g_list_last(nodepath->subpaths);
2525        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2526         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2527     }
2530 /**
2531  * \brief Select all nodes that are within the rectangle.
2532  */
2533 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2535     if (!incremental) {
2536         sp_nodepath_deselect(nodepath);
2537     }
2539     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2540        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2541         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2542            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2544             if (b.contains(node->pos)) {
2545                 sp_nodepath_node_select(node, TRUE, TRUE);
2546             }
2547         }
2548     }
2552 void
2553 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2555     g_assert (n);
2556     g_assert (nodepath);
2557     g_assert (n->subpath->nodepath == nodepath);
2559     if (g_list_length (nodepath->selected) == 0) {
2560         if (grow > 0) {
2561             sp_nodepath_node_select(n, TRUE, TRUE);
2562         }
2563         return;
2564     }
2566     if (g_list_length (nodepath->selected) == 1) {
2567         if (grow < 0) {
2568             sp_nodepath_deselect (nodepath);
2569             return;
2570         }
2571     }
2573         double n_sel_range = 0, p_sel_range = 0;
2574             Inkscape::NodePath::Node *farthest_n_node = n;
2575             Inkscape::NodePath::Node *farthest_p_node = n;
2577         // Calculate ranges
2578         {
2579             double n_range = 0, p_range = 0;
2580             bool n_going = true, p_going = true;
2581             Inkscape::NodePath::Node *n_node = n;
2582             Inkscape::NodePath::Node *p_node = n;
2583             do {
2584                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2585                 if (n_node && n_going)
2586                     n_node = n_node->n.other;
2587                 if (n_node == NULL) {
2588                     n_going = false;
2589                 } else {
2590                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2591                     if (n_node->selected) {
2592                         n_sel_range = n_range;
2593                         farthest_n_node = n_node;
2594                     }
2595                     if (n_node == p_node) {
2596                         n_going = false;
2597                         p_going = false;
2598                     }
2599                 }
2600                 if (p_node && p_going)
2601                     p_node = p_node->p.other;
2602                 if (p_node == NULL) {
2603                     p_going = false;
2604                 } else {
2605                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2606                     if (p_node->selected) {
2607                         p_sel_range = p_range;
2608                         farthest_p_node = p_node;
2609                     }
2610                     if (p_node == n_node) {
2611                         n_going = false;
2612                         p_going = false;
2613                     }
2614                 }
2615             } while (n_going || p_going);
2616         }
2618     if (grow > 0) {
2619         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2620                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2621         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2622                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2623         }
2624     } else {
2625         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2626                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2627         } else if (farthest_p_node && farthest_p_node->selected) {
2628                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2629         }
2630     }
2633 void
2634 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2636     g_assert (n);
2637     g_assert (nodepath);
2638     g_assert (n->subpath->nodepath == nodepath);
2640     if (g_list_length (nodepath->selected) == 0) {
2641         if (grow > 0) {
2642             sp_nodepath_node_select(n, TRUE, TRUE);
2643         }
2644         return;
2645     }
2647     if (g_list_length (nodepath->selected) == 1) {
2648         if (grow < 0) {
2649             sp_nodepath_deselect (nodepath);
2650             return;
2651         }
2652     }
2654     Inkscape::NodePath::Node *farthest_selected = NULL;
2655     double farthest_dist = 0;
2657     Inkscape::NodePath::Node *closest_unselected = NULL;
2658     double closest_dist = NR_HUGE;
2660     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2661        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2662         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2663            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2664            if (node == n)
2665                continue;
2666            if (node->selected) {
2667                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2668                    farthest_dist = NR::L2(node->pos - n->pos);
2669                    farthest_selected = node;
2670                }
2671            } else {
2672                if (NR::L2(node->pos - n->pos) < closest_dist) {
2673                    closest_dist = NR::L2(node->pos - n->pos);
2674                    closest_unselected = node;
2675                }
2676            }
2677         }
2678     }
2680     if (grow > 0) {
2681         if (closest_unselected) {
2682             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2683         }
2684     } else {
2685         if (farthest_selected) {
2686             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2687         }
2688     }
2692 /**
2693 \brief  Saves all nodes' and handles' current positions in their origin members
2694 */
2695 void
2696 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2698     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2699        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2700         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2701            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2702            n->origin = n->pos;
2703            n->p.origin = n->p.pos;
2704            n->n.origin = n->n.pos;
2705         }
2706     }
2707
2709 /**
2710 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2711 */
2712 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2714     if (!nodepath->selected) {
2715         return NULL;
2716     }
2718     GList *r = NULL;
2719     guint i = 0;
2720     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2721        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2722         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2723            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2724             i++;
2725             if (node->selected) {
2726                 r = g_list_append(r, GINT_TO_POINTER(i));
2727             }
2728         }
2729     }
2730     return r;
2733 /**
2734 \brief  Restores selection by selecting nodes whose positions are in the list
2735 */
2736 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2738     sp_nodepath_deselect(nodepath);
2740     guint i = 0;
2741     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2742        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2743         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2744            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2745             i++;
2746             if (g_list_find(r, GINT_TO_POINTER(i))) {
2747                 sp_nodepath_node_select(node, TRUE, TRUE);
2748             }
2749         }
2750     }
2754 /**
2755 \brief Adjusts handle according to node type and line code.
2756 */
2757 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2759     double len, otherlen, linelen;
2761     g_assert(node);
2763    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2764    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2766     /** \todo fixme: */
2767     if (me->other == NULL) return;
2768     if (other->other == NULL) return;
2770     /* I have line */
2772     NRPathcode mecode, ocode;
2773     if (which_adjust == 1) {
2774         mecode = (NRPathcode)me->other->code;
2775         ocode = (NRPathcode)node->code;
2776     } else {
2777         mecode = (NRPathcode)node->code;
2778         ocode = (NRPathcode)other->other->code;
2779     }
2781     if (mecode == NR_LINETO) return;
2783     /* I am curve */
2785     if (other->other == NULL) return;
2787     /* Other has line */
2789     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2791     NR::Point delta;
2792     if (ocode == NR_LINETO) {
2793         /* other is lineto, we are either smooth or symm */
2794        Inkscape::NodePath::Node *othernode = other->other;
2795         len = NR::L2(me->pos - node->pos);
2796         delta = node->pos - othernode->pos;
2797         linelen = NR::L2(delta);
2798         if (linelen < 1e-18) 
2799             return;
2800         me->pos = node->pos + (len / linelen)*delta;
2801         return;
2802     }
2804     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2806         me->pos = 2 * node->pos - other->pos;
2807         return;
2808     }
2810     /* We are smooth */
2812     len = NR::L2(me->pos - node->pos);
2813     delta = other->pos - node->pos;
2814     otherlen = NR::L2(delta);
2815     if (otherlen < 1e-18) return;
2817     me->pos = node->pos - (len / otherlen) * delta;
2820 /**
2821  \brief Adjusts both handles according to node type and line code
2822  */
2823 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2825     g_assert(node);
2827     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2829     /* we are either smooth or symm */
2831     if (node->p.other == NULL) return;
2833     if (node->n.other == NULL) return;
2835     if (node->code == NR_LINETO) {
2836         if (node->n.other->code == NR_LINETO) return;
2837         sp_node_adjust_handle(node, 1);
2838         return;
2839     }
2841     if (node->n.other->code == NR_LINETO) {
2842         if (node->code == NR_LINETO) return;
2843         sp_node_adjust_handle(node, -1);
2844         return;
2845     }
2847     /* both are curves */
2848     NR::Point const delta( node->n.pos - node->p.pos );
2850     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2851         node->p.pos = node->pos - delta / 2;
2852         node->n.pos = node->pos + delta / 2;
2853         return;
2854     }
2856     /* We are smooth */
2857     double plen = NR::L2(node->p.pos - node->pos);
2858     if (plen < 1e-18) return;
2859     double nlen = NR::L2(node->n.pos - node->pos);
2860     if (nlen < 1e-18) return;
2861     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2862     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2865 /**
2866  * Node event callback.
2867  */
2868 static gboolean node_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n)
2870     gboolean ret = FALSE;
2871     switch (event->type) {
2872         case GDK_ENTER_NOTIFY:
2873             active_node = n;
2874             break;
2875         case GDK_LEAVE_NOTIFY:
2876             active_node = NULL;
2877             break;
2878         case GDK_SCROLL:
2879             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
2880                 switch (event->scroll.direction) {
2881                     case GDK_SCROLL_UP:
2882                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2883                         break;
2884                     case GDK_SCROLL_DOWN:
2885                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2886                         break;
2887                     default:
2888                         break;
2889                 }
2890                 ret = TRUE;
2891             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
2892                 switch (event->scroll.direction) {
2893                     case GDK_SCROLL_UP:
2894                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2895                         break;
2896                     case GDK_SCROLL_DOWN:
2897                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2898                         break;
2899                     default:
2900                         break;
2901                 }
2902                 ret = TRUE;
2903             }
2904             break;
2905         case GDK_KEY_PRESS:
2906             switch (get_group0_keyval (&event->key)) {
2907                 case GDK_space:
2908                     if (event->key.state & GDK_BUTTON1_MASK) {
2909                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2910                         stamp_repr(nodepath);
2911                         ret = TRUE;
2912                     }
2913                     break;
2914                 case GDK_Page_Up:
2915                     if (event->key.state & GDK_CONTROL_MASK) {
2916                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2917                     } else {
2918                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2919                     }
2920                     break;
2921                 case GDK_Page_Down:
2922                     if (event->key.state & GDK_CONTROL_MASK) {
2923                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2924                     } else {
2925                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2926                     }
2927                     break;
2928                 default:
2929                     break;
2930             }
2931             break;
2932         default:
2933             break;
2934     }
2936     return ret;
2939 /**
2940  * Handle keypress on node; directly called.
2941  */
2942 gboolean node_key(GdkEvent *event)
2944     Inkscape::NodePath::Path *np;
2946     // there is no way to verify nodes so set active_node to nil when deleting!!
2947     if (active_node == NULL) return FALSE;
2949     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2950         gint ret = FALSE;
2951         switch (get_group0_keyval (&event->key)) {
2952             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2953             case GDK_BackSpace:
2954                 np = active_node->subpath->nodepath;
2955                 sp_nodepath_node_destroy(active_node);
2956                 sp_nodepath_update_repr(np, _("Delete node"));
2957                 active_node = NULL;
2958                 ret = TRUE;
2959                 break;
2960             case GDK_c:
2961                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2962                 ret = TRUE;
2963                 break;
2964             case GDK_s:
2965                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2966                 ret = TRUE;
2967                 break;
2968             case GDK_y:
2969                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2970                 ret = TRUE;
2971                 break;
2972             case GDK_b:
2973                 sp_nodepath_node_break(active_node);
2974                 ret = TRUE;
2975                 break;
2976         }
2977         return ret;
2978     }
2979     return FALSE;
2982 /**
2983  * Mouseclick on node callback.
2984  */
2985 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2987    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2989     if (state & GDK_CONTROL_MASK) {
2990         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2992         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2993             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2994                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2995             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2996                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2997             } else {
2998                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2999             }
3000             sp_nodepath_update_repr(nodepath, _("Change node type"));
3001             sp_nodepath_update_statusbar(nodepath);
3003         } else { //ctrl+alt+click: delete node
3004             GList *node_to_delete = NULL;
3005             node_to_delete = g_list_append(node_to_delete, n);
3006             sp_node_delete_preserve(node_to_delete);
3007         }
3009     } else {
3010         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3011     }
3014 /**
3015  * Mouse grabbed node callback.
3016  */
3017 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3019    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3021     if (!n->selected) {
3022         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3023     }
3025     n->is_dragging = true;
3026     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3028     sp_nodepath_remember_origins (n->subpath->nodepath);
3031 /**
3032  * Mouse ungrabbed node callback.
3033  */
3034 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
3036    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3038    n->dragging_out = NULL;
3039    n->is_dragging = false;
3040    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3042    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3045 /**
3046  * The point on a line, given by its angle, closest to the given point.
3047  * \param p  A point.
3048  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3049  * \param closest  Pointer to the point struct where the result is stored.
3050  * \todo FIXME: use dot product perhaps?
3051  */
3052 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3054     if (a == HUGE_VAL) { // vertical
3055         *closest = NR::Point(0, (*p)[NR::Y]);
3056     } else {
3057         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3058         (*closest)[NR::Y] = a * (*closest)[NR::X];
3059     }
3062 /**
3063  * Distance from the point to a line given by its angle.
3064  * \param p  A point.
3065  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3066  */
3067 static double point_line_distance(NR::Point *p, double a)
3069     NR::Point c;
3070     point_line_closest(p, a, &c);
3071     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]));
3074 /**
3075  * Callback for node "request" signal.
3076  * \todo fixme: This goes to "moved" event? (lauris)
3077  */
3078 static gboolean
3079 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3081     double yn, xn, yp, xp;
3082     double an, ap, na, pa;
3083     double d_an, d_ap, d_na, d_pa;
3084     gboolean collinear = FALSE;
3085     NR::Point c;
3086     NR::Point pr;
3088    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3090    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3091    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
3093        NR::Point mouse = (*p);
3095        if (!n->dragging_out) {
3096            // This is the first drag-out event; find out which handle to drag out
3097            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3098            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3100            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3101                return FALSE;
3103            Inkscape::NodePath::NodeSide *opposite;
3104            if (appr_p > appr_n) { // closer to p
3105                n->dragging_out = &n->p;
3106                opposite = &n->n;
3107                n->code = NR_CURVETO;
3108            } else if (appr_p < appr_n) { // closer to n
3109                n->dragging_out = &n->n;
3110                opposite = &n->p;
3111                n->n.other->code = NR_CURVETO;
3112            } else { // p and n nodes are the same
3113                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3114                    n->dragging_out = &n->p;
3115                    opposite = &n->n;
3116                    n->code = NR_CURVETO;
3117                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3118                    n->dragging_out = &n->n;
3119                    opposite = &n->p;
3120                    n->n.other->code = NR_CURVETO;
3121                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3122                    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);
3123                    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);
3124                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3125                        n->dragging_out = &n->n;
3126                        opposite = &n->p;
3127                        n->n.other->code = NR_CURVETO;
3128                    } else { // closer to other's n handle
3129                        n->dragging_out = &n->p;
3130                        opposite = &n->n;
3131                        n->code = NR_CURVETO;
3132                    }
3133                }
3134            }
3136            // if there's another handle, make sure the one we drag out starts parallel to it
3137            if (opposite->pos != n->pos) {
3138                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3139            }
3141            // knots might not be created yet!
3142            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3143            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3144        }
3146        // pass this on to the handle-moved callback
3147        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3148        sp_node_update_handles(n);
3149        return TRUE;
3150    }
3152     if (state & GDK_CONTROL_MASK) { // constrained motion
3154         // calculate relative distances of handles
3155         // n handle:
3156         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3157         xn = n->n.pos[NR::X] - n->pos[NR::X];
3158         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3159         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3160             if (n->n.other) { // if there is the next point
3161                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3162                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3163                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3164             }
3165         }
3166         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3167         if (yn < 0) { xn = -xn; yn = -yn; }
3169         // p handle:
3170         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3171         xp = n->p.pos[NR::X] - n->pos[NR::X];
3172         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3173         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3174             if (n->p.other) {
3175                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3176                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3177                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3178             }
3179         }
3180         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3181         if (yp < 0) { xp = -xp; yp = -yp; }
3183         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3184             // sliding on handles, only if at least one of the handles is non-vertical
3185             // (otherwise it's the same as ctrl+drag anyway)
3187             // calculate angles of the handles
3188             if (xn == 0) {
3189                 if (yn == 0) { // no handle, consider it the continuation of the other one
3190                     an = 0;
3191                     collinear = TRUE;
3192                 }
3193                 else an = 0; // vertical; set the angle to horizontal
3194             } else an = yn/xn;
3196             if (xp == 0) {
3197                 if (yp == 0) { // no handle, consider it the continuation of the other one
3198                     ap = an;
3199                 }
3200                 else ap = 0; // vertical; set the angle to horizontal
3201             } else  ap = yp/xp;
3203             if (collinear) an = ap;
3205             // angles of the perpendiculars; HUGE_VAL means vertical
3206             if (an == 0) na = HUGE_VAL; else na = -1/an;
3207             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3209             // mouse point relative to the node's original pos
3210             pr = (*p) - n->origin;
3212             // distances to the four lines (two handles and two perpendiculars)
3213             d_an = point_line_distance(&pr, an);
3214             d_na = point_line_distance(&pr, na);
3215             d_ap = point_line_distance(&pr, ap);
3216             d_pa = point_line_distance(&pr, pa);
3218             // find out which line is the closest, save its closest point in c
3219             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3220                 point_line_closest(&pr, an, &c);
3221             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3222                 point_line_closest(&pr, ap, &c);
3223             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3224                 point_line_closest(&pr, na, &c);
3225             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3226                 point_line_closest(&pr, pa, &c);
3227             }
3229             // move the node to the closest point
3230             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3231                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3232                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3234         } else {  // constraining to hor/vert
3236             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3237                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3238             } else { // snap to vert
3239                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3240             }
3241         }
3242     } else { // move freely
3243         if (n->is_dragging) {
3244             if (state & GDK_MOD1_MASK) { // sculpt
3245                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3246             } else {
3247                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3248                                             (*p)[NR::X] - n->pos[NR::X],
3249                                             (*p)[NR::Y] - n->pos[NR::Y],
3250                                             (state & GDK_SHIFT_MASK) == 0);
3251             }
3252         }
3253     }
3255     n->subpath->nodepath->desktop->scroll_to_point(p);
3257     return TRUE;
3260 /**
3261  * Node handle clicked callback.
3262  */
3263 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3265    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3267     if (state & GDK_CONTROL_MASK) { // "delete" handle
3268         if (n->p.knot == knot) {
3269             n->p.pos = n->pos;
3270         } else if (n->n.knot == knot) {
3271             n->n.pos = n->pos;
3272         }
3273         sp_node_update_handles(n);
3274         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3275         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3276         sp_nodepath_update_statusbar(nodepath);
3278     } else { // just select or add to selection, depending in Shift
3279         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3280     }
3283 /**
3284  * Node handle grabbed callback.
3285  */
3286 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3288    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3290     if (!n->selected) {
3291         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3292     }
3294     // remember the origin point of the handle
3295     if (n->p.knot == knot) {
3296         n->p.origin_radial = n->p.pos - n->pos;
3297     } else if (n->n.knot == knot) {
3298         n->n.origin_radial = n->n.pos - n->pos;
3299     } else {
3300         g_assert_not_reached();
3301     }
3303     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3306 /**
3307  * Node handle ungrabbed callback.
3308  */
3309 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3311    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3313     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3314     if (n->p.knot == knot) {
3315         n->p.origin_radial.a = 0;
3316         sp_knot_set_position(knot, &n->p.pos, state);
3317     } else if (n->n.knot == knot) {
3318         n->n.origin_radial.a = 0;
3319         sp_knot_set_position(knot, &n->n.pos, state);
3320     } else {
3321         g_assert_not_reached();
3322     }
3324     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3327 /**
3328  * Node handle "request" signal callback.
3329  */
3330 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3332     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3334     Inkscape::NodePath::NodeSide *me, *opposite;
3335     gint which;
3336     if (n->p.knot == knot) {
3337         me = &n->p;
3338         opposite = &n->n;
3339         which = -1;
3340     } else if (n->n.knot == knot) {
3341         me = &n->n;
3342         opposite = &n->p;
3343         which = 1;
3344     } else {
3345         me = opposite = NULL;
3346         which = 0;
3347         g_assert_not_reached();
3348     }
3350     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3352     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3354     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3355         /* We are smooth node adjacent with line */
3356         NR::Point const delta = *p - n->pos;
3357         NR::Coord const len = NR::L2(delta);
3358         Inkscape::NodePath::Node *othernode = opposite->other;
3359         NR::Point const ndelta = n->pos - othernode->pos;
3360         NR::Coord const linelen = NR::L2(ndelta);
3361         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3362             NR::Coord const scal = dot(delta, ndelta) / linelen;
3363             (*p) = n->pos + (scal / linelen) * ndelta;
3364         }
3365         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
3366     } else {
3367         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
3368     }
3370     sp_node_adjust_handle(n, -which);
3372     return FALSE;
3375 /**
3376  * Node handle moved callback.
3377  */
3378 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3380    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3382    Inkscape::NodePath::NodeSide *me;
3383    Inkscape::NodePath::NodeSide *other;
3384     if (n->p.knot == knot) {
3385         me = &n->p;
3386         other = &n->n;
3387     } else if (n->n.knot == knot) {
3388         me = &n->n;
3389         other = &n->p;
3390     } else {
3391         me = NULL;
3392         other = NULL;
3393         g_assert_not_reached();
3394     }
3396     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3397     Radial rme(me->pos - n->pos);
3398     Radial rother(other->pos - n->pos);
3399     Radial rnew(*p - n->pos);
3401     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3402         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3403         /* 0 interpreted as "no snapping". */
3405         // The closest PI/snaps angle, starting from zero.
3406         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3407         if (me->origin_radial.a == HUGE_VAL) {
3408             // ortho doesn't exist: original handle was zero length.
3409             rnew.a = a_snapped;
3410         } else {
3411             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3412              * its opposite and perpendiculars). */
3413             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3415             // Snap to the closest.
3416             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3417                        ? a_snapped
3418                        : a_ortho );
3419         }
3420     }
3422     if (state & GDK_MOD1_MASK) {
3423         // lock handle length
3424         rnew.r = me->origin_radial.r;
3425     }
3427     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3428         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
3429         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3430         rother.a += rnew.a - rme.a;
3431         other->pos = NR::Point(rother) + n->pos;
3432         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3433         sp_knot_set_position(other->knot, &other->pos, 0);
3434     }
3436     me->pos = NR::Point(rnew) + n->pos;
3437     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3439     // this is what sp_knot_set_position does, but without emitting the signal:
3440     // we cannot emit a "moved" signal because we're now processing it
3441     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
3443     knot->desktop->set_coordinate_status(me->pos);
3445     update_object(n->subpath->nodepath);
3447     /* status text */
3448     SPDesktop *desktop = n->subpath->nodepath->desktop;
3449     if (!desktop) return;
3450     SPEventContext *ec = desktop->event_context;
3451     if (!ec) return;
3452     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3453     if (!mc) return;
3455     double degrees = 180 / M_PI * rnew.a;
3456     if (degrees > 180) degrees -= 360;
3457     if (degrees < -180) degrees += 360;
3458     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3459         degrees = angle_to_compass (degrees);
3461     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3463     mc->setF(Inkscape::NORMAL_MESSAGE,
3464          _("<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);
3466     g_string_free(length, TRUE);
3469 /**
3470  * Node handle event callback.
3471  */
3472 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3474     gboolean ret = FALSE;
3475     switch (event->type) {
3476         case GDK_KEY_PRESS:
3477             switch (get_group0_keyval (&event->key)) {
3478                 case GDK_space:
3479                     if (event->key.state & GDK_BUTTON1_MASK) {
3480                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3481                         stamp_repr(nodepath);
3482                         ret = TRUE;
3483                     }
3484                     break;
3485                 default:
3486                     break;
3487             }
3488             break;
3489         default:
3490             break;
3491     }
3493     return ret;
3496 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3497                                  Radial &rme, Radial &rother, gboolean const both)
3499     rme.a += angle;
3500     if ( both
3501          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3502          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3503     {
3504         rother.a += angle;
3505     }
3508 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3509                                         Radial &rme, Radial &rother, gboolean const both)
3511     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3513     gdouble r;
3514     if ( both
3515          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3516          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3517     {
3518         r = MAX(rme.r, rother.r);
3519     } else {
3520         r = rme.r;
3521     }
3523     gdouble const weird_angle = atan2(norm_angle, r);
3524 /* Bulia says norm_angle is just the visible distance that the
3525  * object's end must travel on the screen.  Left as 'angle' for want of
3526  * a better name.*/
3528     rme.a += weird_angle;
3529     if ( both
3530          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3531          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3532     {
3533         rother.a += weird_angle;
3534     }
3537 /**
3538  * Rotate one node.
3539  */
3540 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3542     Inkscape::NodePath::NodeSide *me, *other;
3543     bool both = false;
3545     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3546     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3548     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3549         me = &(n->p);
3550         other = &(n->n);
3551     } else if (!n->p.other) {
3552         me = &(n->n);
3553         other = &(n->p);
3554     } else {
3555         if (which > 0) { // right handle
3556             if (xn > xp) {
3557                 me = &(n->n);
3558                 other = &(n->p);
3559             } else {
3560                 me = &(n->p);
3561                 other = &(n->n);
3562             }
3563         } else if (which < 0){ // left handle
3564             if (xn <= xp) {
3565                 me = &(n->n);
3566                 other = &(n->p);
3567             } else {
3568                 me = &(n->p);
3569                 other = &(n->n);
3570             }
3571         } else { // both handles
3572             me = &(n->n);
3573             other = &(n->p);
3574             both = true;
3575         }
3576     }
3578     Radial rme(me->pos - n->pos);
3579     Radial rother(other->pos - n->pos);
3581     if (screen) {
3582         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3583     } else {
3584         node_rotate_one_internal (*n, angle, rme, rother, both);
3585     }
3587     me->pos = n->pos + NR::Point(rme);
3589     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3590         other->pos =  n->pos + NR::Point(rother);
3591     }
3593     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3594     // so here we just move all the knots without emitting move signals, for speed
3595     sp_node_update_handles(n, false);
3598 /**
3599  * Rotate selected nodes.
3600  */
3601 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3603     if (!nodepath || !nodepath->selected) return;
3605     if (g_list_length(nodepath->selected) == 1) {
3606        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3607         node_rotate_one (n, angle, which, screen);
3608     } else {
3609        // rotate as an object:
3611         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3612         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3613         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3614             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3615             box.expandTo (n->pos); // contain all selected nodes
3616         }
3618         gdouble rot;
3619         if (screen) {
3620             gdouble const zoom = nodepath->desktop->current_zoom();
3621             gdouble const zmove = angle / zoom;
3622             gdouble const r = NR::L2(box.max() - box.midpoint());
3623             rot = atan2(zmove, r);
3624         } else {
3625             rot = angle;
3626         }
3628         NR::Matrix t =
3629             NR::Matrix (NR::translate(-box.midpoint())) *
3630             NR::Matrix (NR::rotate(rot)) *
3631             NR::Matrix (NR::translate(box.midpoint()));
3633         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3634             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3635             n->pos *= t;
3636             n->n.pos *= t;
3637             n->p.pos *= t;
3638             sp_node_update_handles(n, false);
3639         }
3640     }
3642     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3645 /**
3646  * Scale one node.
3647  */
3648 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3650     bool both = false;
3651     Inkscape::NodePath::NodeSide *me, *other;
3653     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3654     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3656     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3657         me = &(n->p);
3658         other = &(n->n);
3659         n->code = NR_CURVETO;
3660     } else if (!n->p.other) {
3661         me = &(n->n);
3662         other = &(n->p);
3663         if (n->n.other)
3664             n->n.other->code = NR_CURVETO;
3665     } else {
3666         if (which > 0) { // right handle
3667             if (xn > xp) {
3668                 me = &(n->n);
3669                 other = &(n->p);
3670                 if (n->n.other)
3671                     n->n.other->code = NR_CURVETO;
3672             } else {
3673                 me = &(n->p);
3674                 other = &(n->n);
3675                 n->code = NR_CURVETO;
3676             }
3677         } else if (which < 0){ // left handle
3678             if (xn <= xp) {
3679                 me = &(n->n);
3680                 other = &(n->p);
3681                 if (n->n.other)
3682                     n->n.other->code = NR_CURVETO;
3683             } else {
3684                 me = &(n->p);
3685                 other = &(n->n);
3686                 n->code = NR_CURVETO;
3687             }
3688         } else { // both handles
3689             me = &(n->n);
3690             other = &(n->p);
3691             both = true;
3692             n->code = NR_CURVETO;
3693             if (n->n.other)
3694                 n->n.other->code = NR_CURVETO;
3695         }
3696     }
3698     Radial rme(me->pos - n->pos);
3699     Radial rother(other->pos - n->pos);
3701     rme.r += grow;
3702     if (rme.r < 0) rme.r = 0;
3703     if (rme.a == HUGE_VAL) {
3704         if (me->other) { // if direction is unknown, initialize it towards the next node
3705             Radial rme_next(me->other->pos - n->pos);
3706             rme.a = rme_next.a;
3707         } else { // if there's no next, initialize to 0
3708             rme.a = 0;
3709         }
3710     }
3711     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3712         rother.r += grow;
3713         if (rother.r < 0) rother.r = 0;
3714         if (rother.a == HUGE_VAL) {
3715             rother.a = rme.a + M_PI;
3716         }
3717     }
3719     me->pos = n->pos + NR::Point(rme);
3721     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3722         other->pos = n->pos + NR::Point(rother);
3723     }
3725     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3726     // so here we just move all the knots without emitting move signals, for speed
3727     sp_node_update_handles(n, false);
3730 /**
3731  * Scale selected nodes.
3732  */
3733 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3735     if (!nodepath || !nodepath->selected) return;
3737     if (g_list_length(nodepath->selected) == 1) {
3738         // scale handles of the single selected node
3739         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3740         node_scale_one (n, grow, which);
3741     } else {
3742         // scale nodes as an "object":
3744         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3745         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3746         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3747             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3748             box.expandTo (n->pos); // contain all selected nodes
3749         }
3751         double scale = (box.maxExtent() + grow)/box.maxExtent();
3753         NR::Matrix t =
3754             NR::Matrix (NR::translate(-box.midpoint())) *
3755             NR::Matrix (NR::scale(scale, scale)) *
3756             NR::Matrix (NR::translate(box.midpoint()));
3758         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3759             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3760             n->pos *= t;
3761             n->n.pos *= t;
3762             n->p.pos *= t;
3763             sp_node_update_handles(n, false);
3764         }
3765     }
3767     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
3770 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3772     if (!nodepath) return;
3773     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3776 /**
3777  * Flip selected nodes horizontally/vertically.
3778  */
3779 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3781     if (!nodepath || !nodepath->selected) return;
3783     if (g_list_length(nodepath->selected) == 1) {
3784         // flip handles of the single selected node
3785         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3786         double temp = n->p.pos[axis];
3787         n->p.pos[axis] = n->n.pos[axis];
3788         n->n.pos[axis] = temp;
3789         sp_node_update_handles(n, false);
3790     } else {
3791         // scale nodes as an "object":
3793         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3794         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3795         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3796             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3797             box.expandTo (n->pos); // contain all selected nodes
3798         }
3800         NR::Matrix t =
3801             NR::Matrix (NR::translate(-box.midpoint())) *
3802             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3803             NR::Matrix (NR::translate(box.midpoint()));
3805         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3806             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3807             n->pos *= t;
3808             n->n.pos *= t;
3809             n->p.pos *= t;
3810             sp_node_update_handles(n, false);
3811         }
3812     }
3814     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
3817 //-----------------------------------------------
3818 /**
3819  * Return new subpath under given nodepath.
3820  */
3821 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3823     g_assert(nodepath);
3824     g_assert(nodepath->desktop);
3826    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3828     s->nodepath = nodepath;
3829     s->closed = FALSE;
3830     s->nodes = NULL;
3831     s->first = NULL;
3832     s->last = NULL;
3834     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3835     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3836     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3838     return s;
3841 /**
3842  * Destroy nodes in subpath, then subpath itself.
3843  */
3844 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3846     g_assert(subpath);
3847     g_assert(subpath->nodepath);
3848     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3850     while (subpath->nodes) {
3851         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3852     }
3854     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3856     g_free(subpath);
3859 /**
3860  * Link head to tail in subpath.
3861  */
3862 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3864     g_assert(!sp->closed);
3865     g_assert(sp->last != sp->first);
3866     g_assert(sp->first->code == NR_MOVETO);
3868     sp->closed = TRUE;
3870     //Link the head to the tail
3871     sp->first->p.other = sp->last;
3872     sp->last->n.other  = sp->first;
3873     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3874     sp->first          = sp->last;
3876     //Remove the extra end node
3877     sp_nodepath_node_destroy(sp->last->n.other);
3880 /**
3881  * Open closed (loopy) subpath at node.
3882  */
3883 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3885     g_assert(sp->closed);
3886     g_assert(n->subpath == sp);
3887     g_assert(sp->first == sp->last);
3889     /* We create new startpoint, current node will become last one */
3891    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3892                                                 &n->pos, &n->pos, &n->n.pos);
3895     sp->closed        = FALSE;
3897     //Unlink to make a head and tail
3898     sp->first         = new_path;
3899     sp->last          = n;
3900     n->n.other        = NULL;
3901     new_path->p.other = NULL;
3904 /**
3905  * Returns area in triangle given by points; may be negative.
3906  */
3907 inline double
3908 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3910     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]);
3913 /**
3914  * Return new node in subpath with given properties.
3915  * \param pos Position of node.
3916  * \param ppos Handle position in previous direction
3917  * \param npos Handle position in previous direction
3918  */
3919 Inkscape::NodePath::Node *
3920 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)
3922     g_assert(sp);
3923     g_assert(sp->nodepath);
3924     g_assert(sp->nodepath->desktop);
3926     if (nodechunk == NULL)
3927         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3929     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3931     n->subpath  = sp;
3933     if (type != Inkscape::NodePath::NODE_NONE) {
3934         // use the type from sodipodi:nodetypes
3935         n->type = type;
3936     } else {
3937         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3938             // points are (almost) collinear
3939             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3940                 // endnode, or a node with a retracted handle
3941                 n->type = Inkscape::NodePath::NODE_CUSP;
3942             } else {
3943                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3944             }
3945         } else {
3946             n->type = Inkscape::NodePath::NODE_CUSP;
3947         }
3948     }
3950     n->code     = code;
3951     n->selected = FALSE;
3952     n->pos      = *pos;
3953     n->p.pos    = *ppos;
3954     n->n.pos    = *npos;
3956     n->dragging_out = NULL;
3958     Inkscape::NodePath::Node *prev;
3959     if (next) {
3960         //g_assert(g_list_find(sp->nodes, next));
3961         prev = next->p.other;
3962     } else {
3963         prev = sp->last;
3964     }
3966     if (prev)
3967         prev->n.other = n;
3968     else
3969         sp->first = n;
3971     if (next)
3972         next->p.other = n;
3973     else
3974         sp->last = n;
3976     n->p.other = prev;
3977     n->n.other = next;
3979     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"));
3980     sp_knot_set_position(n->knot, pos, 0);
3982     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3983     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3984     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3985     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3986     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3987     sp_knot_update_ctrl(n->knot);
3989     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3990     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3991     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3992     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3993     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3994     sp_knot_show(n->knot);
3996     // We only create handle knots and lines on demand
3997     n->p.knot = NULL;
3998     n->p.line = NULL;
3999     n->n.knot = NULL;
4000     n->n.line = NULL;
4002     sp->nodes = g_list_prepend(sp->nodes, n);
4004     return n;
4007 /**
4008  * Destroy node and its knots, link neighbors in subpath.
4009  */
4010 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4012     g_assert(node);
4013     g_assert(node->subpath);
4014     g_assert(SP_IS_KNOT(node->knot));
4016    Inkscape::NodePath::SubPath *sp = node->subpath;
4018     if (node->selected) { // first, deselect
4019         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4020         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4021     }
4023     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4025     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4026     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4027     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4028     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4029     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4030     g_object_unref(G_OBJECT(node->knot));
4032     if (node->p.knot) {
4033         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4034         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4035         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4036         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4037         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4038         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4039         g_object_unref(G_OBJECT(node->p.knot));
4040         node->p.knot = NULL;
4041     }
4043     if (node->n.knot) {
4044         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4045         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4046         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4047         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4048         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4049         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4050         g_object_unref(G_OBJECT(node->n.knot));
4051         node->n.knot = NULL;
4052     }
4054     if (node->p.line)
4055         gtk_object_destroy(GTK_OBJECT(node->p.line));
4056     if (node->n.line)
4057         gtk_object_destroy(GTK_OBJECT(node->n.line));
4059     if (sp->nodes) { // there are others nodes on the subpath
4060         if (sp->closed) {
4061             if (sp->first == node) {
4062                 g_assert(sp->last == node);
4063                 sp->first = node->n.other;
4064                 sp->last = sp->first;
4065             }
4066             node->p.other->n.other = node->n.other;
4067             node->n.other->p.other = node->p.other;
4068         } else {
4069             if (sp->first == node) {
4070                 sp->first = node->n.other;
4071                 sp->first->code = NR_MOVETO;
4072             }
4073             if (sp->last == node) sp->last = node->p.other;
4074             if (node->p.other) node->p.other->n.other = node->n.other;
4075             if (node->n.other) node->n.other->p.other = node->p.other;
4076         }
4077     } else { // this was the last node on subpath
4078         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4079     }
4081     g_mem_chunk_free(nodechunk, node);
4084 /**
4085  * Returns one of the node's two sides.
4086  * \param which Indicates which side.
4087  * \return Pointer to previous node side if which==-1, next if which==1.
4088  */
4089 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4091     g_assert(node);
4093     switch (which) {
4094         case -1:
4095             return &node->p;
4096         case 1:
4097             return &node->n;
4098         default:
4099             break;
4100     }
4102     g_assert_not_reached();
4104     return NULL;
4107 /**
4108  * Return the other side of the node, given one of its sides.
4109  */
4110 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4112     g_assert(node);
4114     if (me == &node->p) return &node->n;
4115     if (me == &node->n) return &node->p;
4117     g_assert_not_reached();
4119     return NULL;
4122 /**
4123  * Return NRPathcode on the given side of the node.
4124  */
4125 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4127     g_assert(node);
4129     if (me == &node->p) {
4130         if (node->p.other) return (NRPathcode)node->code;
4131         return NR_MOVETO;
4132     }
4134     if (me == &node->n) {
4135         if (node->n.other) return (NRPathcode)node->n.other->code;
4136         return NR_MOVETO;
4137     }
4139     g_assert_not_reached();
4141     return NR_END;
4144 /**
4145  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
4146  */
4147 Inkscape::NodePath::Node *
4148 sp_nodepath_get_node_by_index(int index)
4150     Inkscape::NodePath::Node *e = NULL;
4152     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4153     if (!nodepath) {
4154         return e;
4155     }
4157     //find segment
4158     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4160         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4161         int n = g_list_length(sp->nodes);
4162         if (sp->closed) {
4163             n++;
4164         }
4166         //if the piece belongs to this subpath grab it
4167         //otherwise move onto the next subpath
4168         if (index < n) {
4169             e = sp->first;
4170             for (int i = 0; i < index; ++i) {
4171                 e = e->n.other;
4172             }
4173             break;
4174         } else {
4175             if (sp->closed) {
4176                 index -= (n+1);
4177             } else {
4178                 index -= n;
4179             }
4180         }
4181     }
4183     return e;
4186 /**
4187  * Returns plain text meaning of node type.
4188  */
4189 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4191     unsigned retracted = 0;
4192     bool endnode = false;
4194     for (int which = -1; which <= 1; which += 2) {
4195         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4196         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4197             retracted ++;
4198         if (!side->other)
4199             endnode = true;
4200     }
4202     if (retracted == 0) {
4203         if (endnode) {
4204                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4205                 return _("end node");
4206         } else {
4207             switch (node->type) {
4208                 case Inkscape::NodePath::NODE_CUSP:
4209                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4210                     return _("cusp");
4211                 case Inkscape::NodePath::NODE_SMOOTH:
4212                     // TRANSLATORS: "smooth" is an adjective here
4213                     return _("smooth");
4214                 case Inkscape::NodePath::NODE_SYMM:
4215                     return _("symmetric");
4216             }
4217         }
4218     } else if (retracted == 1) {
4219         if (endnode) {
4220             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4221             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4222         } else {
4223             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4224         }
4225     } else {
4226         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4227     }
4229     return NULL;
4232 /**
4233  * Handles content of statusbar as long as node tool is active.
4234  */
4235 void
4236 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
4238     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");
4239     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4241     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4242     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4243     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4244     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4246     SPDesktop *desktop = NULL;
4247     if (nodepath) {
4248         desktop = nodepath->desktop;
4249     } else {
4250         desktop = SP_ACTIVE_DESKTOP;
4251     }
4253     SPEventContext *ec = desktop->event_context;
4254     if (!ec) return;
4255     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4256     if (!mc) return;
4258     if (selected_nodes == 0) {
4259         Inkscape::Selection *sel = desktop->selection;
4260         if (!sel || sel->isEmpty()) {
4261             mc->setF(Inkscape::NORMAL_MESSAGE,
4262                      _("Select a single object to edit its nodes or handles."));
4263         } else {
4264             if (nodepath) {
4265             mc->setF(Inkscape::NORMAL_MESSAGE,
4266                      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.",
4267                               "<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.",
4268                               total_nodes),
4269                      total_nodes);
4270             } else {
4271                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4272                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4273                 } else {
4274                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4275                 }
4276             }
4277         }
4278     } else if (nodepath && selected_nodes == 1) {
4279         mc->setF(Inkscape::NORMAL_MESSAGE,
4280                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4281                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4282                           total_nodes),
4283                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4284     } else {
4285         if (selected_subpaths > 1) {
4286             mc->setF(Inkscape::NORMAL_MESSAGE,
4287                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4288                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4289                               total_nodes),
4290                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4291         } else {
4292             mc->setF(Inkscape::NORMAL_MESSAGE,
4293                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4294                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4295                               total_nodes),
4296                      selected_nodes, total_nodes, when_selected);
4297         }
4298     }
4302 /*
4303   Local Variables:
4304   mode:c++
4305   c-file-style:"stroustrup"
4306   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4307   indent-tabs-mode:nil
4308   fill-column:99
4309   End:
4310 */
4311 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :