Code

fix an old typo by lauris
[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  * This code is in public domain
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 "display/bezier-utils.h"
44 #include <vector>
45 #include <algorithm>
47 class NR::Matrix;
49 /// \todo
50 /// evil evil evil. FIXME: conflict of two different Path classes!
51 /// There is a conflict in the namespace between two classes named Path.
52 /// #include "sp-flowtext.h"
53 /// #include "sp-flowregion.h"
55 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
56 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
57 GType sp_flowregion_get_type (void);
58 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
59 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
60 GType sp_flowtext_get_type (void);
61 // end evil workaround
63 #include "helper/stlport.h"
66 /// \todo fixme: Implement these via preferences */
68 #define NODE_FILL          0xbfbfbf00
69 #define NODE_STROKE        0x000000ff
70 #define NODE_FILL_HI       0xff000000
71 #define NODE_STROKE_HI     0x000000ff
72 #define NODE_FILL_SEL      0x0000ffff
73 #define NODE_STROKE_SEL    0x000000ff
74 #define NODE_FILL_SEL_HI   0xff000000
75 #define NODE_STROKE_SEL_HI 0x000000ff
76 #define KNOT_FILL          0xffffffff
77 #define KNOT_STROKE        0x000000ff
78 #define KNOT_FILL_HI       0xff000000
79 #define KNOT_STROKE_HI     0x000000ff
81 static GMemChunk *nodechunk = NULL;
83 /* Creation from object */
85 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
86 static gchar *parse_nodetypes(gchar const *types, gint length);
88 /* Object updating */
90 static void stamp_repr(Inkscape::NodePath::Path *np);
91 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
92 static gchar *create_typestr(Inkscape::NodePath::Path *np);
94 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
96 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
98 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
100 /* Adjust handle placement, if the node or the other handle is moved */
101 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
102 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
104 /* Node event callbacks */
105 static void node_clicked(SPKnot *knot, guint state, gpointer data);
106 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
107 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
108 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
110 /* Handle event callbacks */
111 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
112 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
113 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
114 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
115 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
116 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
118 /* Constructors and destructors */
120 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
121 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
122 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
123 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
124 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
125                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
126 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
128 /* Helpers */
130 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
131 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
132 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
134 // active_node indicates mouseover node
135 static Inkscape::NodePath::Node *active_node = NULL;
137 /**
138  * \brief Creates new nodepath from item
139  */
140 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPItem *item)
142     Inkscape::XML::Node *repr = SP_OBJECT(item)->repr;
144     /** \todo
145      * FIXME: remove this. We don't want to edit paths inside flowtext.
146      * Instead we will build our flowtext with cloned paths, so that the
147      * real paths are outside the flowtext and thus editable as usual.
148      */
149     if (SP_IS_FLOWTEXT(item)) {
150         for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
151             if SP_IS_FLOWREGION(child) {
152                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
153                 if (grandchild && SP_IS_PATH(grandchild)) {
154                     item = SP_ITEM(grandchild);
155                     break;
156                 }
157             }
158         }
159     }
161     if (!SP_IS_PATH(item))
162         return NULL;
163     SPPath *path = SP_PATH(item);
164     SPCurve *curve = sp_shape_get_curve(SP_SHAPE(path));
165     if (curve == NULL)
166         return NULL;
168     NArtBpath *bpath = sp_curve_first_bpath(curve);
169     gint length = curve->end;
170     if (length == 0)
171         return NULL; // prevent crash for one-node paths
173     gchar const *nodetypes = repr->attribute("sodipodi:nodetypes");
174     gchar *typestr = parse_nodetypes(nodetypes, length);
176     //Create new nodepath
177     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
178     if (!np)
179         return NULL;
181     // Set defaults
182     np->desktop     = desktop;
183     np->path        = path;
184     np->subpaths    = NULL;
185     np->selected    = NULL;
186     np->nodeContext = NULL; //Let the context that makes this set it
187     np->livarot_path = NULL;
188     np->local_change = 0;
190     // we need to update item's transform from the repr here,
191     // because they may be out of sync when we respond
192     // to a change in repr by regenerating nodepath     --bb
193     sp_object_read_attr(SP_OBJECT(item), "transform");
195     np->i2d  = sp_item_i2d_affine(SP_ITEM(path));
196     np->d2i  = np->i2d.inverse();
197     np->repr = repr;
199     // create the subpath(s) from the bpath
200     NArtBpath *b = bpath;
201     while (b->code != NR_END) {
202         b = subpath_from_bpath(np, b, typestr + (b - bpath));
203     }
205     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
206     np->subpaths = g_list_reverse(np->subpaths);
208     g_free(typestr);
209     sp_curve_unref(curve);
211     // create the livarot representation from the same item
212     np->livarot_path = Path_for_item(item, true, true);
213     if (np->livarot_path)
214         np->livarot_path->ConvertWithBackData(0.01);
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 /**
249  *  Return the node count of a given NodeSubPath.
250  */
251 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
253     if (!subpath)
254         return 0;
255     gint nodeCount = g_list_length(subpath->nodes);
256     return nodeCount;
259 /**
260  *  Return the node count of a given NodePath.
261  */
262 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
264     if (!np)
265         return 0;
266     gint nodeCount = 0;
267     for (GList *item = np->subpaths ; item ; item=item->next) {
268        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
269         nodeCount += g_list_length(subpath->nodes);
270     }
271     return nodeCount;
275 /**
276  * Clean up a nodepath after editing.
277  *
278  * Currently we are deleting trivial subpaths.
279  */
280 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
282     GList *badSubPaths = NULL;
284     //Check all subpaths to be >=2 nodes
285     for (GList *l = nodepath->subpaths; l ; l=l->next) {
286        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
287         if (sp_nodepath_subpath_get_node_count(sp)<2)
288             badSubPaths = g_list_append(badSubPaths, sp);
289     }
291     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
292     //also removes the subpath from nodepath->subpaths
293     for (GList *l = badSubPaths; l ; l=l->next) {
294        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
295         sp_nodepath_subpath_destroy(sp);
296     }
298     g_list_free(badSubPaths);
301 /**
302  * Create new nodepath from b, make it subpath of np.
303  * \param t The node type.
304  * \todo Fixme: t should be a proper type, rather than gchar
305  */
306 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
308     NR::Point ppos, pos, npos;
310     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
312     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
313     bool const closed = (b->code == NR_MOVETO);
315     pos = NR::Point(b->x3, b->y3) * np->i2d;
316     if (b[1].code == NR_CURVETO) {
317         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
318     } else {
319         npos = pos;
320     }
321     Inkscape::NodePath::Node *n;
322     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
323     g_assert(sp->first == n);
324     g_assert(sp->last  == n);
326     b++;
327     t++;
328     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
329         pos = NR::Point(b->x3, b->y3) * np->i2d;
330         if (b->code == NR_CURVETO) {
331             ppos = NR::Point(b->x2, b->y2) * np->i2d;
332         } else {
333             ppos = pos;
334         }
335         if (b[1].code == NR_CURVETO) {
336             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
337         } else {
338             npos = pos;
339         }
340         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
341         b++;
342         t++;
343     }
345     if (closed) sp_nodepath_subpath_close(sp);
347     return b;
350 /**
351  * Convert from sodipodi:nodetypes to new style type string.
352  */
353 static gchar *parse_nodetypes(gchar const *types, gint length)
355     g_assert(length > 0);
357     gchar *typestr = g_new(gchar, length + 1);
359     gint pos = 0;
361     if (types) {
362         for (gint i = 0; types[i] && ( i < length ); i++) {
363             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
364             if (types[i] != '\0') {
365                 switch (types[i]) {
366                     case 's':
367                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
368                         break;
369                     case 'z':
370                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
371                         break;
372                     case 'c':
373                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
374                         break;
375                     default:
376                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
377                         break;
378                 }
379             }
380         }
381     }
383     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
385     return typestr;
388 /**
389  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
390  * updated but repr is not (for speed). Used during curve and node drag.
391  */
392 static void update_object(Inkscape::NodePath::Path *np)
394     g_assert(np);
396     SPCurve *curve = create_curve(np);
398     sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
400     sp_curve_unref(curve);
403 /**
404  * Update XML path node with data from path object.
405  */
406 static void update_repr_internal(Inkscape::NodePath::Path *np)
408     g_assert(np);
410     Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
412     SPCurve *curve = create_curve(np);
413     gchar *typestr = create_typestr(np);
414     gchar *svgpath = sp_svg_write_path(curve->bpath);
416     if (repr->attribute("d") == NULL || strcmp(svgpath, repr->attribute("d"))) { // d changed
417         np->local_change++;
418         repr->setAttribute("d", svgpath);
419     }
421     if (repr->attribute("sodipodi:nodetypes") == NULL || strcmp(typestr, repr->attribute("sodipodi:nodetypes"))) { // nodetypes changed
422         np->local_change++;
423         repr->setAttribute("sodipodi:nodetypes", typestr);
424     }
426     g_free(svgpath);
427     g_free(typestr);
428     sp_curve_unref(curve);
431 /**
432  * Update XML path node with data from path object, commit changes forever.
433  */
434 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np)
436     update_repr_internal(np);
437     sp_document_done(SP_DT_DOCUMENT(np->desktop));
439     if (np->livarot_path) {
440         delete np->livarot_path;
441         np->livarot_path = NULL;
442     }
444     if (np->path && SP_IS_ITEM(np->path)) {
445         np->livarot_path = Path_for_item (np->path, true, true);
446         if (np->livarot_path)
447             np->livarot_path->ConvertWithBackData(0.01);
448     }
451 /**
452  * Update XML path node with data from path object, commit changes with undo.
453  */
454 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key)
456     update_repr_internal(np);
457     sp_document_maybe_done(SP_DT_DOCUMENT(np->desktop), key);
459     if (np->livarot_path) {
460         delete np->livarot_path;
461         np->livarot_path = NULL;
462     }
464     if (np->path && SP_IS_ITEM(np->path)) {
465         np->livarot_path = Path_for_item (np->path, true, true);
466         if (np->livarot_path)
467             np->livarot_path->ConvertWithBackData(0.01);
468     }
471 /**
472  * Make duplicate of path, replace corresponding XML node in tree, commit.
473  */
474 static void stamp_repr(Inkscape::NodePath::Path *np)
476     g_assert(np);
478     Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
479     Inkscape::XML::Node *new_repr = old_repr->duplicate();
481     // remember the position of the item
482     gint pos = old_repr->position();
483     // remember parent
484     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
486     SPCurve *curve = create_curve(np);
487     gchar *typestr = create_typestr(np);
489     gchar *svgpath = sp_svg_write_path(curve->bpath);
491     new_repr->setAttribute("d", svgpath);
492     new_repr->setAttribute("sodipodi:nodetypes", typestr);
494     // add the new repr to the parent
495     parent->appendChild(new_repr);
496     // move to the saved position
497     new_repr->setPosition(pos > 0 ? pos : 0);
499     sp_document_done(SP_DT_DOCUMENT(np->desktop));
501     Inkscape::GC::release(new_repr);
502     g_free(svgpath);
503     g_free(typestr);
504     sp_curve_unref(curve);
507 /**
508  * Create curve from path.
509  */
510 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
512     SPCurve *curve = sp_curve_new();
514     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
515        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
516         sp_curve_moveto(curve,
517                         sp->first->pos * np->d2i);
518        Inkscape::NodePath::Node *n = sp->first->n.other;
519         while (n) {
520             NR::Point const end_pt = n->pos * np->d2i;
521             switch (n->code) {
522                 case NR_LINETO:
523                     sp_curve_lineto(curve, end_pt);
524                     break;
525                 case NR_CURVETO:
526                     sp_curve_curveto(curve,
527                                      n->p.other->n.pos * np->d2i,
528                                      n->p.pos * np->d2i,
529                                      end_pt);
530                     break;
531                 default:
532                     g_assert_not_reached();
533                     break;
534             }
535             if (n != sp->last) {
536                 n = n->n.other;
537             } else {
538                 n = NULL;
539             }
540         }
541         if (sp->closed) {
542             sp_curve_closepath(curve);
543         }
544     }
546     return curve;
549 /**
550  * Convert path type string to sodipodi:nodetypes style.
551  */
552 static gchar *create_typestr(Inkscape::NodePath::Path *np)
554     gchar *typestr = g_new(gchar, 32);
555     gint len = 32;
556     gint pos = 0;
558     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
559        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
561         if (pos >= len) {
562             typestr = g_renew(gchar, typestr, len + 32);
563             len += 32;
564         }
566         typestr[pos++] = 'c';
568        Inkscape::NodePath::Node *n;
569         n = sp->first->n.other;
570         while (n) {
571             gchar code;
573             switch (n->type) {
574                 case Inkscape::NodePath::NODE_CUSP:
575                     code = 'c';
576                     break;
577                 case Inkscape::NodePath::NODE_SMOOTH:
578                     code = 's';
579                     break;
580                 case Inkscape::NodePath::NODE_SYMM:
581                     code = 'z';
582                     break;
583                 default:
584                     g_assert_not_reached();
585                     code = '\0';
586                     break;
587             }
589             if (pos >= len) {
590                 typestr = g_renew(gchar, typestr, len + 32);
591                 len += 32;
592             }
594             typestr[pos++] = code;
596             if (n != sp->last) {
597                 n = n->n.other;
598             } else {
599                 n = NULL;
600             }
601         }
602     }
604     if (pos >= len) {
605         typestr = g_renew(gchar, typestr, len + 1);
606         len += 1;
607     }
609     typestr[pos++] = '\0';
611     return typestr;
614 /**
615  * Returns current path in context.
616  */
617 static Inkscape::NodePath::Path *sp_nodepath_current()
619     if (!SP_ACTIVE_DESKTOP) {
620         return NULL;
621     }
623     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
625     if (!SP_IS_NODE_CONTEXT(event_context)) {
626         return NULL;
627     }
629     return SP_NODE_CONTEXT(event_context)->nodepath;
634 /**
635  \brief Fills node and handle positions for three nodes, splitting line
636   marked by end at distance t.
637  */
638 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
640     g_assert(new_path != NULL);
641     g_assert(end      != NULL);
643     g_assert(end->p.other == new_path);
644    Inkscape::NodePath::Node *start = new_path->p.other;
645     g_assert(start);
647     if (end->code == NR_LINETO) {
648         new_path->type =Inkscape::NodePath::NODE_CUSP;
649         new_path->code = NR_LINETO;
650         new_path->pos  = (t * start->pos + (1 - t) * end->pos);
651     } else {
652         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
653         new_path->code = NR_CURVETO;
654         gdouble s      = 1 - t;
655         for (int dim = 0; dim < 2; dim++) {
656             NR::Coord const f000 = start->pos[dim];
657             NR::Coord const f001 = start->n.pos[dim];
658             NR::Coord const f011 = end->p.pos[dim];
659             NR::Coord const f111 = end->pos[dim];
660             NR::Coord const f00t = s * f000 + t * f001;
661             NR::Coord const f01t = s * f001 + t * f011;
662             NR::Coord const f11t = s * f011 + t * f111;
663             NR::Coord const f0tt = s * f00t + t * f01t;
664             NR::Coord const f1tt = s * f01t + t * f11t;
665             NR::Coord const fttt = s * f0tt + t * f1tt;
666             start->n.pos[dim]    = f00t;
667             new_path->p.pos[dim] = f0tt;
668             new_path->pos[dim]   = fttt;
669             new_path->n.pos[dim] = f1tt;
670             end->p.pos[dim]      = f11t;
671         }
672     }
675 /**
676  * Adds new node on direct line between two nodes, activates handles of all
677  * three nodes.
678  */
679 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
681     g_assert(end);
682     g_assert(end->subpath);
683     g_assert(g_list_find(end->subpath->nodes, end));
685    Inkscape::NodePath::Node *start = end->p.other;
686     g_assert( start->n.other == end );
687    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
688                                                end,
689                                               Inkscape::NodePath::NODE_SMOOTH,
690                                                (NRPathcode)end->code,
691                                                &start->pos, &start->pos, &start->n.pos);
692     sp_nodepath_line_midpoint(newnode, end, t);
694     sp_node_update_handles(start);
695     sp_node_update_handles(newnode);
696     sp_node_update_handles(end);
698     return newnode;
701 /**
702 \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
703 */
704 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
706     g_assert(node);
707     g_assert(node->subpath);
708     g_assert(g_list_find(node->subpath->nodes, node));
710    Inkscape::NodePath::SubPath *sp = node->subpath;
711     Inkscape::NodePath::Path *np    = sp->nodepath;
713     if (sp->closed) {
714         sp_nodepath_subpath_open(sp, node);
715         return sp->first;
716     } else {
717         // no break for end nodes
718         if (node == sp->first) return NULL;
719         if (node == sp->last ) return NULL;
721         // create a new subpath
722        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
724         // duplicate the break node as start of the new subpath
725        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
727         while (node->n.other) { // copy the remaining nodes into the new subpath
728            Inkscape::NodePath::Node *n  = node->n.other;
729            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);
730             if (n->selected) {
731                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
732             }
733             sp_nodepath_node_destroy(n); // remove the point on the original subpath
734         }
736         return newnode;
737     }
740 /**
741  * Duplicate node and connect to neighbours.
742  */
743 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
745     g_assert(node);
746     g_assert(node->subpath);
747     g_assert(g_list_find(node->subpath->nodes, node));
749    Inkscape::NodePath::SubPath *sp = node->subpath;
751     NRPathcode code = (NRPathcode) node->code;
752     if (code == NR_MOVETO) { // if node is the endnode,
753         node->code = NR_LINETO; // new one is inserted before it, so change that to line
754     }
756     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
758     if (!node->n.other || !node->p.other) // if node is an endnode, select it
759         return node;
760     else
761         return newnode; // otherwise select the newly created node
764 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
766     node->p.pos = (node->pos + (node->pos - node->n.pos));
769 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
771     node->n.pos = (node->pos + (node->pos - node->p.pos));
774 /**
775  * Change line type at node, with side effects on neighbours.
776  */
777 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
779     g_assert(end);
780     g_assert(end->subpath);
781     g_assert(end->p.other);
783     if (end->code == static_cast< guint > ( code ) )
784         return;
786    Inkscape::NodePath::Node *start = end->p.other;
788     end->code = code;
790     if (code == NR_LINETO) {
791         if (start->code == NR_LINETO) start->type =Inkscape::NodePath::NODE_CUSP;
792         if (end->n.other) {
793             if (end->n.other->code == NR_LINETO) end->type =Inkscape::NodePath::NODE_CUSP;
794         }
795         sp_node_adjust_handle(start, -1);
796         sp_node_adjust_handle(end, 1);
797     } else {
798         NR::Point delta = end->pos - start->pos;
799         start->n.pos = start->pos + delta / 3;
800         end->p.pos = end->pos - delta / 3;
801         sp_node_adjust_handle(start, 1);
802         sp_node_adjust_handle(end, -1);
803     }
805     sp_node_update_handles(start);
806     sp_node_update_handles(end);
809 /**
810  * Change node type, and its handles accordingly.
811  */
812 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
814     g_assert(node);
815     g_assert(node->subpath);
817     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
818         return node;
820     if ((node->p.other != NULL) && (node->n.other != NULL)) {
821         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
822             type =Inkscape::NodePath::NODE_CUSP;
823         }
824     }
826     node->type = type;
828     if (node->type == Inkscape::NodePath::NODE_CUSP) {
829         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
830         node->knot->setSize (node->selected? 11 : 9);
831         sp_knot_update_ctrl(node->knot);
832     } else {
833         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
834         node->knot->setSize (node->selected? 9 : 7);
835         sp_knot_update_ctrl(node->knot);
836     }
838     // if one of handles is mouseovered, preserve its position
839     if (node->p.knot && SP_KNOT_IS_MOSEOVER(node->p.knot)) {
840         sp_node_adjust_handle(node, 1);
841     } else if (node->n.knot && SP_KNOT_IS_MOSEOVER(node->n.knot)) {
842         sp_node_adjust_handle(node, -1);
843     } else {
844         sp_node_adjust_handles(node);
845     }
847     sp_node_update_handles(node);
849     sp_nodepath_update_statusbar(node->subpath->nodepath);
851     return node;
854 /**
855  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
856  * adjacent segments from lines to curves.
857 */
858 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
860     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
861         if ((node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos)) {
862             // convert adjacent segment BEFORE to curve
863             node->code = NR_CURVETO;
864             NR::Point delta;
865             if (node->n.other != NULL)
866                 delta = node->n.other->pos - node->p.other->pos;
867             else
868                 delta = node->pos - node->p.other->pos;
869             node->p.pos = node->pos - delta / 4;
870             sp_node_update_handles(node);
871         }
873         if ((node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos)) {
874             // convert adjacent segment AFTER to curve
875             node->n.other->code = NR_CURVETO;
876             NR::Point delta;
877             if (node->p.other != NULL)
878                 delta = node->p.other->pos - node->n.other->pos;
879             else
880                 delta = node->pos - node->n.other->pos;
881             node->n.pos = node->pos - delta / 4;
882             sp_node_update_handles(node);
883         }
884     }
886     sp_nodepath_set_node_type (node, type);
889 /**
890  * Move node to point, and adjust its and neighbouring handles.
891  */
892 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
894     NR::Point delta = p - node->pos;
895     node->pos = p;
897     node->p.pos += delta;
898     node->n.pos += delta;
900     if (node->p.other) {
901         if (node->code == NR_LINETO) {
902             sp_node_adjust_handle(node, 1);
903             sp_node_adjust_handle(node->p.other, -1);
904         }
905     }
906     if (node->n.other) {
907         if (node->n.other->code == NR_LINETO) {
908             sp_node_adjust_handle(node, -1);
909             sp_node_adjust_handle(node->n.other, 1);
910         }
911     }
913     // this function is only called from batch movers that will update display at the end
914     // themselves, so here we just move all the knots without emitting move signals, for speed
915     sp_node_update_handles(node, false);
918 /**
919  * Call sp_node_moveto() for node selection and handle possible snapping.
920  */
921 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
922                                             bool const snap = true)
924     NR::Coord best[2] = { NR_HUGE, NR_HUGE };
925     NR::Point delta(dx, dy);
926     NR::Point best_pt = delta;
928     if (snap) {
929         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
930            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
931             NR::Point p = n->pos + delta;
932             for (int dim = 0; dim < 2; dim++) {
933                 NR::Coord dist = namedview_dim_snap(nodepath->desktop->namedview,
934                                                     Inkscape::Snapper::SNAP_POINT, p,
935                                                     NR::Dim2(dim), nodepath->path);
936                 if (dist < best[dim]) {
937                     best[dim] = dist;
938                     best_pt[dim] = p[dim] - n->pos[dim];
939                 }
940             }
941         }
942     }
944     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
945        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
946         sp_node_moveto(n, n->pos + best_pt);
947     }
949     // do not update repr here so that node dragging is acceptably fast
950     update_object(nodepath);
953 /**
954  * Move node selection to point, adjust its and neighbouring handles,
955  * handle possible snapping, and commit the change with possible undo.
956  */
957 void
958 sp_node_selected_move(gdouble dx, gdouble dy)
960     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
961     if (!nodepath) return;
963     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
965     if (dx == 0) {
966         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
967     } else if (dy == 0) {
968         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
969     } else {
970         sp_nodepath_update_repr(nodepath);
971     }
974 /**
975  * Move node selection off screen and commit the change.
976  */
977 void
978 sp_node_selected_move_screen(gdouble dx, gdouble dy)
980     // borrowed from sp_selection_move_screen in selection-chemistry.c
981     // we find out the current zoom factor and divide deltas by it
982     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
984     gdouble zoom = desktop->current_zoom();
985     gdouble zdx = dx / zoom;
986     gdouble zdy = dy / zoom;
988     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
989     if (!nodepath) return;
991     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
993     if (dx == 0) {
994         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
995     } else if (dy == 0) {
996         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
997     } else {
998         sp_nodepath_update_repr(nodepath);
999     }
1002 /** If they don't yet exist, creates knot and line for the given side of the node */
1003 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1005     if (!side->knot) {
1006         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"));
1008         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1009         side->knot->setSize (7);
1010         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1011         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1012         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1013         sp_knot_update_ctrl(side->knot);
1015         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1016         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1017         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1018         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1019         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1020         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1021     }
1023     if (!side->line) {
1024         side->line = sp_canvas_item_new(SP_DT_CONTROLS(desktop),
1025                                         SP_TYPE_CTRLLINE, NULL);
1026     }
1029 /**
1030  * Ensure the given handle of the node is visible/invisible, update its screen position
1031  */
1032 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1034     g_assert(node != NULL);
1036    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1037     NRPathcode code = sp_node_path_code_from_side(node, side);
1039     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1041     if (show_handle) {
1042         if (!side->knot) { // No handle knot at all
1043             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1044             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1045             side->knot->pos = side->pos;
1046             if (side->knot->item) 
1047                 SP_CTRL(side->knot->item)->moveto(side->pos);
1048             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1049             sp_knot_show(side->knot);
1050         } else {
1051             if (side->knot->pos != side->pos) { // only if it's really moved
1052                 if (fire_move_signals) {
1053                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1054                 } else {
1055                     sp_knot_moveto(side->knot, &side->pos);
1056                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1057                 }
1058             }
1059             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1060                 sp_knot_show(side->knot);
1061             }
1062         }
1063         sp_canvas_item_show(side->line);
1064     } else {
1065         if (side->knot) {
1066             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1067                 sp_knot_hide(side->knot);
1068             }
1069         }
1070         if (side->line) {
1071             sp_canvas_item_hide(side->line);
1072         }
1073     }
1076 /**
1077  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1078  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1079  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1080  * updated; otherwise, just move the knots silently (used in batch moves).
1081  */
1082 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1084     g_assert(node != NULL);
1086     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1087         sp_knot_show(node->knot);
1088     }
1090     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1091         if (fire_move_signals)
1092             sp_knot_set_position(node->knot, &node->pos, 0);
1093         else 
1094             sp_knot_moveto(node->knot, &node->pos);
1095     }
1097     gboolean show_handles = node->selected;
1098     if (node->p.other != NULL) {
1099         if (node->p.other->selected) show_handles = TRUE;
1100     }
1101     if (node->n.other != NULL) {
1102         if (node->n.other->selected) show_handles = TRUE;
1103     }
1105     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1106     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1109 /**
1110  * Call sp_node_update_handles() for all nodes on subpath.
1111  */
1112 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1114     g_assert(subpath != NULL);
1116     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1117         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1118     }
1121 /**
1122  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1123  */
1124 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1126     g_assert(nodepath != NULL);
1128     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1129         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1130     }
1133 /**
1134  * Adds all selected nodes in nodepath to list.
1135  */
1136 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1138     StlConv<Node *>::list(l, selected);
1139 /// \todo this adds a copying, rework when the selection becomes a stl list
1142 /**
1143  * Align selected nodes on the specified axis.
1144  */
1145 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1147     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1148         return;
1149     }
1151     if ( !nodepath->selected->next ) { // only one node selected
1152         return;
1153     }
1154    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1155     NR::Point dest(pNode->pos);
1156     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1157         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1158         if (pNode) {
1159             dest[axis] = pNode->pos[axis];
1160             sp_node_moveto(pNode, dest);
1161         }
1162     }
1164     sp_nodepath_update_repr(nodepath);
1167 /// Helper struct.
1168 struct NodeSort
1170    Inkscape::NodePath::Node *_node;
1171     NR::Coord _coord;
1172     /// \todo use vectorof pointers instead of calling copy ctor
1173     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1174         _node(node), _coord(node->pos[axis])
1175     {}
1177 };
1179 static bool operator<(NodeSort const &a, NodeSort const &b)
1181     return (a._coord < b._coord);
1184 /**
1185  * Distribute selected nodes on the specified axis.
1186  */
1187 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1189     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1190         return;
1191     }
1193     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1194         return;
1195     }
1197    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1198     std::vector<NodeSort> sorted;
1199     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1200         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1201         if (pNode) {
1202             NodeSort n(pNode, axis);
1203             sorted.push_back(n);
1204             //dest[axis] = pNode->pos[axis];
1205             //sp_node_moveto(pNode, dest);
1206         }
1207     }
1208     std::sort(sorted.begin(), sorted.end());
1209     unsigned int len = sorted.size();
1210     //overall bboxes span
1211     float dist = (sorted.back()._coord -
1212                   sorted.front()._coord);
1213     //new distance between each bbox
1214     float step = (dist) / (len - 1);
1215     float pos = sorted.front()._coord;
1216     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1217           it < sorted.end();
1218           it ++ )
1219     {
1220         NR::Point dest((*it)._node->pos);
1221         dest[axis] = pos;
1222         sp_node_moveto((*it)._node, dest);
1223         pos += step;
1224     }
1226     sp_nodepath_update_repr(nodepath);
1230 /**
1231  * Call sp_nodepath_line_add_node() for all selected segments.
1232  */
1233 void
1234 sp_node_selected_add_node(void)
1236     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1237     if (!nodepath) {
1238         return;
1239     }
1241     GList *nl = NULL;
1243     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1244        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1245         g_assert(t->selected);
1246         if (t->p.other && t->p.other->selected) {
1247             nl = g_list_prepend(nl, t);
1248         }
1249     }
1251     while (nl) {
1252        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1253        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1254         sp_nodepath_node_select(n, TRUE, FALSE);
1255         nl = g_list_remove(nl, t);
1256     }
1258     /** \todo fixme: adjust ? */
1259     sp_nodepath_update_handles(nodepath);
1261     sp_nodepath_update_repr(nodepath);
1263     sp_nodepath_update_statusbar(nodepath);
1266 /**
1267  * Select segment nearest to point
1268  */
1269 void
1270 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1272     if (!nodepath) {
1273         return;
1274     }
1276     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1278     //find segment to segment
1279     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1281     gboolean force = FALSE;
1282     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1283         force = TRUE;
1284     }
1285     sp_nodepath_node_select(e, (gboolean) toggle, force);
1286     if (e->p.other)
1287         sp_nodepath_node_select(e->p.other, TRUE, force);
1289     sp_nodepath_update_handles(nodepath);
1291     sp_nodepath_update_statusbar(nodepath);
1294 /**
1295  * Add a node nearest to point
1296  */
1297 void
1298 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1300     if (!nodepath) {
1301         return;
1302     }
1304     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1306     //find segment to split
1307     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1309     //don't know why but t seems to flip for lines
1310     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1311         position.t = 1.0 - position.t;
1312     }
1313     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1314     sp_nodepath_node_select(n, FALSE, TRUE);
1316     /* fixme: adjust ? */
1317     sp_nodepath_update_handles(nodepath);
1319     sp_nodepath_update_repr(nodepath);
1321     sp_nodepath_update_statusbar(nodepath);
1324 /*
1325  * Adjusts a segment so that t moves by a certain delta for dragging
1326  * converts lines to curves
1327  *
1328  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1329  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1330  */
1331 void
1332 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta)
1334     /* feel good is an arbitrary parameter that distributes the delta between handles
1335      * if t of the drag point is less than 1/6 distance form the endpoint only
1336      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1337      */
1338     double feel_good;
1339     if (t <= 1.0 / 6.0)
1340         feel_good = 0;
1341     else if (t <= 0.5)
1342         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1343     else if (t <= 5.0 / 6.0)
1344         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1345     else
1346         feel_good = 1;
1348     //if we're dragging a line convert it to a curve
1349     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1350         sp_nodepath_set_line_type(e, NR_CURVETO);
1351     }
1353     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1354     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1355     e->p.other->n.pos += offsetcoord0;
1356     e->p.pos += offsetcoord1;
1358     // adjust handles of adjacent nodes where necessary
1359     sp_node_adjust_handle(e,1);
1360     sp_node_adjust_handle(e->p.other,-1);
1362     sp_nodepath_update_handles(e->subpath->nodepath);
1364     update_object(e->subpath->nodepath);
1366     sp_nodepath_update_statusbar(e->subpath->nodepath);
1370 /**
1371  * Call sp_nodepath_break() for all selected segments.
1372  */
1373 void sp_node_selected_break()
1375     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1376     if (!nodepath) return;
1378     GList *temp = NULL;
1379     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1380        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1381        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1382         if (nn == NULL) continue; // no break, no new node
1383         temp = g_list_prepend(temp, nn);
1384     }
1386     if (temp) {
1387         sp_nodepath_deselect(nodepath);
1388     }
1389     for (GList *l = temp; l != NULL; l = l->next) {
1390         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1391     }
1393     sp_nodepath_update_handles(nodepath);
1395     sp_nodepath_update_repr(nodepath);
1398 /**
1399  * Duplicate the selected node(s).
1400  */
1401 void sp_node_selected_duplicate()
1403     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1404     if (!nodepath) {
1405         return;
1406     }
1408     GList *temp = NULL;
1409     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1410        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1411        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1412         if (nn == NULL) continue; // could not duplicate
1413         temp = g_list_prepend(temp, nn);
1414     }
1416     if (temp) {
1417         sp_nodepath_deselect(nodepath);
1418     }
1419     for (GList *l = temp; l != NULL; l = l->next) {
1420         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1421     }
1423     sp_nodepath_update_handles(nodepath);
1425     sp_nodepath_update_repr(nodepath);
1428 /**
1429  *  Join two nodes by merging them into one.
1430  */
1431 void sp_node_selected_join()
1433     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1434     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1436     if (g_list_length(nodepath->selected) != 2) {
1437         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1438         return;
1439     }
1441    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1442    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1444     g_assert(a != b);
1445     g_assert(a->p.other || a->n.other);
1446     g_assert(b->p.other || b->n.other);
1448     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1449         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1450         return;
1451     }
1453     /* a and b are endpoints */
1455     NR::Point c = (a->pos + b->pos) / 2;
1457     if (a->subpath == b->subpath) {
1458        Inkscape::NodePath::SubPath *sp = a->subpath;
1459         sp_nodepath_subpath_close(sp);
1461         sp_nodepath_update_handles(sp->nodepath);
1463         sp_nodepath_update_repr(nodepath);
1465         return;
1466     }
1468     /* a and b are separate subpaths */
1469    Inkscape::NodePath::SubPath *sa = a->subpath;
1470    Inkscape::NodePath::SubPath *sb = b->subpath;
1471     NR::Point p;
1472    Inkscape::NodePath::Node *n;
1473     NRPathcode code;
1474     if (a == sa->first) {
1475         p = sa->first->n.pos;
1476         code = (NRPathcode)sa->first->n.other->code;
1477        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1478         n = sa->last;
1479         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1480         n = n->p.other;
1481         while (n) {
1482             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1483             n = n->p.other;
1484             if (n == sa->first) n = NULL;
1485         }
1486         sp_nodepath_subpath_destroy(sa);
1487         sa = t;
1488     } else if (a == sa->last) {
1489         p = sa->last->p.pos;
1490         code = (NRPathcode)sa->last->code;
1491         sp_nodepath_node_destroy(sa->last);
1492     } else {
1493         code = NR_END;
1494         g_assert_not_reached();
1495     }
1497     if (b == sb->first) {
1498         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1499         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1500             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1501         }
1502     } else if (b == sb->last) {
1503         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1504         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1505             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1506         }
1507     } else {
1508         g_assert_not_reached();
1509     }
1510     /* and now destroy sb */
1512     sp_nodepath_subpath_destroy(sb);
1514     sp_nodepath_update_handles(sa->nodepath);
1516     sp_nodepath_update_repr(nodepath);
1518     sp_nodepath_update_statusbar(nodepath);
1521 /**
1522  *  Join two nodes by adding a segment between them.
1523  */
1524 void sp_node_selected_join_segment()
1526     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1527     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1529     if (g_list_length(nodepath->selected) != 2) {
1530         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1531         return;
1532     }
1534    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1535    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1537     g_assert(a != b);
1538     g_assert(a->p.other || a->n.other);
1539     g_assert(b->p.other || b->n.other);
1541     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1542         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1543         return;
1544     }
1546     if (a->subpath == b->subpath) {
1547        Inkscape::NodePath::SubPath *sp = a->subpath;
1549         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1550         sp->closed = TRUE;
1552         sp->first->p.other = sp->last;
1553         sp->last->n.other  = sp->first;
1555         sp_node_handle_mirror_p_to_n(sp->last);
1556         sp_node_handle_mirror_n_to_p(sp->first);
1558         sp->first->code = sp->last->code;
1559         sp->first       = sp->last;
1561         sp_nodepath_update_handles(sp->nodepath);
1563         sp_nodepath_update_repr(nodepath);
1565         return;
1566     }
1568     /* a and b are separate subpaths */
1569    Inkscape::NodePath::SubPath *sa = a->subpath;
1570    Inkscape::NodePath::SubPath *sb = b->subpath;
1572    Inkscape::NodePath::Node *n;
1573     NR::Point p;
1574     NRPathcode code;
1575     if (a == sa->first) {
1576         code = (NRPathcode) sa->first->n.other->code;
1577        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1578         n = sa->last;
1579         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1580         for (n = n->p.other; n != NULL; n = n->p.other) {
1581             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1582         }
1583         sp_nodepath_subpath_destroy(sa);
1584         sa = t;
1585     } else if (a == sa->last) {
1586         code = (NRPathcode)sa->last->code;
1587     } else {
1588         code = NR_END;
1589         g_assert_not_reached();
1590     }
1592     if (b == sb->first) {
1593         n = sb->first;
1594         sp_node_handle_mirror_p_to_n(sa->last);
1595         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1596         sp_node_handle_mirror_n_to_p(sa->last);
1597         for (n = n->n.other; n != NULL; n = n->n.other) {
1598             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1599         }
1600     } else if (b == sb->last) {
1601         n = sb->last;
1602         sp_node_handle_mirror_p_to_n(sa->last);
1603         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1604         sp_node_handle_mirror_n_to_p(sa->last);
1605         for (n = n->p.other; n != NULL; n = n->p.other) {
1606             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1607         }
1608     } else {
1609         g_assert_not_reached();
1610     }
1611     /* and now destroy sb */
1613     sp_nodepath_subpath_destroy(sb);
1615     sp_nodepath_update_handles(sa->nodepath);
1617     sp_nodepath_update_repr(nodepath);
1620 /**
1621  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1622  */
1623 void sp_node_delete_preserve(GList *nodes_to_delete)
1625     
1626     while (nodes_to_delete) {
1627         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1628         Inkscape::NodePath::SubPath *sp = node->subpath;
1629         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1630         Inkscape::NodePath::Node *sample_cursor = NULL;
1631         Inkscape::NodePath::Node *sample_end = NULL;
1632         Inkscape::NodePath::Node *delete_cursor = node;
1633         bool just_delete = false;
1634         
1635         //find the start of this contiguous selection
1636         //move left to the first node that is not selected
1637         //or the start of the non-closed path
1638         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1639             delete_cursor = curr;
1640         }
1642         //just delete at the beginning of an open path
1643         if (!delete_cursor->p.other) {
1644             sample_cursor = delete_cursor;
1645             just_delete = true;
1646         } else {
1647             sample_cursor = delete_cursor->p.other;
1648         }
1649         
1650         //calculate points for each segment
1651         int rate = 5;
1652         float period = 1.0 / rate;
1653         std::vector<NR::Point> data;
1654         if (!just_delete) {
1655             data.push_back(sample_cursor->pos);
1656             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1657                 //just delete at the end of an open path
1658                 if (!sp->closed && curr->n.other == sp->last) {
1659                     just_delete = true;
1660                     break;
1661                 }
1662                 
1663                 //sample points on the contiguous selected segment
1664                 NR::Point *bez;
1665                 bez = new NR::Point [4];
1666                 bez[0] = curr->pos;
1667                 bez[1] = curr->n.pos;
1668                 bez[2] = curr->n.other->p.pos;
1669                 bez[3] = curr->n.other->pos;
1670                 for (int i=1; i<rate; i++) {
1671                     gdouble t = i * period;
1672                     NR::Point p = bezier_pt(3, bez, t);
1673                     data.push_back(p);
1674                 }
1675                 data.push_back(curr->n.other->pos);
1677                 sample_end = curr->n.other;
1678                 //break if we've come full circle or hit the end of the selection
1679                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
1680                     break;
1681                 }
1682             }
1683         }
1685         if (!just_delete) {
1686             //calculate the best fitting single segment and adjust the endpoints
1687             NR::Point *adata;
1688             adata = new NR::Point [data.size()];
1689             copy(data.begin(), data.end(), adata);
1690             
1691             NR::Point *bez;
1692             bez = new NR::Point [4];
1693             //would decreasing error create a better fitting approximation?
1694             gdouble error = 1.0;
1695             gint ret;
1696             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
1698             //adjust endpoints
1699             sample_cursor->n.pos = bez[1];
1700             sample_end->p.pos = bez[2];
1701         }
1702        
1703         //destroy this contiguous selection
1704         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
1705             Inkscape::NodePath::Node *temp = delete_cursor;
1706             if (delete_cursor->n.other == delete_cursor) {
1707                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
1708                 delete_cursor = NULL; 
1709             } else {
1710                 delete_cursor = delete_cursor->n.other;
1711             }
1712             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
1713             sp_nodepath_node_destroy(temp);
1714         }
1716         //clean up the nodepath (such as for trivial subpaths)
1717         sp_nodepath_cleanup(nodepath);
1719         sp_nodepath_update_handles(nodepath);
1721         // if the entire nodepath is removed, delete the selected object.
1722         if (nodepath->subpaths == NULL ||
1723             sp_nodepath_get_node_count(nodepath) < 2) {
1724             SPDocument *document = SP_DT_DOCUMENT (nodepath->desktop);
1725             sp_nodepath_destroy(nodepath);
1726             g_list_free(nodes_to_delete);
1727             nodes_to_delete = NULL;
1728             //is the next line necessary?
1729             sp_selection_delete();
1730             sp_document_done (document);
1731             return;
1732         }
1734         sp_nodepath_update_repr(nodepath);
1736         sp_nodepath_update_statusbar(nodepath);
1737     }
1740 /**
1741  * Delete one or more selected nodes.
1742  */
1743 void sp_node_selected_delete()
1745     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1746     if (!nodepath) return;
1747     if (!nodepath->selected) return;
1749     /** \todo fixme: do it the right way */
1750     while (nodepath->selected) {
1751        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
1752         sp_nodepath_node_destroy(node);
1753     }
1756     //clean up the nodepath (such as for trivial subpaths)
1757     sp_nodepath_cleanup(nodepath);
1759     sp_nodepath_update_handles(nodepath);
1761     // if the entire nodepath is removed, delete the selected object.
1762     if (nodepath->subpaths == NULL ||
1763         sp_nodepath_get_node_count(nodepath) < 2) {
1764         SPDocument *document = SP_DT_DOCUMENT (nodepath->desktop);
1765         sp_nodepath_destroy(nodepath);
1766         sp_selection_delete();
1767         sp_document_done (document);
1768         return;
1769     }
1771     sp_nodepath_update_repr(nodepath);
1773     sp_nodepath_update_statusbar(nodepath);
1776 /**
1777  * Delete one or more segments between two selected nodes.
1778  * This is the code for 'split'.
1779  */
1780 void
1781 sp_node_selected_delete_segment(void)
1783    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
1784    Inkscape::NodePath::Node *curr, *next;     //Iterators
1786     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1787     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1789     if (g_list_length(nodepath->selected) != 2) {
1790         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1791                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1792         return;
1793     }
1795     //Selected nodes, not inclusive
1796    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1797    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1799     if ( ( a==b)                       ||  //same node
1800          (a->subpath  != b->subpath )  ||  //not the same path
1801          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
1802          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
1803     {
1804         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1805                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1806         return;
1807     }
1809     //###########################################
1810     //# BEGIN EDITS
1811     //###########################################
1812     //##################################
1813     //# CLOSED PATH
1814     //##################################
1815     if (a->subpath->closed) {
1818         gboolean reversed = FALSE;
1820         //Since we can go in a circle, we need to find the shorter distance.
1821         //  a->b or b->a
1822         start = end = NULL;
1823         int distance    = 0;
1824         int minDistance = 0;
1825         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
1826             if (curr==b) {
1827                 //printf("a to b:%d\n", distance);
1828                 start = a;//go from a to b
1829                 end   = b;
1830                 minDistance = distance;
1831                 //printf("A to B :\n");
1832                 break;
1833             }
1834             distance++;
1835         }
1837         //try again, the other direction
1838         distance = 0;
1839         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
1840             if (curr==a) {
1841                 //printf("b to a:%d\n", distance);
1842                 if (distance < minDistance) {
1843                     start    = b;  //we go from b to a
1844                     end      = a;
1845                     reversed = TRUE;
1846                     //printf("B to A\n");
1847                 }
1848                 break;
1849             }
1850             distance++;
1851         }
1854         //Copy everything from 'end' to 'start' to a new subpath
1855        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
1856         for (curr=end ; curr ; curr=curr->n.other) {
1857             NRPathcode code = (NRPathcode) curr->code;
1858             if (curr == end)
1859                 code = NR_MOVETO;
1860             sp_nodepath_node_new(t, NULL,
1861                                  (Inkscape::NodePath::NodeType)curr->type, code,
1862                                  &curr->p.pos, &curr->pos, &curr->n.pos);
1863             if (curr == start)
1864                 break;
1865         }
1866         sp_nodepath_subpath_destroy(a->subpath);
1869     }
1873     //##################################
1874     //# OPEN PATH
1875     //##################################
1876     else {
1878         //We need to get the direction of the list between A and B
1879         //Can we walk from a to b?
1880         start = end = NULL;
1881         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
1882             if (curr==b) {
1883                 start = a;  //did it!  we go from a to b
1884                 end   = b;
1885                 //printf("A to B\n");
1886                 break;
1887             }
1888         }
1889         if (!start) {//didn't work?  let's try the other direction
1890             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
1891                 if (curr==a) {
1892                     start = b;  //did it!  we go from b to a
1893                     end   = a;
1894                     //printf("B to A\n");
1895                     break;
1896                 }
1897             }
1898         }
1899         if (!start) {
1900             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1901                                                      _("Cannot find path between nodes."));
1902             return;
1903         }
1907         //Copy everything after 'end' to a new subpath
1908        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
1909         for (curr=end ; curr ; curr=curr->n.other) {
1910             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
1911                                  &curr->p.pos, &curr->pos, &curr->n.pos);
1912         }
1914         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
1915         for (curr = start->n.other ; curr  ; curr=next) {
1916             next = curr->n.other;
1917             sp_nodepath_node_destroy(curr);
1918         }
1920     }
1921     //###########################################
1922     //# END EDITS
1923     //###########################################
1925     //clean up the nodepath (such as for trivial subpaths)
1926     sp_nodepath_cleanup(nodepath);
1928     sp_nodepath_update_handles(nodepath);
1930     sp_nodepath_update_repr(nodepath);
1932     // if the entire nodepath is removed, delete the selected object.
1933     if (nodepath->subpaths == NULL ||
1934         sp_nodepath_get_node_count(nodepath) < 2) {
1935         sp_nodepath_destroy(nodepath);
1936         sp_selection_delete();
1937         return;
1938     }
1940     sp_nodepath_update_statusbar(nodepath);
1943 /**
1944  * Call sp_nodepath_set_line() for all selected segments.
1945  */
1946 void
1947 sp_node_selected_set_line_type(NRPathcode code)
1949     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1950     if (nodepath == NULL) return;
1952     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1953        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1954         g_assert(n->selected);
1955         if (n->p.other && n->p.other->selected) {
1956             sp_nodepath_set_line_type(n, code);
1957         }
1958     }
1960     sp_nodepath_update_repr(nodepath);
1963 /**
1964  * Call sp_nodepath_convert_node_type() for all selected nodes.
1965  */
1966 void
1967 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
1969     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1970     if (nodepath == NULL) return;
1972     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1973         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
1974     }
1976     sp_nodepath_update_repr(nodepath);
1979 /**
1980  * Change select status of node, update its own and neighbour handles.
1981  */
1982 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
1984     node->selected = selected;
1986     if (selected) {
1987         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
1988         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
1989         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
1990         sp_knot_update_ctrl(node->knot);
1991     } else {
1992         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
1993         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
1994         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
1995         sp_knot_update_ctrl(node->knot);
1996     }
1998     sp_node_update_handles(node);
1999     if (node->n.other) sp_node_update_handles(node->n.other);
2000     if (node->p.other) sp_node_update_handles(node->p.other);
2003 /**
2004 \brief Select a node
2005 \param node     The node to select
2006 \param incremental   If true, add to selection, otherwise deselect others
2007 \param override   If true, always select this node, otherwise toggle selected status
2008 */
2009 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2011     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2013     if (incremental) {
2014         if (override) {
2015             if (!g_list_find(nodepath->selected, node)) {
2016                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2017             }
2018             sp_node_set_selected(node, TRUE);
2019         } else { // toggle
2020             if (node->selected) {
2021                 g_assert(g_list_find(nodepath->selected, node));
2022                 nodepath->selected = g_list_remove(nodepath->selected, node);
2023             } else {
2024                 g_assert(!g_list_find(nodepath->selected, node));
2025                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2026             }
2027             sp_node_set_selected(node, !node->selected);
2028         }
2029     } else {
2030         sp_nodepath_deselect(nodepath);
2031         nodepath->selected = g_list_prepend(nodepath->selected, node);
2032         sp_node_set_selected(node, TRUE);
2033     }
2035     sp_nodepath_update_statusbar(nodepath);
2039 /**
2040 \brief Deselect all nodes in the nodepath
2041 */
2042 void
2043 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2045     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2047     while (nodepath->selected) {
2048         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2049         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2050     }
2051     sp_nodepath_update_statusbar(nodepath);
2054 /**
2055 \brief Select or invert selection of all nodes in the nodepath
2056 */
2057 void
2058 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2060     if (!nodepath) return;
2062     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2063        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2064         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2065            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2066            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2067         }
2068     }
2071 /**
2072  * If nothing selected, does the same as sp_nodepath_select_all();
2073  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2074  * (i.e., similar to "select all in layer", with the "selected" subpaths
2075  * being treated as "layers" in the path).
2076  */
2077 void
2078 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2080     if (!nodepath) return;
2082     if (g_list_length (nodepath->selected) == 0) {
2083         sp_nodepath_select_all (nodepath, invert);
2084         return;
2085     }
2087     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2088     GSList *subpaths = NULL;
2090     for (GList *l = copy; l != NULL; l = l->next) {
2091         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2092         Inkscape::NodePath::SubPath *subpath = n->subpath;
2093         if (!g_slist_find (subpaths, subpath))
2094             subpaths = g_slist_prepend (subpaths, subpath);
2095     }
2097     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2098         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2099         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2100             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2101             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2102         }
2103     }
2105     g_slist_free (subpaths);
2106     g_list_free (copy);
2109 /**
2110  * \brief Select the node after the last selected; if none is selected,
2111  * select the first within path.
2112  */
2113 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2115     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2117    Inkscape::NodePath::Node *last = NULL;
2118     if (nodepath->selected) {
2119         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2120            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2121             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2122             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2123                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2124                 if (node->selected) {
2125                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2126                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2127                             if (spl->next) { // there's a next subpath
2128                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2129                                 last = subpath_next->first;
2130                             } else if (spl->prev) { // there's a previous subpath
2131                                 last = NULL; // to be set later to the first node of first subpath
2132                             } else {
2133                                 last = node->n.other;
2134                             }
2135                         } else {
2136                             last = node->n.other;
2137                         }
2138                     } else {
2139                         if (node->n.other) {
2140                             last = node->n.other;
2141                         } else {
2142                             if (spl->next) { // there's a next subpath
2143                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2144                                 last = subpath_next->first;
2145                             } else if (spl->prev) { // there's a previous subpath
2146                                 last = NULL; // to be set later to the first node of first subpath
2147                             } else {
2148                                 last = (Inkscape::NodePath::Node *) subpath->first;
2149                             }
2150                         }
2151                     }
2152                 }
2153             }
2154         }
2155         sp_nodepath_deselect(nodepath);
2156     }
2158     if (last) { // there's at least one more node after selected
2159         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2160     } else { // no more nodes, select the first one in first subpath
2161        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2162         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2163     }
2166 /**
2167  * \brief Select the node before the first selected; if none is selected,
2168  * select the last within path
2169  */
2170 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2172     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2174    Inkscape::NodePath::Node *last = NULL;
2175     if (nodepath->selected) {
2176         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2177            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2178             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2179                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2180                 if (node->selected) {
2181                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2182                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2183                             if (spl->prev) { // there's a prev subpath
2184                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2185                                 last = subpath_prev->last;
2186                             } else if (spl->next) { // there's a next subpath
2187                                 last = NULL; // to be set later to the last node of last subpath
2188                             } else {
2189                                 last = node->p.other;
2190                             }
2191                         } else {
2192                             last = node->p.other;
2193                         }
2194                     } else {
2195                         if (node->p.other) {
2196                             last = node->p.other;
2197                         } else {
2198                             if (spl->prev) { // there's a prev subpath
2199                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2200                                 last = subpath_prev->last;
2201                             } else if (spl->next) { // there's a next subpath
2202                                 last = NULL; // to be set later to the last node of last subpath
2203                             } else {
2204                                 last = (Inkscape::NodePath::Node *) subpath->last;
2205                             }
2206                         }
2207                     }
2208                 }
2209             }
2210         }
2211         sp_nodepath_deselect(nodepath);
2212     }
2214     if (last) { // there's at least one more node before selected
2215         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2216     } else { // no more nodes, select the last one in last subpath
2217         GList *spl = g_list_last(nodepath->subpaths);
2218        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2219         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2220     }
2223 /**
2224  * \brief Select all nodes that are within the rectangle.
2225  */
2226 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2228     if (!incremental) {
2229         sp_nodepath_deselect(nodepath);
2230     }
2232     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2233        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2234         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2235            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2237             if (b.contains(node->pos)) {
2238                 sp_nodepath_node_select(node, TRUE, TRUE);
2239             }
2240         }
2241     }
2244 /**
2245 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2246 */
2247 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2249     if (!nodepath->selected) {
2250         return NULL;
2251     }
2253     GList *r = NULL;
2254     guint i = 0;
2255     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2256        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2257         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2258            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2259             i++;
2260             if (node->selected) {
2261                 r = g_list_append(r, GINT_TO_POINTER(i));
2262             }
2263         }
2264     }
2265     return r;
2268 /**
2269 \brief  Restores selection by selecting nodes whose positions are in the list
2270 */
2271 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2273     sp_nodepath_deselect(nodepath);
2275     guint i = 0;
2276     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2277        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2278         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2279            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2280             i++;
2281             if (g_list_find(r, GINT_TO_POINTER(i))) {
2282                 sp_nodepath_node_select(node, TRUE, TRUE);
2283             }
2284         }
2285     }
2289 /**
2290 \brief Adjusts handle according to node type and line code.
2291 */
2292 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2294     double len, otherlen, linelen;
2296     g_assert(node);
2298    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2299    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2301     /** \todo fixme: */
2302     if (me->other == NULL) return;
2303     if (other->other == NULL) return;
2305     /* I have line */
2307     NRPathcode mecode, ocode;
2308     if (which_adjust == 1) {
2309         mecode = (NRPathcode)me->other->code;
2310         ocode = (NRPathcode)node->code;
2311     } else {
2312         mecode = (NRPathcode)node->code;
2313         ocode = (NRPathcode)other->other->code;
2314     }
2316     if (mecode == NR_LINETO) return;
2318     /* I am curve */
2320     if (other->other == NULL) return;
2322     /* Other has line */
2324     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2326     NR::Point delta;
2327     if (ocode == NR_LINETO) {
2328         /* other is lineto, we are either smooth or symm */
2329        Inkscape::NodePath::Node *othernode = other->other;
2330         len = NR::L2(me->pos - node->pos);
2331         delta = node->pos - othernode->pos;
2332         linelen = NR::L2(delta);
2333         if (linelen < 1e-18) 
2334             return;
2335         me->pos = node->pos + (len / linelen)*delta;
2336         return;
2337     }
2339     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2341         me->pos = 2 * node->pos - other->pos;
2342         return;
2343     }
2345     /* We are smooth */
2347     len = NR::L2(me->pos - node->pos);
2348     delta = other->pos - node->pos;
2349     otherlen = NR::L2(delta);
2350     if (otherlen < 1e-18) return;
2352     me->pos = node->pos - (len / otherlen) * delta;
2355 /**
2356  \brief Adjusts both handles according to node type and line code
2357  */
2358 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2360     g_assert(node);
2362     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2364     /* we are either smooth or symm */
2366     if (node->p.other == NULL) return;
2368     if (node->n.other == NULL) return;
2370     if (node->code == NR_LINETO) {
2371         if (node->n.other->code == NR_LINETO) return;
2372         sp_node_adjust_handle(node, 1);
2373         return;
2374     }
2376     if (node->n.other->code == NR_LINETO) {
2377         if (node->code == NR_LINETO) return;
2378         sp_node_adjust_handle(node, -1);
2379         return;
2380     }
2382     /* both are curves */
2383     NR::Point const delta( node->n.pos - node->p.pos );
2385     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2386         node->p.pos = node->pos - delta / 2;
2387         node->n.pos = node->pos + delta / 2;
2388         return;
2389     }
2391     /* We are smooth */
2392     double plen = NR::L2(node->p.pos - node->pos);
2393     if (plen < 1e-18) return;
2394     double nlen = NR::L2(node->n.pos - node->pos);
2395     if (nlen < 1e-18) return;
2396     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2397     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2400 /**
2401  * Node event callback.
2402  */
2403 static gboolean node_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2405     gboolean ret = FALSE;
2406     switch (event->type) {
2407         case GDK_ENTER_NOTIFY:
2408             active_node = n;
2409             break;
2410         case GDK_LEAVE_NOTIFY:
2411             active_node = NULL;
2412             break;
2413         case GDK_KEY_PRESS:
2414             switch (get_group0_keyval (&event->key)) {
2415                 case GDK_space:
2416                     if (event->key.state & GDK_BUTTON1_MASK) {
2417                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2418                         stamp_repr(nodepath);
2419                         ret = TRUE;
2420                     }
2421                     break;
2422                 default:
2423                     break;
2424             }
2425             break;
2426         default:
2427             break;
2428     }
2430     return ret;
2433 /**
2434  * Handle keypress on node; directly called.
2435  */
2436 gboolean node_key(GdkEvent *event)
2438     Inkscape::NodePath::Path *np;
2440     // there is no way to verify nodes so set active_node to nil when deleting!!
2441     if (active_node == NULL) return FALSE;
2443     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2444         gint ret = FALSE;
2445         switch (get_group0_keyval (&event->key)) {
2446             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2447             case GDK_BackSpace:
2448                 np = active_node->subpath->nodepath;
2449                 sp_nodepath_node_destroy(active_node);
2450                 sp_nodepath_update_repr(np);
2451                 active_node = NULL;
2452                 ret = TRUE;
2453                 break;
2454             case GDK_c:
2455                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2456                 ret = TRUE;
2457                 break;
2458             case GDK_s:
2459                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2460                 ret = TRUE;
2461                 break;
2462             case GDK_y:
2463                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2464                 ret = TRUE;
2465                 break;
2466             case GDK_b:
2467                 sp_nodepath_node_break(active_node);
2468                 ret = TRUE;
2469                 break;
2470         }
2471         return ret;
2472     }
2473     return FALSE;
2476 /**
2477  * Mouseclick on node callback.
2478  */
2479 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2481    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2483     if (state & GDK_CONTROL_MASK) {
2484         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2486         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2487             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2488                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2489             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2490                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2491             } else {
2492                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2493             }
2494             sp_nodepath_update_repr(nodepath);
2495             sp_nodepath_update_statusbar(nodepath);
2497         } else { //ctrl+alt+click: delete node
2498             GList *node_to_delete = NULL;
2499             node_to_delete = g_list_append(node_to_delete, n);
2500             sp_node_delete_preserve(node_to_delete);
2501         }
2503     } else {
2504         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2505     }
2508 /**
2509  * Mouse grabbed node callback.
2510  */
2511 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2513    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2515     n->origin = knot->pos;
2517     if (!n->selected) {
2518         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2519     }
2522 /**
2523  * Mouse ungrabbed node callback.
2524  */
2525 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
2527    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2529    n->dragging_out = NULL;
2531    sp_nodepath_update_repr(n->subpath->nodepath);
2534 /**
2535  * The point on a line, given by its angle, closest to the given point.
2536  * \param p  A point.
2537  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2538  * \param closest  Pointer to the point struct where the result is stored.
2539  * \todo FIXME: use dot product perhaps?
2540  */
2541 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
2543     if (a == HUGE_VAL) { // vertical
2544         *closest = NR::Point(0, (*p)[NR::Y]);
2545     } else {
2546         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
2547         (*closest)[NR::Y] = a * (*closest)[NR::X];
2548     }
2551 /**
2552  * Distance from the point to a line given by its angle.
2553  * \param p  A point.
2554  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2555  */
2556 static double point_line_distance(NR::Point *p, double a)
2558     NR::Point c;
2559     point_line_closest(p, a, &c);
2560     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]));
2563 /**
2564  * Callback for node "request" signal.
2565  * \todo fixme: This goes to "moved" event? (lauris)
2566  */
2567 static gboolean
2568 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2570     double yn, xn, yp, xp;
2571     double an, ap, na, pa;
2572     double d_an, d_ap, d_na, d_pa;
2573     gboolean collinear = FALSE;
2574     NR::Point c;
2575     NR::Point pr;
2577    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2579    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
2580    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
2582        NR::Point mouse = (*p);
2584        if (!n->dragging_out) {
2585            // This is the first drag-out event; find out which handle to drag out
2586            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
2587            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
2589            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
2590                return FALSE;
2592            Inkscape::NodePath::NodeSide *opposite;
2593            if (appr_p > appr_n) { // closer to p
2594                n->dragging_out = &n->p;
2595                opposite = &n->n;
2596                n->code = NR_CURVETO;
2597            } else if (appr_p < appr_n) { // closer to n
2598                n->dragging_out = &n->n;
2599                opposite = &n->p;
2600                n->n.other->code = NR_CURVETO;
2601            } else { // p and n nodes are the same
2602                if (n->n.pos != n->pos) { // n handle already dragged, drag p
2603                    n->dragging_out = &n->p;
2604                    opposite = &n->n;
2605                    n->code = NR_CURVETO;
2606                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
2607                    n->dragging_out = &n->n;
2608                    opposite = &n->p;
2609                    n->n.other->code = NR_CURVETO;
2610                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
2611                    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);
2612                    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);
2613                    if (appr_other_p > appr_other_n) { // closer to other's p handle
2614                        n->dragging_out = &n->n;
2615                        opposite = &n->p;
2616                        n->n.other->code = NR_CURVETO;
2617                    } else { // closer to other's n handle
2618                        n->dragging_out = &n->p;
2619                        opposite = &n->n;
2620                        n->code = NR_CURVETO;
2621                    }
2622                }
2623            }
2625            // if there's another handle, make sure the one we drag out starts parallel to it
2626            if (opposite->pos != n->pos) {
2627                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
2628            }
2630            // knots might not be created yet!
2631            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
2632            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
2633        }
2635        // pass this on to the handle-moved callback
2636        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
2637        sp_node_update_handles(n);
2638        return TRUE;
2639    }
2641     if (state & GDK_CONTROL_MASK) { // constrained motion
2643         // calculate relative distances of handles
2644         // n handle:
2645         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
2646         xn = n->n.pos[NR::X] - n->pos[NR::X];
2647         // if there's no n handle (straight line), see if we can use the direction to the next point on path
2648         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
2649             if (n->n.other) { // if there is the next point
2650                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
2651                     yn = n->n.other->pos[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
2652                     xn = n->n.other->pos[NR::X] - n->origin[NR::X];
2653             }
2654         }
2655         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
2656         if (yn < 0) { xn = -xn; yn = -yn; }
2658         // p handle:
2659         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
2660         xp = n->p.pos[NR::X] - n->pos[NR::X];
2661         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
2662         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
2663             if (n->p.other) {
2664                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
2665                     yp = n->p.other->pos[NR::Y] - n->origin[NR::Y];
2666                     xp = n->p.other->pos[NR::X] - n->origin[NR::X];
2667             }
2668         }
2669         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
2670         if (yp < 0) { xp = -xp; yp = -yp; }
2672         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
2673             // sliding on handles, only if at least one of the handles is non-vertical
2674             // (otherwise it's the same as ctrl+drag anyway)
2676             // calculate angles of the handles
2677             if (xn == 0) {
2678                 if (yn == 0) { // no handle, consider it the continuation of the other one
2679                     an = 0;
2680                     collinear = TRUE;
2681                 }
2682                 else an = 0; // vertical; set the angle to horizontal
2683             } else an = yn/xn;
2685             if (xp == 0) {
2686                 if (yp == 0) { // no handle, consider it the continuation of the other one
2687                     ap = an;
2688                 }
2689                 else ap = 0; // vertical; set the angle to horizontal
2690             } else  ap = yp/xp;
2692             if (collinear) an = ap;
2694             // angles of the perpendiculars; HUGE_VAL means vertical
2695             if (an == 0) na = HUGE_VAL; else na = -1/an;
2696             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
2698             //g_print("an %g    ap %g\n", an, ap);
2700             // mouse point relative to the node's original pos
2701             pr = (*p) - n->origin;
2703             // distances to the four lines (two handles and two perpendiculars)
2704             d_an = point_line_distance(&pr, an);
2705             d_na = point_line_distance(&pr, na);
2706             d_ap = point_line_distance(&pr, ap);
2707             d_pa = point_line_distance(&pr, pa);
2709             // find out which line is the closest, save its closest point in c
2710             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
2711                 point_line_closest(&pr, an, &c);
2712             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
2713                 point_line_closest(&pr, ap, &c);
2714             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
2715                 point_line_closest(&pr, na, &c);
2716             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
2717                 point_line_closest(&pr, pa, &c);
2718             }
2720             // move the node to the closest point
2721             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2722                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
2723                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
2725         } else {  // constraining to hor/vert
2727             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
2728                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
2729             } else { // snap to vert
2730                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
2731             }
2732         }
2733     } else { // move freely
2734         sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2735                                         (*p)[NR::X] - n->pos[NR::X],
2736                                         (*p)[NR::Y] - n->pos[NR::Y],
2737                                         (state & GDK_SHIFT_MASK) == 0);
2738     }
2740     n->subpath->nodepath->desktop->scroll_to_point(p);
2742     return TRUE;
2745 /**
2746  * Node handle clicked callback.
2747  */
2748 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
2750    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2752     if (state & GDK_CONTROL_MASK) { // "delete" handle
2753         if (n->p.knot == knot) {
2754             n->p.pos = n->pos;
2755         } else if (n->n.knot == knot) {
2756             n->n.pos = n->pos;
2757         }
2758         sp_node_update_handles(n);
2759         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2760         sp_nodepath_update_repr(nodepath);
2761         sp_nodepath_update_statusbar(nodepath);
2763     } else { // just select or add to selection, depending in Shift
2764         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2765     }
2768 /**
2769  * Node handle grabbed callback.
2770  */
2771 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
2773    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2775     if (!n->selected) {
2776         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2777     }
2779     // remember the origin point of the handle
2780     if (n->p.knot == knot) {
2781         n->p.origin = n->p.pos - n->pos;
2782     } else if (n->n.knot == knot) {
2783         n->n.origin = n->n.pos - n->pos;
2784     } else {
2785         g_assert_not_reached();
2786     }
2790 /**
2791  * Node handle ungrabbed callback.
2792  */
2793 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
2795    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2797     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
2798     if (n->p.knot == knot) {
2799         n->p.origin.a = 0;
2800         sp_knot_set_position(knot, &n->p.pos, state);
2801     } else if (n->n.knot == knot) {
2802         n->n.origin.a = 0;
2803         sp_knot_set_position(knot, &n->n.pos, state);
2804     } else {
2805         g_assert_not_reached();
2806     }
2808     sp_nodepath_update_repr(n->subpath->nodepath);
2811 /**
2812  * Node handle "request" signal callback.
2813  */
2814 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2816     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2818     Inkscape::NodePath::NodeSide *me, *opposite;
2819     gint which;
2820     if (n->p.knot == knot) {
2821         me = &n->p;
2822         opposite = &n->n;
2823         which = -1;
2824     } else if (n->n.knot == knot) {
2825         me = &n->n;
2826         opposite = &n->p;
2827         which = 1;
2828     } else {
2829         me = opposite = NULL;
2830         which = 0;
2831         g_assert_not_reached();
2832     }
2834     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
2836     SnapManager const m(n->subpath->nodepath->desktop->namedview);
2838     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
2839         /* We are smooth node adjacent with line */
2840         NR::Point const delta = *p - n->pos;
2841         NR::Coord const len = NR::L2(delta);
2842         Inkscape::NodePath::Node *othernode = opposite->other;
2843         NR::Point const ndelta = n->pos - othernode->pos;
2844         NR::Coord const linelen = NR::L2(ndelta);
2845         if (len > NR_EPSILON && linelen > NR_EPSILON) {
2846             NR::Coord const scal = dot(delta, ndelta) / linelen;
2847             (*p) = n->pos + (scal / linelen) * ndelta;
2848         }
2849         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, ndelta, NULL).getPoint();
2850     } else {
2851         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
2852     }
2854     sp_node_adjust_handle(n, -which);
2856     return FALSE;
2859 /**
2860  * Node handle moved callback.
2861  */
2862 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2864    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2866    Inkscape::NodePath::NodeSide *me;
2867    Inkscape::NodePath::NodeSide *other;
2868     if (n->p.knot == knot) {
2869         me = &n->p;
2870         other = &n->n;
2871     } else if (n->n.knot == knot) {
2872         me = &n->n;
2873         other = &n->p;
2874     } else {
2875         me = NULL;
2876         other = NULL;
2877         g_assert_not_reached();
2878     }
2880     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
2881     Radial rme(me->pos - n->pos);
2882     Radial rother(other->pos - n->pos);
2883     Radial rnew(*p - n->pos);
2885     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
2886         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
2887         /* 0 interpreted as "no snapping". */
2889         // The closest PI/snaps angle, starting from zero.
2890         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
2891         if (me->origin.a == HUGE_VAL) {
2892             // ortho doesn't exist: original handle was zero length.
2893             rnew.a = a_snapped;
2894         } else {
2895             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
2896              * its opposite and perpendiculars). */
2897             double const a_ortho = me->origin.a + floor((rnew.a - me->origin.a)/(M_PI/2) + 0.5) * (M_PI/2);
2899             // Snap to the closest.
2900             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
2901                        ? a_snapped
2902                        : a_ortho );
2903         }
2904     }
2906     if (state & GDK_MOD1_MASK) {
2907         // lock handle length
2908         rnew.r = me->origin.r;
2909     }
2911     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
2912         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
2913         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
2914         rother.a += rnew.a - rme.a;
2915         other->pos = NR::Point(rother) + n->pos;
2916         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
2917         sp_knot_set_position(other->knot, &other->pos, 0);
2918     }
2920     me->pos = NR::Point(rnew) + n->pos;
2921     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
2923     // this is what sp_knot_set_position does, but without emitting the signal:
2924     // we cannot emit a "moved" signal because we're now processing it
2925     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
2927     knot->desktop->set_coordinate_status(me->pos);
2929     update_object(n->subpath->nodepath);
2931     /* status text */
2932     SPDesktop *desktop = n->subpath->nodepath->desktop;
2933     if (!desktop) return;
2934     SPEventContext *ec = desktop->event_context;
2935     if (!ec) return;
2936     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
2937     if (!mc) return;
2939     double degrees = 180 / M_PI * rnew.a;
2940     if (degrees > 180) degrees -= 360;
2941     if (degrees < -180) degrees += 360;
2942     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
2943         degrees = angle_to_compass (degrees);
2945     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
2947     mc->setF(Inkscape::NORMAL_MESSAGE,
2948          _("<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);
2950     g_string_free(length, TRUE);
2953 /**
2954  * Node handle event callback.
2955  */
2956 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2958     gboolean ret = FALSE;
2959     switch (event->type) {
2960         case GDK_KEY_PRESS:
2961             switch (get_group0_keyval (&event->key)) {
2962                 case GDK_space:
2963                     if (event->key.state & GDK_BUTTON1_MASK) {
2964                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2965                         stamp_repr(nodepath);
2966                         ret = TRUE;
2967                     }
2968                     break;
2969                 default:
2970                     break;
2971             }
2972             break;
2973         default:
2974             break;
2975     }
2977     return ret;
2980 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
2981                                  Radial &rme, Radial &rother, gboolean const both)
2983     rme.a += angle;
2984     if ( both
2985          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2986          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2987     {
2988         rother.a += angle;
2989     }
2992 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
2993                                         Radial &rme, Radial &rother, gboolean const both)
2995     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
2997     gdouble r;
2998     if ( both
2999          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3000          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3001     {
3002         r = MAX(rme.r, rother.r);
3003     } else {
3004         r = rme.r;
3005     }
3007     gdouble const weird_angle = atan2(norm_angle, r);
3008 /* Bulia says norm_angle is just the visible distance that the
3009  * object's end must travel on the screen.  Left as 'angle' for want of
3010  * a better name.*/
3012     rme.a += weird_angle;
3013     if ( both
3014          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3015          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3016     {
3017         rother.a += weird_angle;
3018     }
3021 /**
3022  * Rotate one node.
3023  */
3024 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3026     Inkscape::NodePath::NodeSide *me, *other;
3027     bool both = false;
3029     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3030     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3032     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3033         me = &(n->p);
3034         other = &(n->n);
3035     } else if (!n->p.other) {
3036         me = &(n->n);
3037         other = &(n->p);
3038     } else {
3039         if (which > 0) { // right handle
3040             if (xn > xp) {
3041                 me = &(n->n);
3042                 other = &(n->p);
3043             } else {
3044                 me = &(n->p);
3045                 other = &(n->n);
3046             }
3047         } else if (which < 0){ // left handle
3048             if (xn <= xp) {
3049                 me = &(n->n);
3050                 other = &(n->p);
3051             } else {
3052                 me = &(n->p);
3053                 other = &(n->n);
3054             }
3055         } else { // both handles
3056             me = &(n->n);
3057             other = &(n->p);
3058             both = true;
3059         }
3060     }
3062     Radial rme(me->pos - n->pos);
3063     Radial rother(other->pos - n->pos);
3065     if (screen) {
3066         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3067     } else {
3068         node_rotate_one_internal (*n, angle, rme, rother, both);
3069     }
3071     me->pos = n->pos + NR::Point(rme);
3073     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3074         other->pos =  n->pos + NR::Point(rother);
3075     }
3077     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3078     // so here we just move all the knots without emitting move signals, for speed
3079     sp_node_update_handles(n, false);
3082 /**
3083  * Rotate selected nodes.
3084  */
3085 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3087     if (!nodepath || !nodepath->selected) return;
3089     if (g_list_length(nodepath->selected) == 1) {
3090        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3091         node_rotate_one (n, angle, which, screen);
3092     } else {
3093        // rotate as an object:
3095         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3096         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3097         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3098             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3099             box.expandTo (n->pos); // contain all selected nodes
3100         }
3102         gdouble rot;
3103         if (screen) {
3104             gdouble const zoom = nodepath->desktop->current_zoom();
3105             gdouble const zmove = angle / zoom;
3106             gdouble const r = NR::L2(box.max() - box.midpoint());
3107             rot = atan2(zmove, r);
3108         } else {
3109             rot = angle;
3110         }
3112         NR::Matrix t =
3113             NR::Matrix (NR::translate(-box.midpoint())) *
3114             NR::Matrix (NR::rotate(rot)) *
3115             NR::Matrix (NR::translate(box.midpoint()));
3117         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3118             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3119             n->pos *= t;
3120             n->n.pos *= t;
3121             n->p.pos *= t;
3122             sp_node_update_handles(n, false);
3123         }
3124     }
3126     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n");
3129 /**
3130  * Scale one node.
3131  */
3132 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3134     bool both = false;
3135     Inkscape::NodePath::NodeSide *me, *other;
3137     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3138     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3140     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3141         me = &(n->p);
3142         other = &(n->n);
3143         n->code = NR_CURVETO;
3144     } else if (!n->p.other) {
3145         me = &(n->n);
3146         other = &(n->p);
3147         if (n->n.other)
3148             n->n.other->code = NR_CURVETO;
3149     } else {
3150         if (which > 0) { // right handle
3151             if (xn > xp) {
3152                 me = &(n->n);
3153                 other = &(n->p);
3154                 if (n->n.other)
3155                     n->n.other->code = NR_CURVETO;
3156             } else {
3157                 me = &(n->p);
3158                 other = &(n->n);
3159                 n->code = NR_CURVETO;
3160             }
3161         } else if (which < 0){ // left handle
3162             if (xn <= xp) {
3163                 me = &(n->n);
3164                 other = &(n->p);
3165                 if (n->n.other)
3166                     n->n.other->code = NR_CURVETO;
3167             } else {
3168                 me = &(n->p);
3169                 other = &(n->n);
3170                 n->code = NR_CURVETO;
3171             }
3172         } else { // both handles
3173             me = &(n->n);
3174             other = &(n->p);
3175             both = true;
3176             n->code = NR_CURVETO;
3177             if (n->n.other)
3178                 n->n.other->code = NR_CURVETO;
3179         }
3180     }
3182     Radial rme(me->pos - n->pos);
3183     Radial rother(other->pos - n->pos);
3185     rme.r += grow;
3186     if (rme.r < 0) rme.r = 0;
3187     if (rme.a == HUGE_VAL) {
3188         if (me->other) { // if direction is unknown, initialize it towards the next node
3189             Radial rme_next(me->other->pos - n->pos);
3190             rme.a = rme_next.a;
3191         } else { // if there's no next, initialize to 0
3192             rme.a = 0;
3193         }
3194     }
3195     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3196         rother.r += grow;
3197         if (rother.r < 0) rother.r = 0;
3198         if (rother.a == HUGE_VAL) {
3199             rother.a = rme.a + M_PI;
3200         }
3201     }
3203     me->pos = n->pos + NR::Point(rme);
3205     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3206         other->pos = n->pos + NR::Point(rother);
3207     }
3209     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3210     // so here we just move all the knots without emitting move signals, for speed
3211     sp_node_update_handles(n, false);
3214 /**
3215  * Scale selected nodes.
3216  */
3217 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3219     if (!nodepath || !nodepath->selected) return;
3221     if (g_list_length(nodepath->selected) == 1) {
3222         // scale handles of the single selected node
3223         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3224         node_scale_one (n, grow, which);
3225     } else {
3226         // scale nodes as an "object":
3228         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3229         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3230         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3231             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3232             box.expandTo (n->pos); // contain all selected nodes
3233         }
3235         double scale = (box.maxExtent() + grow)/box.maxExtent();
3237         NR::Matrix t =
3238             NR::Matrix (NR::translate(-box.midpoint())) *
3239             NR::Matrix (NR::scale(scale, scale)) *
3240             NR::Matrix (NR::translate(box.midpoint()));
3242         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3243             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3244             n->pos *= t;
3245             n->n.pos *= t;
3246             n->p.pos *= t;
3247             sp_node_update_handles(n, false);
3248         }
3249     }
3251     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n");
3254 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3256     if (!nodepath) return;
3257     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3260 /**
3261  * Flip selected nodes horizontally/vertically.
3262  */
3263 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3265     if (!nodepath || !nodepath->selected) return;
3267     if (g_list_length(nodepath->selected) == 1) {
3268         // flip handles of the single selected node
3269         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3270         double temp = n->p.pos[axis];
3271         n->p.pos[axis] = n->n.pos[axis];
3272         n->n.pos[axis] = temp;
3273         sp_node_update_handles(n, false);
3274     } else {
3275         // scale nodes as an "object":
3277         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3278         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3279         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3280             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3281             box.expandTo (n->pos); // contain all selected nodes
3282         }
3284         NR::Matrix t =
3285             NR::Matrix (NR::translate(-box.midpoint())) *
3286             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3287             NR::Matrix (NR::translate(box.midpoint()));
3289         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3290             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3291             n->pos *= t;
3292             n->n.pos *= t;
3293             n->p.pos *= t;
3294             sp_node_update_handles(n, false);
3295         }
3296     }
3298     sp_nodepath_update_repr(nodepath);
3301 //-----------------------------------------------
3302 /**
3303  * Return new subpath under given nodepath.
3304  */
3305 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3307     g_assert(nodepath);
3308     g_assert(nodepath->desktop);
3310    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3312     s->nodepath = nodepath;
3313     s->closed = FALSE;
3314     s->nodes = NULL;
3315     s->first = NULL;
3316     s->last = NULL;
3318     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3319     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3320     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3322     return s;
3325 /**
3326  * Destroy nodes in subpath, then subpath itself.
3327  */
3328 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3330     g_assert(subpath);
3331     g_assert(subpath->nodepath);
3332     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3334     while (subpath->nodes) {
3335         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3336     }
3338     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3340     g_free(subpath);
3343 /**
3344  * Link head to tail in subpath.
3345  */
3346 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3348     g_assert(!sp->closed);
3349     g_assert(sp->last != sp->first);
3350     g_assert(sp->first->code == NR_MOVETO);
3352     sp->closed = TRUE;
3354     //Link the head to the tail
3355     sp->first->p.other = sp->last;
3356     sp->last->n.other  = sp->first;
3357     sp->last->n.pos    = sp->first->n.pos;
3358     sp->first          = sp->last;
3360     //Remove the extra end node
3361     sp_nodepath_node_destroy(sp->last->n.other);
3364 /**
3365  * Open closed (loopy) subpath at node.
3366  */
3367 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3369     g_assert(sp->closed);
3370     g_assert(n->subpath == sp);
3371     g_assert(sp->first == sp->last);
3373     /* We create new startpoint, current node will become last one */
3375    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3376                                                 &n->pos, &n->pos, &n->n.pos);
3379     sp->closed        = FALSE;
3381     //Unlink to make a head and tail
3382     sp->first         = new_path;
3383     sp->last          = n;
3384     n->n.other        = NULL;
3385     new_path->p.other = NULL;
3388 /**
3389  * Returns area in triangle given by points; may be negative.
3390  */
3391 inline double
3392 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3394     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]);
3397 /**
3398  * Return new node in subpath with given properties.
3399  * \param pos Position of node.
3400  * \param ppos Handle position in previous direction
3401  * \param npos Handle position in previous direction
3402  */
3403 Inkscape::NodePath::Node *
3404 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)
3406     g_assert(sp);
3407     g_assert(sp->nodepath);
3408     g_assert(sp->nodepath->desktop);
3410     if (nodechunk == NULL)
3411         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3413     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3415     n->subpath  = sp;
3417     if (type != Inkscape::NodePath::NODE_NONE) {
3418         // use the type from sodipodi:nodetypes
3419         n->type = type;
3420     } else {
3421         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3422             // points are (almost) collinear
3423             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3424                 // endnode, or a node with a retracted handle
3425                 n->type = Inkscape::NodePath::NODE_CUSP;
3426             } else {
3427                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3428             }
3429         } else {
3430             n->type = Inkscape::NodePath::NODE_CUSP;
3431         }
3432     }
3434     n->code     = code;
3435     n->selected = FALSE;
3436     n->pos      = *pos;
3437     n->p.pos    = *ppos;
3438     n->n.pos    = *npos;
3440     n->dragging_out = NULL;
3442     Inkscape::NodePath::Node *prev;
3443     if (next) {
3444         //g_assert(g_list_find(sp->nodes, next));
3445         prev = next->p.other;
3446     } else {
3447         prev = sp->last;
3448     }
3450     if (prev)
3451         prev->n.other = n;
3452     else
3453         sp->first = n;
3455     if (next)
3456         next->p.other = n;
3457     else
3458         sp->last = n;
3460     n->p.other = prev;
3461     n->n.other = next;
3463     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"));
3464     sp_knot_set_position(n->knot, pos, 0);
3466     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3467     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3468     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3469     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3470     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3471     sp_knot_update_ctrl(n->knot);
3473     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3474     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3475     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3476     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3477     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3478     sp_knot_show(n->knot);
3480     // We only create handle knots and lines on demand
3481     n->p.knot = NULL;
3482     n->p.line = NULL;
3483     n->n.knot = NULL;
3484     n->n.line = NULL;
3486     sp->nodes = g_list_prepend(sp->nodes, n);
3488     return n;
3491 /**
3492  * Destroy node and its knots, link neighbors in subpath.
3493  */
3494 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3496     g_assert(node);
3497     g_assert(node->subpath);
3498     g_assert(SP_IS_KNOT(node->knot));
3500    Inkscape::NodePath::SubPath *sp = node->subpath;
3502     if (node->selected) { // first, deselect
3503         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3504         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3505     }
3507     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3509     g_object_unref(G_OBJECT(node->knot));
3510     if (node->p.knot)
3511         g_object_unref(G_OBJECT(node->p.knot));
3512     if (node->n.knot)
3513         g_object_unref(G_OBJECT(node->n.knot));
3515     if (node->p.line)
3516         gtk_object_destroy(GTK_OBJECT(node->p.line));
3517     if (node->n.line)
3518         gtk_object_destroy(GTK_OBJECT(node->n.line));
3520     if (sp->nodes) { // there are others nodes on the subpath
3521         if (sp->closed) {
3522             if (sp->first == node) {
3523                 g_assert(sp->last == node);
3524                 sp->first = node->n.other;
3525                 sp->last = sp->first;
3526             }
3527             node->p.other->n.other = node->n.other;
3528             node->n.other->p.other = node->p.other;
3529         } else {
3530             if (sp->first == node) {
3531                 sp->first = node->n.other;
3532                 sp->first->code = NR_MOVETO;
3533             }
3534             if (sp->last == node) sp->last = node->p.other;
3535             if (node->p.other) node->p.other->n.other = node->n.other;
3536             if (node->n.other) node->n.other->p.other = node->p.other;
3537         }
3538     } else { // this was the last node on subpath
3539         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
3540     }
3542     g_mem_chunk_free(nodechunk, node);
3545 /**
3546  * Returns one of the node's two sides.
3547  * \param which Indicates which side.
3548  * \return Pointer to previous node side if which==-1, next if which==1.
3549  */
3550 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
3552     g_assert(node);
3554     switch (which) {
3555         case -1:
3556             return &node->p;
3557         case 1:
3558             return &node->n;
3559         default:
3560             break;
3561     }
3563     g_assert_not_reached();
3565     return NULL;
3568 /**
3569  * Return the other side of the node, given one of its sides.
3570  */
3571 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
3573     g_assert(node);
3575     if (me == &node->p) return &node->n;
3576     if (me == &node->n) return &node->p;
3578     g_assert_not_reached();
3580     return NULL;
3583 /**
3584  * Return NRPathcode on the given side of the node.
3585  */
3586 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
3588     g_assert(node);
3590     if (me == &node->p) {
3591         if (node->p.other) return (NRPathcode)node->code;
3592         return NR_MOVETO;
3593     }
3595     if (me == &node->n) {
3596         if (node->n.other) return (NRPathcode)node->n.other->code;
3597         return NR_MOVETO;
3598     }
3600     g_assert_not_reached();
3602     return NR_END;
3605 /**
3606  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
3607  */
3608 Inkscape::NodePath::Node *
3609 sp_nodepath_get_node_by_index(int index)
3611     Inkscape::NodePath::Node *e = NULL;
3613     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
3614     if (!nodepath) {
3615         return e;
3616     }
3618     //find segment
3619     for (GList *l = nodepath->subpaths; l ; l=l->next) {
3621         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
3622         int n = g_list_length(sp->nodes);
3623         if (sp->closed) {
3624             n++;
3625         }
3627         //if the piece belongs to this subpath grab it
3628         //otherwise move onto the next subpath
3629         if (index < n) {
3630             e = sp->first;
3631             for (int i = 0; i < index; ++i) {
3632                 e = e->n.other;
3633             }
3634             break;
3635         } else {
3636             if (sp->closed) {
3637                 index -= (n+1);
3638             } else {
3639                 index -= n;
3640             }
3641         }
3642     }
3644     return e;
3647 /**
3648  * Returns plain text meaning of node type.
3649  */
3650 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
3652     unsigned retracted = 0;
3653     bool endnode = false;
3655     for (int which = -1; which <= 1; which += 2) {
3656         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
3657         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
3658             retracted ++;
3659         if (!side->other)
3660             endnode = true;
3661     }
3663     if (retracted == 0) {
3664         if (endnode) {
3665                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
3666                 return _("end node");
3667         } else {
3668             switch (node->type) {
3669                 case Inkscape::NodePath::NODE_CUSP:
3670                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
3671                     return _("cusp");
3672                 case Inkscape::NodePath::NODE_SMOOTH:
3673                     // TRANSLATORS: "smooth" is an adjective here
3674                     return _("smooth");
3675                 case Inkscape::NodePath::NODE_SYMM:
3676                     return _("symmetric");
3677             }
3678         }
3679     } else if (retracted == 1) {
3680         if (endnode) {
3681             // TRANSLATORS: "end" is an adjective here (NOT a verb)
3682             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
3683         } else {
3684             return _("one handle retracted (drag with <b>Shift</b> to extend)");
3685         }
3686     } else {
3687         return _("both handles retracted (drag with <b>Shift</b> to extend)");
3688     }
3690     return NULL;
3693 /**
3694  * Handles content of statusbar as long as node tool is active.
3695  */
3696 void
3697 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
3699     gchar const *when_selected = _("<b>Drag</b> nodes or node handles; <b>arrow</b> keys to move nodes");
3700     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
3702     gint total = 0;
3703     gint selected = 0;
3704     SPDesktop *desktop = NULL;
3706     if (nodepath) {
3707         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3708             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3709             total += g_list_length(subpath->nodes);
3710         }
3711         selected = g_list_length(nodepath->selected);
3712         desktop = nodepath->desktop;
3713     } else {
3714         desktop = SP_ACTIVE_DESKTOP;
3715     }
3717     SPEventContext *ec = desktop->event_context;
3718     if (!ec) return;
3719     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3720     if (!mc) return;
3722     if (selected == 0) {
3723         Inkscape::Selection *sel = desktop->selection;
3724         if (!sel || sel->isEmpty()) {
3725             mc->setF(Inkscape::NORMAL_MESSAGE,
3726                      _("Select a single object to edit its nodes or handles."));
3727         } else {
3728             if (nodepath) {
3729             mc->setF(Inkscape::NORMAL_MESSAGE,
3730                      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.",
3731                               "<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.",
3732                               total),
3733                      total);
3734             } else {
3735                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
3736                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
3737                 } else {
3738                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
3739                 }
3740             }
3741         }
3742     } else if (nodepath && selected == 1) {
3743         mc->setF(Inkscape::NORMAL_MESSAGE,
3744                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
3745                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
3746                           total),
3747                  selected, total, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
3748     } else {
3749         mc->setF(Inkscape::NORMAL_MESSAGE,
3750                  ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
3751                           "<b>%i</b> of <b>%i</b> nodes selected. %s.",
3752                           total),
3753                  selected, total, when_selected);
3754     }
3758 /*
3759   Local Variables:
3760   mode:c++
3761   c-file-style:"stroustrup"
3762   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
3763   indent-tabs-mode:nil
3764   fill-column:99
3765   End:
3766 */
3767 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :