Code

Various snapping cleanups and bug fixes.
[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_desktop_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_desktop_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_desktop_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_MOUSEOVER(node->p.knot)) {
840         sp_node_adjust_handle(node, 1);
841     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(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_desktop_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;
1456     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1457         c = a->pos;
1458     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1459         c = b->pos;
1460     } else {
1461         c = (a->pos + b->pos) / 2;
1462     }
1464     if (a->subpath == b->subpath) {
1465        Inkscape::NodePath::SubPath *sp = a->subpath;
1466         sp_nodepath_subpath_close(sp);
1467         sp_node_moveto (sp->first, c);
1469         sp_nodepath_update_handles(sp->nodepath);
1470         sp_nodepath_update_repr(nodepath);
1471         return;
1472     }
1474     /* a and b are separate subpaths */
1475    Inkscape::NodePath::SubPath *sa = a->subpath;
1476    Inkscape::NodePath::SubPath *sb = b->subpath;
1477     NR::Point p;
1478    Inkscape::NodePath::Node *n;
1479     NRPathcode code;
1480     if (a == sa->first) {
1481         p = sa->first->n.pos;
1482         code = (NRPathcode)sa->first->n.other->code;
1483        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1484         n = sa->last;
1485         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1486         n = n->p.other;
1487         while (n) {
1488             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1489             n = n->p.other;
1490             if (n == sa->first) n = NULL;
1491         }
1492         sp_nodepath_subpath_destroy(sa);
1493         sa = t;
1494     } else if (a == sa->last) {
1495         p = sa->last->p.pos;
1496         code = (NRPathcode)sa->last->code;
1497         sp_nodepath_node_destroy(sa->last);
1498     } else {
1499         code = NR_END;
1500         g_assert_not_reached();
1501     }
1503     if (b == sb->first) {
1504         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1505         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1506             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1507         }
1508     } else if (b == sb->last) {
1509         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1510         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1511             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1512         }
1513     } else {
1514         g_assert_not_reached();
1515     }
1516     /* and now destroy sb */
1518     sp_nodepath_subpath_destroy(sb);
1520     sp_nodepath_update_handles(sa->nodepath);
1522     sp_nodepath_update_repr(nodepath);
1524     sp_nodepath_update_statusbar(nodepath);
1527 /**
1528  *  Join two nodes by adding a segment between them.
1529  */
1530 void sp_node_selected_join_segment()
1532     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1533     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1535     if (g_list_length(nodepath->selected) != 2) {
1536         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1537         return;
1538     }
1540    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1541    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1543     g_assert(a != b);
1544     g_assert(a->p.other || a->n.other);
1545     g_assert(b->p.other || b->n.other);
1547     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1548         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1549         return;
1550     }
1552     if (a->subpath == b->subpath) {
1553        Inkscape::NodePath::SubPath *sp = a->subpath;
1555         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1556         sp->closed = TRUE;
1558         sp->first->p.other = sp->last;
1559         sp->last->n.other  = sp->first;
1561         sp_node_handle_mirror_p_to_n(sp->last);
1562         sp_node_handle_mirror_n_to_p(sp->first);
1564         sp->first->code = sp->last->code;
1565         sp->first       = sp->last;
1567         sp_nodepath_update_handles(sp->nodepath);
1569         sp_nodepath_update_repr(nodepath);
1571         return;
1572     }
1574     /* a and b are separate subpaths */
1575    Inkscape::NodePath::SubPath *sa = a->subpath;
1576    Inkscape::NodePath::SubPath *sb = b->subpath;
1578    Inkscape::NodePath::Node *n;
1579     NR::Point p;
1580     NRPathcode code;
1581     if (a == sa->first) {
1582         code = (NRPathcode) sa->first->n.other->code;
1583        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1584         n = sa->last;
1585         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1586         for (n = n->p.other; n != NULL; n = n->p.other) {
1587             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1588         }
1589         sp_nodepath_subpath_destroy(sa);
1590         sa = t;
1591     } else if (a == sa->last) {
1592         code = (NRPathcode)sa->last->code;
1593     } else {
1594         code = NR_END;
1595         g_assert_not_reached();
1596     }
1598     if (b == sb->first) {
1599         n = sb->first;
1600         sp_node_handle_mirror_p_to_n(sa->last);
1601         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1602         sp_node_handle_mirror_n_to_p(sa->last);
1603         for (n = n->n.other; n != NULL; n = n->n.other) {
1604             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1605         }
1606     } else if (b == sb->last) {
1607         n = sb->last;
1608         sp_node_handle_mirror_p_to_n(sa->last);
1609         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1610         sp_node_handle_mirror_n_to_p(sa->last);
1611         for (n = n->p.other; n != NULL; n = n->p.other) {
1612             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1613         }
1614     } else {
1615         g_assert_not_reached();
1616     }
1617     /* and now destroy sb */
1619     sp_nodepath_subpath_destroy(sb);
1621     sp_nodepath_update_handles(sa->nodepath);
1623     sp_nodepath_update_repr(nodepath);
1626 /**
1627  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1628  */
1629 void sp_node_delete_preserve(GList *nodes_to_delete)
1631     
1632     while (nodes_to_delete) {
1633         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1634         Inkscape::NodePath::SubPath *sp = node->subpath;
1635         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1636         Inkscape::NodePath::Node *sample_cursor = NULL;
1637         Inkscape::NodePath::Node *sample_end = NULL;
1638         Inkscape::NodePath::Node *delete_cursor = node;
1639         bool just_delete = false;
1640         
1641         //find the start of this contiguous selection
1642         //move left to the first node that is not selected
1643         //or the start of the non-closed path
1644         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1645             delete_cursor = curr;
1646         }
1648         //just delete at the beginning of an open path
1649         if (!delete_cursor->p.other) {
1650             sample_cursor = delete_cursor;
1651             just_delete = true;
1652         } else {
1653             sample_cursor = delete_cursor->p.other;
1654         }
1655         
1656         //calculate points for each segment
1657         int rate = 5;
1658         float period = 1.0 / rate;
1659         std::vector<NR::Point> data;
1660         if (!just_delete) {
1661             data.push_back(sample_cursor->pos);
1662             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1663                 //just delete at the end of an open path
1664                 if (!sp->closed && curr->n.other == sp->last) {
1665                     just_delete = true;
1666                     break;
1667                 }
1668                 
1669                 //sample points on the contiguous selected segment
1670                 NR::Point *bez;
1671                 bez = new NR::Point [4];
1672                 bez[0] = curr->pos;
1673                 bez[1] = curr->n.pos;
1674                 bez[2] = curr->n.other->p.pos;
1675                 bez[3] = curr->n.other->pos;
1676                 for (int i=1; i<rate; i++) {
1677                     gdouble t = i * period;
1678                     NR::Point p = bezier_pt(3, bez, t);
1679                     data.push_back(p);
1680                 }
1681                 data.push_back(curr->n.other->pos);
1683                 sample_end = curr->n.other;
1684                 //break if we've come full circle or hit the end of the selection
1685                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
1686                     break;
1687                 }
1688             }
1689         }
1691         if (!just_delete) {
1692             //calculate the best fitting single segment and adjust the endpoints
1693             NR::Point *adata;
1694             adata = new NR::Point [data.size()];
1695             copy(data.begin(), data.end(), adata);
1696             
1697             NR::Point *bez;
1698             bez = new NR::Point [4];
1699             //would decreasing error create a better fitting approximation?
1700             gdouble error = 1.0;
1701             gint ret;
1702             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
1704             //adjust endpoints
1705             sample_cursor->n.pos = bez[1];
1706             sample_end->p.pos = bez[2];
1707         }
1708        
1709         //destroy this contiguous selection
1710         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
1711             Inkscape::NodePath::Node *temp = delete_cursor;
1712             if (delete_cursor->n.other == delete_cursor) {
1713                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
1714                 delete_cursor = NULL; 
1715             } else {
1716                 delete_cursor = delete_cursor->n.other;
1717             }
1718             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
1719             sp_nodepath_node_destroy(temp);
1720         }
1722         //clean up the nodepath (such as for trivial subpaths)
1723         sp_nodepath_cleanup(nodepath);
1725         sp_nodepath_update_handles(nodepath);
1727         // if the entire nodepath is removed, delete the selected object.
1728         if (nodepath->subpaths == NULL ||
1729             sp_nodepath_get_node_count(nodepath) < 2) {
1730             SPDocument *document = sp_desktop_document (nodepath->desktop);
1731             sp_nodepath_destroy(nodepath);
1732             g_list_free(nodes_to_delete);
1733             nodes_to_delete = NULL;
1734             //is the next line necessary?
1735             sp_selection_delete();
1736             sp_document_done (document);
1737             return;
1738         }
1740         sp_nodepath_update_repr(nodepath);
1742         sp_nodepath_update_statusbar(nodepath);
1743     }
1746 /**
1747  * Delete one or more selected nodes.
1748  */
1749 void sp_node_selected_delete()
1751     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1752     if (!nodepath) return;
1753     if (!nodepath->selected) return;
1755     /** \todo fixme: do it the right way */
1756     while (nodepath->selected) {
1757        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
1758         sp_nodepath_node_destroy(node);
1759     }
1762     //clean up the nodepath (such as for trivial subpaths)
1763     sp_nodepath_cleanup(nodepath);
1765     sp_nodepath_update_handles(nodepath);
1767     // if the entire nodepath is removed, delete the selected object.
1768     if (nodepath->subpaths == NULL ||
1769         sp_nodepath_get_node_count(nodepath) < 2) {
1770         SPDocument *document = sp_desktop_document (nodepath->desktop);
1771         sp_nodepath_destroy(nodepath);
1772         sp_selection_delete();
1773         sp_document_done (document);
1774         return;
1775     }
1777     sp_nodepath_update_repr(nodepath);
1779     sp_nodepath_update_statusbar(nodepath);
1782 /**
1783  * Delete one or more segments between two selected nodes.
1784  * This is the code for 'split'.
1785  */
1786 void
1787 sp_node_selected_delete_segment(void)
1789    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
1790    Inkscape::NodePath::Node *curr, *next;     //Iterators
1792     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1793     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1795     if (g_list_length(nodepath->selected) != 2) {
1796         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1797                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1798         return;
1799     }
1801     //Selected nodes, not inclusive
1802    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1803    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1805     if ( ( a==b)                       ||  //same node
1806          (a->subpath  != b->subpath )  ||  //not the same path
1807          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
1808          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
1809     {
1810         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1811                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1812         return;
1813     }
1815     //###########################################
1816     //# BEGIN EDITS
1817     //###########################################
1818     //##################################
1819     //# CLOSED PATH
1820     //##################################
1821     if (a->subpath->closed) {
1824         gboolean reversed = FALSE;
1826         //Since we can go in a circle, we need to find the shorter distance.
1827         //  a->b or b->a
1828         start = end = NULL;
1829         int distance    = 0;
1830         int minDistance = 0;
1831         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
1832             if (curr==b) {
1833                 //printf("a to b:%d\n", distance);
1834                 start = a;//go from a to b
1835                 end   = b;
1836                 minDistance = distance;
1837                 //printf("A to B :\n");
1838                 break;
1839             }
1840             distance++;
1841         }
1843         //try again, the other direction
1844         distance = 0;
1845         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
1846             if (curr==a) {
1847                 //printf("b to a:%d\n", distance);
1848                 if (distance < minDistance) {
1849                     start    = b;  //we go from b to a
1850                     end      = a;
1851                     reversed = TRUE;
1852                     //printf("B to A\n");
1853                 }
1854                 break;
1855             }
1856             distance++;
1857         }
1860         //Copy everything from 'end' to 'start' to a new subpath
1861        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
1862         for (curr=end ; curr ; curr=curr->n.other) {
1863             NRPathcode code = (NRPathcode) curr->code;
1864             if (curr == end)
1865                 code = NR_MOVETO;
1866             sp_nodepath_node_new(t, NULL,
1867                                  (Inkscape::NodePath::NodeType)curr->type, code,
1868                                  &curr->p.pos, &curr->pos, &curr->n.pos);
1869             if (curr == start)
1870                 break;
1871         }
1872         sp_nodepath_subpath_destroy(a->subpath);
1875     }
1879     //##################################
1880     //# OPEN PATH
1881     //##################################
1882     else {
1884         //We need to get the direction of the list between A and B
1885         //Can we walk from a to b?
1886         start = end = NULL;
1887         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
1888             if (curr==b) {
1889                 start = a;  //did it!  we go from a to b
1890                 end   = b;
1891                 //printf("A to B\n");
1892                 break;
1893             }
1894         }
1895         if (!start) {//didn't work?  let's try the other direction
1896             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
1897                 if (curr==a) {
1898                     start = b;  //did it!  we go from b to a
1899                     end   = a;
1900                     //printf("B to A\n");
1901                     break;
1902                 }
1903             }
1904         }
1905         if (!start) {
1906             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1907                                                      _("Cannot find path between nodes."));
1908             return;
1909         }
1913         //Copy everything after 'end' to a new subpath
1914        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
1915         for (curr=end ; curr ; curr=curr->n.other) {
1916             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
1917                                  &curr->p.pos, &curr->pos, &curr->n.pos);
1918         }
1920         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
1921         for (curr = start->n.other ; curr  ; curr=next) {
1922             next = curr->n.other;
1923             sp_nodepath_node_destroy(curr);
1924         }
1926     }
1927     //###########################################
1928     //# END EDITS
1929     //###########################################
1931     //clean up the nodepath (such as for trivial subpaths)
1932     sp_nodepath_cleanup(nodepath);
1934     sp_nodepath_update_handles(nodepath);
1936     sp_nodepath_update_repr(nodepath);
1938     // if the entire nodepath is removed, delete the selected object.
1939     if (nodepath->subpaths == NULL ||
1940         sp_nodepath_get_node_count(nodepath) < 2) {
1941         sp_nodepath_destroy(nodepath);
1942         sp_selection_delete();
1943         return;
1944     }
1946     sp_nodepath_update_statusbar(nodepath);
1949 /**
1950  * Call sp_nodepath_set_line() for all selected segments.
1951  */
1952 void
1953 sp_node_selected_set_line_type(NRPathcode code)
1955     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1956     if (nodepath == NULL) return;
1958     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1959        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1960         g_assert(n->selected);
1961         if (n->p.other && n->p.other->selected) {
1962             sp_nodepath_set_line_type(n, code);
1963         }
1964     }
1966     sp_nodepath_update_repr(nodepath);
1969 /**
1970  * Call sp_nodepath_convert_node_type() for all selected nodes.
1971  */
1972 void
1973 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
1975     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1976     if (nodepath == NULL) return;
1978     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1979         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
1980     }
1982     sp_nodepath_update_repr(nodepath);
1985 /**
1986  * Change select status of node, update its own and neighbour handles.
1987  */
1988 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
1990     node->selected = selected;
1992     if (selected) {
1993         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
1994         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
1995         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
1996         sp_knot_update_ctrl(node->knot);
1997     } else {
1998         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
1999         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2000         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2001         sp_knot_update_ctrl(node->knot);
2002     }
2004     sp_node_update_handles(node);
2005     if (node->n.other) sp_node_update_handles(node->n.other);
2006     if (node->p.other) sp_node_update_handles(node->p.other);
2009 /**
2010 \brief Select a node
2011 \param node     The node to select
2012 \param incremental   If true, add to selection, otherwise deselect others
2013 \param override   If true, always select this node, otherwise toggle selected status
2014 */
2015 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2017     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2019     if (incremental) {
2020         if (override) {
2021             if (!g_list_find(nodepath->selected, node)) {
2022                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2023             }
2024             sp_node_set_selected(node, TRUE);
2025         } else { // toggle
2026             if (node->selected) {
2027                 g_assert(g_list_find(nodepath->selected, node));
2028                 nodepath->selected = g_list_remove(nodepath->selected, node);
2029             } else {
2030                 g_assert(!g_list_find(nodepath->selected, node));
2031                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2032             }
2033             sp_node_set_selected(node, !node->selected);
2034         }
2035     } else {
2036         sp_nodepath_deselect(nodepath);
2037         nodepath->selected = g_list_prepend(nodepath->selected, node);
2038         sp_node_set_selected(node, TRUE);
2039     }
2041     sp_nodepath_update_statusbar(nodepath);
2045 /**
2046 \brief Deselect all nodes in the nodepath
2047 */
2048 void
2049 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2051     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2053     while (nodepath->selected) {
2054         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2055         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2056     }
2057     sp_nodepath_update_statusbar(nodepath);
2060 /**
2061 \brief Select or invert selection of all nodes in the nodepath
2062 */
2063 void
2064 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2066     if (!nodepath) return;
2068     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2069        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2070         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2071            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2072            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2073         }
2074     }
2077 /**
2078  * If nothing selected, does the same as sp_nodepath_select_all();
2079  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2080  * (i.e., similar to "select all in layer", with the "selected" subpaths
2081  * being treated as "layers" in the path).
2082  */
2083 void
2084 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2086     if (!nodepath) return;
2088     if (g_list_length (nodepath->selected) == 0) {
2089         sp_nodepath_select_all (nodepath, invert);
2090         return;
2091     }
2093     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2094     GSList *subpaths = NULL;
2096     for (GList *l = copy; l != NULL; l = l->next) {
2097         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2098         Inkscape::NodePath::SubPath *subpath = n->subpath;
2099         if (!g_slist_find (subpaths, subpath))
2100             subpaths = g_slist_prepend (subpaths, subpath);
2101     }
2103     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2104         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2105         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2106             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2107             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2108         }
2109     }
2111     g_slist_free (subpaths);
2112     g_list_free (copy);
2115 /**
2116  * \brief Select the node after the last selected; if none is selected,
2117  * select the first within path.
2118  */
2119 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2121     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2123    Inkscape::NodePath::Node *last = NULL;
2124     if (nodepath->selected) {
2125         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2126            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2127             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2128             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2129                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2130                 if (node->selected) {
2131                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2132                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2133                             if (spl->next) { // there's a next subpath
2134                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2135                                 last = subpath_next->first;
2136                             } else if (spl->prev) { // there's a previous subpath
2137                                 last = NULL; // to be set later to the first node of first subpath
2138                             } else {
2139                                 last = node->n.other;
2140                             }
2141                         } else {
2142                             last = node->n.other;
2143                         }
2144                     } else {
2145                         if (node->n.other) {
2146                             last = node->n.other;
2147                         } else {
2148                             if (spl->next) { // there's a next subpath
2149                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2150                                 last = subpath_next->first;
2151                             } else if (spl->prev) { // there's a previous subpath
2152                                 last = NULL; // to be set later to the first node of first subpath
2153                             } else {
2154                                 last = (Inkscape::NodePath::Node *) subpath->first;
2155                             }
2156                         }
2157                     }
2158                 }
2159             }
2160         }
2161         sp_nodepath_deselect(nodepath);
2162     }
2164     if (last) { // there's at least one more node after selected
2165         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2166     } else { // no more nodes, select the first one in first subpath
2167        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2168         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2169     }
2172 /**
2173  * \brief Select the node before the first selected; if none is selected,
2174  * select the last within path
2175  */
2176 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2178     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2180    Inkscape::NodePath::Node *last = NULL;
2181     if (nodepath->selected) {
2182         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2183            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2184             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2185                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2186                 if (node->selected) {
2187                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2188                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2189                             if (spl->prev) { // there's a prev subpath
2190                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2191                                 last = subpath_prev->last;
2192                             } else if (spl->next) { // there's a next subpath
2193                                 last = NULL; // to be set later to the last node of last subpath
2194                             } else {
2195                                 last = node->p.other;
2196                             }
2197                         } else {
2198                             last = node->p.other;
2199                         }
2200                     } else {
2201                         if (node->p.other) {
2202                             last = node->p.other;
2203                         } else {
2204                             if (spl->prev) { // there's a prev subpath
2205                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2206                                 last = subpath_prev->last;
2207                             } else if (spl->next) { // there's a next subpath
2208                                 last = NULL; // to be set later to the last node of last subpath
2209                             } else {
2210                                 last = (Inkscape::NodePath::Node *) subpath->last;
2211                             }
2212                         }
2213                     }
2214                 }
2215             }
2216         }
2217         sp_nodepath_deselect(nodepath);
2218     }
2220     if (last) { // there's at least one more node before selected
2221         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2222     } else { // no more nodes, select the last one in last subpath
2223         GList *spl = g_list_last(nodepath->subpaths);
2224        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2225         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2226     }
2229 /**
2230  * \brief Select all nodes that are within the rectangle.
2231  */
2232 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2234     if (!incremental) {
2235         sp_nodepath_deselect(nodepath);
2236     }
2238     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2239        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2240         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2241            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2243             if (b.contains(node->pos)) {
2244                 sp_nodepath_node_select(node, TRUE, TRUE);
2245             }
2246         }
2247     }
2250 /**
2251 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2252 */
2253 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2255     if (!nodepath->selected) {
2256         return NULL;
2257     }
2259     GList *r = NULL;
2260     guint i = 0;
2261     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2262        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2263         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2264            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2265             i++;
2266             if (node->selected) {
2267                 r = g_list_append(r, GINT_TO_POINTER(i));
2268             }
2269         }
2270     }
2271     return r;
2274 /**
2275 \brief  Restores selection by selecting nodes whose positions are in the list
2276 */
2277 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2279     sp_nodepath_deselect(nodepath);
2281     guint i = 0;
2282     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2283        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2284         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2285            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2286             i++;
2287             if (g_list_find(r, GINT_TO_POINTER(i))) {
2288                 sp_nodepath_node_select(node, TRUE, TRUE);
2289             }
2290         }
2291     }
2295 /**
2296 \brief Adjusts handle according to node type and line code.
2297 */
2298 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2300     double len, otherlen, linelen;
2302     g_assert(node);
2304    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2305    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2307     /** \todo fixme: */
2308     if (me->other == NULL) return;
2309     if (other->other == NULL) return;
2311     /* I have line */
2313     NRPathcode mecode, ocode;
2314     if (which_adjust == 1) {
2315         mecode = (NRPathcode)me->other->code;
2316         ocode = (NRPathcode)node->code;
2317     } else {
2318         mecode = (NRPathcode)node->code;
2319         ocode = (NRPathcode)other->other->code;
2320     }
2322     if (mecode == NR_LINETO) return;
2324     /* I am curve */
2326     if (other->other == NULL) return;
2328     /* Other has line */
2330     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2332     NR::Point delta;
2333     if (ocode == NR_LINETO) {
2334         /* other is lineto, we are either smooth or symm */
2335        Inkscape::NodePath::Node *othernode = other->other;
2336         len = NR::L2(me->pos - node->pos);
2337         delta = node->pos - othernode->pos;
2338         linelen = NR::L2(delta);
2339         if (linelen < 1e-18) 
2340             return;
2341         me->pos = node->pos + (len / linelen)*delta;
2342         return;
2343     }
2345     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2347         me->pos = 2 * node->pos - other->pos;
2348         return;
2349     }
2351     /* We are smooth */
2353     len = NR::L2(me->pos - node->pos);
2354     delta = other->pos - node->pos;
2355     otherlen = NR::L2(delta);
2356     if (otherlen < 1e-18) return;
2358     me->pos = node->pos - (len / otherlen) * delta;
2361 /**
2362  \brief Adjusts both handles according to node type and line code
2363  */
2364 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2366     g_assert(node);
2368     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2370     /* we are either smooth or symm */
2372     if (node->p.other == NULL) return;
2374     if (node->n.other == NULL) return;
2376     if (node->code == NR_LINETO) {
2377         if (node->n.other->code == NR_LINETO) return;
2378         sp_node_adjust_handle(node, 1);
2379         return;
2380     }
2382     if (node->n.other->code == NR_LINETO) {
2383         if (node->code == NR_LINETO) return;
2384         sp_node_adjust_handle(node, -1);
2385         return;
2386     }
2388     /* both are curves */
2389     NR::Point const delta( node->n.pos - node->p.pos );
2391     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2392         node->p.pos = node->pos - delta / 2;
2393         node->n.pos = node->pos + delta / 2;
2394         return;
2395     }
2397     /* We are smooth */
2398     double plen = NR::L2(node->p.pos - node->pos);
2399     if (plen < 1e-18) return;
2400     double nlen = NR::L2(node->n.pos - node->pos);
2401     if (nlen < 1e-18) return;
2402     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2403     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2406 /**
2407  * Node event callback.
2408  */
2409 static gboolean node_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2411     gboolean ret = FALSE;
2412     switch (event->type) {
2413         case GDK_ENTER_NOTIFY:
2414             active_node = n;
2415             break;
2416         case GDK_LEAVE_NOTIFY:
2417             active_node = NULL;
2418             break;
2419         case GDK_KEY_PRESS:
2420             switch (get_group0_keyval (&event->key)) {
2421                 case GDK_space:
2422                     if (event->key.state & GDK_BUTTON1_MASK) {
2423                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2424                         stamp_repr(nodepath);
2425                         ret = TRUE;
2426                     }
2427                     break;
2428                 default:
2429                     break;
2430             }
2431             break;
2432         default:
2433             break;
2434     }
2436     return ret;
2439 /**
2440  * Handle keypress on node; directly called.
2441  */
2442 gboolean node_key(GdkEvent *event)
2444     Inkscape::NodePath::Path *np;
2446     // there is no way to verify nodes so set active_node to nil when deleting!!
2447     if (active_node == NULL) return FALSE;
2449     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2450         gint ret = FALSE;
2451         switch (get_group0_keyval (&event->key)) {
2452             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2453             case GDK_BackSpace:
2454                 np = active_node->subpath->nodepath;
2455                 sp_nodepath_node_destroy(active_node);
2456                 sp_nodepath_update_repr(np);
2457                 active_node = NULL;
2458                 ret = TRUE;
2459                 break;
2460             case GDK_c:
2461                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2462                 ret = TRUE;
2463                 break;
2464             case GDK_s:
2465                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2466                 ret = TRUE;
2467                 break;
2468             case GDK_y:
2469                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2470                 ret = TRUE;
2471                 break;
2472             case GDK_b:
2473                 sp_nodepath_node_break(active_node);
2474                 ret = TRUE;
2475                 break;
2476         }
2477         return ret;
2478     }
2479     return FALSE;
2482 /**
2483  * Mouseclick on node callback.
2484  */
2485 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2487    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2489     if (state & GDK_CONTROL_MASK) {
2490         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2492         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2493             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2494                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2495             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2496                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2497             } else {
2498                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2499             }
2500             sp_nodepath_update_repr(nodepath);
2501             sp_nodepath_update_statusbar(nodepath);
2503         } else { //ctrl+alt+click: delete node
2504             GList *node_to_delete = NULL;
2505             node_to_delete = g_list_append(node_to_delete, n);
2506             sp_node_delete_preserve(node_to_delete);
2507         }
2509     } else {
2510         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2511     }
2514 /**
2515  * Mouse grabbed node callback.
2516  */
2517 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2519    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2521     n->origin = knot->pos;
2523     if (!n->selected) {
2524         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2525     }
2528 /**
2529  * Mouse ungrabbed node callback.
2530  */
2531 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
2533    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2535    n->dragging_out = NULL;
2537    sp_nodepath_update_repr(n->subpath->nodepath);
2540 /**
2541  * The point on a line, given by its angle, closest to the given point.
2542  * \param p  A point.
2543  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2544  * \param closest  Pointer to the point struct where the result is stored.
2545  * \todo FIXME: use dot product perhaps?
2546  */
2547 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
2549     if (a == HUGE_VAL) { // vertical
2550         *closest = NR::Point(0, (*p)[NR::Y]);
2551     } else {
2552         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
2553         (*closest)[NR::Y] = a * (*closest)[NR::X];
2554     }
2557 /**
2558  * Distance from the point to a line given by its angle.
2559  * \param p  A point.
2560  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2561  */
2562 static double point_line_distance(NR::Point *p, double a)
2564     NR::Point c;
2565     point_line_closest(p, a, &c);
2566     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]));
2569 /**
2570  * Callback for node "request" signal.
2571  * \todo fixme: This goes to "moved" event? (lauris)
2572  */
2573 static gboolean
2574 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2576     double yn, xn, yp, xp;
2577     double an, ap, na, pa;
2578     double d_an, d_ap, d_na, d_pa;
2579     gboolean collinear = FALSE;
2580     NR::Point c;
2581     NR::Point pr;
2583    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2585    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
2586    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
2588        NR::Point mouse = (*p);
2590        if (!n->dragging_out) {
2591            // This is the first drag-out event; find out which handle to drag out
2592            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
2593            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
2595            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
2596                return FALSE;
2598            Inkscape::NodePath::NodeSide *opposite;
2599            if (appr_p > appr_n) { // closer to p
2600                n->dragging_out = &n->p;
2601                opposite = &n->n;
2602                n->code = NR_CURVETO;
2603            } else if (appr_p < appr_n) { // closer to n
2604                n->dragging_out = &n->n;
2605                opposite = &n->p;
2606                n->n.other->code = NR_CURVETO;
2607            } else { // p and n nodes are the same
2608                if (n->n.pos != n->pos) { // n handle already dragged, drag p
2609                    n->dragging_out = &n->p;
2610                    opposite = &n->n;
2611                    n->code = NR_CURVETO;
2612                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
2613                    n->dragging_out = &n->n;
2614                    opposite = &n->p;
2615                    n->n.other->code = NR_CURVETO;
2616                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
2617                    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);
2618                    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);
2619                    if (appr_other_p > appr_other_n) { // closer to other's p handle
2620                        n->dragging_out = &n->n;
2621                        opposite = &n->p;
2622                        n->n.other->code = NR_CURVETO;
2623                    } else { // closer to other's n handle
2624                        n->dragging_out = &n->p;
2625                        opposite = &n->n;
2626                        n->code = NR_CURVETO;
2627                    }
2628                }
2629            }
2631            // if there's another handle, make sure the one we drag out starts parallel to it
2632            if (opposite->pos != n->pos) {
2633                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
2634            }
2636            // knots might not be created yet!
2637            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
2638            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
2639        }
2641        // pass this on to the handle-moved callback
2642        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
2643        sp_node_update_handles(n);
2644        return TRUE;
2645    }
2647     if (state & GDK_CONTROL_MASK) { // constrained motion
2649         // calculate relative distances of handles
2650         // n handle:
2651         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
2652         xn = n->n.pos[NR::X] - n->pos[NR::X];
2653         // if there's no n handle (straight line), see if we can use the direction to the next point on path
2654         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
2655             if (n->n.other) { // if there is the next point
2656                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
2657                     yn = n->n.other->pos[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
2658                     xn = n->n.other->pos[NR::X] - n->origin[NR::X];
2659             }
2660         }
2661         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
2662         if (yn < 0) { xn = -xn; yn = -yn; }
2664         // p handle:
2665         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
2666         xp = n->p.pos[NR::X] - n->pos[NR::X];
2667         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
2668         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
2669             if (n->p.other) {
2670                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
2671                     yp = n->p.other->pos[NR::Y] - n->origin[NR::Y];
2672                     xp = n->p.other->pos[NR::X] - n->origin[NR::X];
2673             }
2674         }
2675         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
2676         if (yp < 0) { xp = -xp; yp = -yp; }
2678         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
2679             // sliding on handles, only if at least one of the handles is non-vertical
2680             // (otherwise it's the same as ctrl+drag anyway)
2682             // calculate angles of the handles
2683             if (xn == 0) {
2684                 if (yn == 0) { // no handle, consider it the continuation of the other one
2685                     an = 0;
2686                     collinear = TRUE;
2687                 }
2688                 else an = 0; // vertical; set the angle to horizontal
2689             } else an = yn/xn;
2691             if (xp == 0) {
2692                 if (yp == 0) { // no handle, consider it the continuation of the other one
2693                     ap = an;
2694                 }
2695                 else ap = 0; // vertical; set the angle to horizontal
2696             } else  ap = yp/xp;
2698             if (collinear) an = ap;
2700             // angles of the perpendiculars; HUGE_VAL means vertical
2701             if (an == 0) na = HUGE_VAL; else na = -1/an;
2702             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
2704             //g_print("an %g    ap %g\n", an, ap);
2706             // mouse point relative to the node's original pos
2707             pr = (*p) - n->origin;
2709             // distances to the four lines (two handles and two perpendiculars)
2710             d_an = point_line_distance(&pr, an);
2711             d_na = point_line_distance(&pr, na);
2712             d_ap = point_line_distance(&pr, ap);
2713             d_pa = point_line_distance(&pr, pa);
2715             // find out which line is the closest, save its closest point in c
2716             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
2717                 point_line_closest(&pr, an, &c);
2718             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
2719                 point_line_closest(&pr, ap, &c);
2720             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
2721                 point_line_closest(&pr, na, &c);
2722             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
2723                 point_line_closest(&pr, pa, &c);
2724             }
2726             // move the node to the closest point
2727             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2728                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
2729                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
2731         } else {  // constraining to hor/vert
2733             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
2734                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
2735             } else { // snap to vert
2736                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
2737             }
2738         }
2739     } else { // move freely
2740         sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2741                                         (*p)[NR::X] - n->pos[NR::X],
2742                                         (*p)[NR::Y] - n->pos[NR::Y],
2743                                         (state & GDK_SHIFT_MASK) == 0);
2744     }
2746     n->subpath->nodepath->desktop->scroll_to_point(p);
2748     return TRUE;
2751 /**
2752  * Node handle clicked callback.
2753  */
2754 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
2756    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2758     if (state & GDK_CONTROL_MASK) { // "delete" handle
2759         if (n->p.knot == knot) {
2760             n->p.pos = n->pos;
2761         } else if (n->n.knot == knot) {
2762             n->n.pos = n->pos;
2763         }
2764         sp_node_update_handles(n);
2765         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2766         sp_nodepath_update_repr(nodepath);
2767         sp_nodepath_update_statusbar(nodepath);
2769     } else { // just select or add to selection, depending in Shift
2770         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2771     }
2774 /**
2775  * Node handle grabbed callback.
2776  */
2777 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
2779    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2781     if (!n->selected) {
2782         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2783     }
2785     // remember the origin point of the handle
2786     if (n->p.knot == knot) {
2787         n->p.origin = n->p.pos - n->pos;
2788     } else if (n->n.knot == knot) {
2789         n->n.origin = n->n.pos - n->pos;
2790     } else {
2791         g_assert_not_reached();
2792     }
2796 /**
2797  * Node handle ungrabbed callback.
2798  */
2799 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
2801    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2803     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
2804     if (n->p.knot == knot) {
2805         n->p.origin.a = 0;
2806         sp_knot_set_position(knot, &n->p.pos, state);
2807     } else if (n->n.knot == knot) {
2808         n->n.origin.a = 0;
2809         sp_knot_set_position(knot, &n->n.pos, state);
2810     } else {
2811         g_assert_not_reached();
2812     }
2814     sp_nodepath_update_repr(n->subpath->nodepath);
2817 /**
2818  * Node handle "request" signal callback.
2819  */
2820 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2822     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2824     Inkscape::NodePath::NodeSide *me, *opposite;
2825     gint which;
2826     if (n->p.knot == knot) {
2827         me = &n->p;
2828         opposite = &n->n;
2829         which = -1;
2830     } else if (n->n.knot == knot) {
2831         me = &n->n;
2832         opposite = &n->p;
2833         which = 1;
2834     } else {
2835         me = opposite = NULL;
2836         which = 0;
2837         g_assert_not_reached();
2838     }
2840     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
2842     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
2844     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
2845         /* We are smooth node adjacent with line */
2846         NR::Point const delta = *p - n->pos;
2847         NR::Coord const len = NR::L2(delta);
2848         Inkscape::NodePath::Node *othernode = opposite->other;
2849         NR::Point const ndelta = n->pos - othernode->pos;
2850         NR::Coord const linelen = NR::L2(ndelta);
2851         if (len > NR_EPSILON && linelen > NR_EPSILON) {
2852             NR::Coord const scal = dot(delta, ndelta) / linelen;
2853             (*p) = n->pos + (scal / linelen) * ndelta;
2854         }
2855         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
2856     } else {
2857         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
2858     }
2860     sp_node_adjust_handle(n, -which);
2862     return FALSE;
2865 /**
2866  * Node handle moved callback.
2867  */
2868 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2870    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2872    Inkscape::NodePath::NodeSide *me;
2873    Inkscape::NodePath::NodeSide *other;
2874     if (n->p.knot == knot) {
2875         me = &n->p;
2876         other = &n->n;
2877     } else if (n->n.knot == knot) {
2878         me = &n->n;
2879         other = &n->p;
2880     } else {
2881         me = NULL;
2882         other = NULL;
2883         g_assert_not_reached();
2884     }
2886     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
2887     Radial rme(me->pos - n->pos);
2888     Radial rother(other->pos - n->pos);
2889     Radial rnew(*p - n->pos);
2891     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
2892         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
2893         /* 0 interpreted as "no snapping". */
2895         // The closest PI/snaps angle, starting from zero.
2896         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
2897         if (me->origin.a == HUGE_VAL) {
2898             // ortho doesn't exist: original handle was zero length.
2899             rnew.a = a_snapped;
2900         } else {
2901             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
2902              * its opposite and perpendiculars). */
2903             double const a_ortho = me->origin.a + floor((rnew.a - me->origin.a)/(M_PI/2) + 0.5) * (M_PI/2);
2905             // Snap to the closest.
2906             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
2907                        ? a_snapped
2908                        : a_ortho );
2909         }
2910     }
2912     if (state & GDK_MOD1_MASK) {
2913         // lock handle length
2914         rnew.r = me->origin.r;
2915     }
2917     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
2918         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
2919         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
2920         rother.a += rnew.a - rme.a;
2921         other->pos = NR::Point(rother) + n->pos;
2922         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
2923         sp_knot_set_position(other->knot, &other->pos, 0);
2924     }
2926     me->pos = NR::Point(rnew) + n->pos;
2927     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
2929     // this is what sp_knot_set_position does, but without emitting the signal:
2930     // we cannot emit a "moved" signal because we're now processing it
2931     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
2933     knot->desktop->set_coordinate_status(me->pos);
2935     update_object(n->subpath->nodepath);
2937     /* status text */
2938     SPDesktop *desktop = n->subpath->nodepath->desktop;
2939     if (!desktop) return;
2940     SPEventContext *ec = desktop->event_context;
2941     if (!ec) return;
2942     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
2943     if (!mc) return;
2945     double degrees = 180 / M_PI * rnew.a;
2946     if (degrees > 180) degrees -= 360;
2947     if (degrees < -180) degrees += 360;
2948     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
2949         degrees = angle_to_compass (degrees);
2951     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
2953     mc->setF(Inkscape::NORMAL_MESSAGE,
2954          _("<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);
2956     g_string_free(length, TRUE);
2959 /**
2960  * Node handle event callback.
2961  */
2962 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2964     gboolean ret = FALSE;
2965     switch (event->type) {
2966         case GDK_KEY_PRESS:
2967             switch (get_group0_keyval (&event->key)) {
2968                 case GDK_space:
2969                     if (event->key.state & GDK_BUTTON1_MASK) {
2970                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2971                         stamp_repr(nodepath);
2972                         ret = TRUE;
2973                     }
2974                     break;
2975                 default:
2976                     break;
2977             }
2978             break;
2979         default:
2980             break;
2981     }
2983     return ret;
2986 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
2987                                  Radial &rme, Radial &rother, gboolean const both)
2989     rme.a += angle;
2990     if ( both
2991          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2992          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2993     {
2994         rother.a += angle;
2995     }
2998 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
2999                                         Radial &rme, Radial &rother, gboolean const both)
3001     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3003     gdouble r;
3004     if ( both
3005          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3006          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3007     {
3008         r = MAX(rme.r, rother.r);
3009     } else {
3010         r = rme.r;
3011     }
3013     gdouble const weird_angle = atan2(norm_angle, r);
3014 /* Bulia says norm_angle is just the visible distance that the
3015  * object's end must travel on the screen.  Left as 'angle' for want of
3016  * a better name.*/
3018     rme.a += weird_angle;
3019     if ( both
3020          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3021          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3022     {
3023         rother.a += weird_angle;
3024     }
3027 /**
3028  * Rotate one node.
3029  */
3030 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3032     Inkscape::NodePath::NodeSide *me, *other;
3033     bool both = false;
3035     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3036     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3038     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3039         me = &(n->p);
3040         other = &(n->n);
3041     } else if (!n->p.other) {
3042         me = &(n->n);
3043         other = &(n->p);
3044     } else {
3045         if (which > 0) { // right handle
3046             if (xn > xp) {
3047                 me = &(n->n);
3048                 other = &(n->p);
3049             } else {
3050                 me = &(n->p);
3051                 other = &(n->n);
3052             }
3053         } else if (which < 0){ // left handle
3054             if (xn <= xp) {
3055                 me = &(n->n);
3056                 other = &(n->p);
3057             } else {
3058                 me = &(n->p);
3059                 other = &(n->n);
3060             }
3061         } else { // both handles
3062             me = &(n->n);
3063             other = &(n->p);
3064             both = true;
3065         }
3066     }
3068     Radial rme(me->pos - n->pos);
3069     Radial rother(other->pos - n->pos);
3071     if (screen) {
3072         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3073     } else {
3074         node_rotate_one_internal (*n, angle, rme, rother, both);
3075     }
3077     me->pos = n->pos + NR::Point(rme);
3079     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3080         other->pos =  n->pos + NR::Point(rother);
3081     }
3083     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3084     // so here we just move all the knots without emitting move signals, for speed
3085     sp_node_update_handles(n, false);
3088 /**
3089  * Rotate selected nodes.
3090  */
3091 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3093     if (!nodepath || !nodepath->selected) return;
3095     if (g_list_length(nodepath->selected) == 1) {
3096        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3097         node_rotate_one (n, angle, which, screen);
3098     } else {
3099        // rotate as an object:
3101         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3102         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3103         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3104             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3105             box.expandTo (n->pos); // contain all selected nodes
3106         }
3108         gdouble rot;
3109         if (screen) {
3110             gdouble const zoom = nodepath->desktop->current_zoom();
3111             gdouble const zmove = angle / zoom;
3112             gdouble const r = NR::L2(box.max() - box.midpoint());
3113             rot = atan2(zmove, r);
3114         } else {
3115             rot = angle;
3116         }
3118         NR::Matrix t =
3119             NR::Matrix (NR::translate(-box.midpoint())) *
3120             NR::Matrix (NR::rotate(rot)) *
3121             NR::Matrix (NR::translate(box.midpoint()));
3123         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3124             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3125             n->pos *= t;
3126             n->n.pos *= t;
3127             n->p.pos *= t;
3128             sp_node_update_handles(n, false);
3129         }
3130     }
3132     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n");
3135 /**
3136  * Scale one node.
3137  */
3138 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3140     bool both = false;
3141     Inkscape::NodePath::NodeSide *me, *other;
3143     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3144     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3146     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3147         me = &(n->p);
3148         other = &(n->n);
3149         n->code = NR_CURVETO;
3150     } else if (!n->p.other) {
3151         me = &(n->n);
3152         other = &(n->p);
3153         if (n->n.other)
3154             n->n.other->code = NR_CURVETO;
3155     } else {
3156         if (which > 0) { // right handle
3157             if (xn > xp) {
3158                 me = &(n->n);
3159                 other = &(n->p);
3160                 if (n->n.other)
3161                     n->n.other->code = NR_CURVETO;
3162             } else {
3163                 me = &(n->p);
3164                 other = &(n->n);
3165                 n->code = NR_CURVETO;
3166             }
3167         } else if (which < 0){ // left handle
3168             if (xn <= xp) {
3169                 me = &(n->n);
3170                 other = &(n->p);
3171                 if (n->n.other)
3172                     n->n.other->code = NR_CURVETO;
3173             } else {
3174                 me = &(n->p);
3175                 other = &(n->n);
3176                 n->code = NR_CURVETO;
3177             }
3178         } else { // both handles
3179             me = &(n->n);
3180             other = &(n->p);
3181             both = true;
3182             n->code = NR_CURVETO;
3183             if (n->n.other)
3184                 n->n.other->code = NR_CURVETO;
3185         }
3186     }
3188     Radial rme(me->pos - n->pos);
3189     Radial rother(other->pos - n->pos);
3191     rme.r += grow;
3192     if (rme.r < 0) rme.r = 0;
3193     if (rme.a == HUGE_VAL) {
3194         if (me->other) { // if direction is unknown, initialize it towards the next node
3195             Radial rme_next(me->other->pos - n->pos);
3196             rme.a = rme_next.a;
3197         } else { // if there's no next, initialize to 0
3198             rme.a = 0;
3199         }
3200     }
3201     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3202         rother.r += grow;
3203         if (rother.r < 0) rother.r = 0;
3204         if (rother.a == HUGE_VAL) {
3205             rother.a = rme.a + M_PI;
3206         }
3207     }
3209     me->pos = n->pos + NR::Point(rme);
3211     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3212         other->pos = n->pos + NR::Point(rother);
3213     }
3215     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3216     // so here we just move all the knots without emitting move signals, for speed
3217     sp_node_update_handles(n, false);
3220 /**
3221  * Scale selected nodes.
3222  */
3223 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3225     if (!nodepath || !nodepath->selected) return;
3227     if (g_list_length(nodepath->selected) == 1) {
3228         // scale handles of the single selected node
3229         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3230         node_scale_one (n, grow, which);
3231     } else {
3232         // scale nodes as an "object":
3234         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3235         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3236         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3237             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3238             box.expandTo (n->pos); // contain all selected nodes
3239         }
3241         double scale = (box.maxExtent() + grow)/box.maxExtent();
3243         NR::Matrix t =
3244             NR::Matrix (NR::translate(-box.midpoint())) *
3245             NR::Matrix (NR::scale(scale, scale)) *
3246             NR::Matrix (NR::translate(box.midpoint()));
3248         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3249             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3250             n->pos *= t;
3251             n->n.pos *= t;
3252             n->p.pos *= t;
3253             sp_node_update_handles(n, false);
3254         }
3255     }
3257     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n");
3260 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3262     if (!nodepath) return;
3263     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3266 /**
3267  * Flip selected nodes horizontally/vertically.
3268  */
3269 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3271     if (!nodepath || !nodepath->selected) return;
3273     if (g_list_length(nodepath->selected) == 1) {
3274         // flip handles of the single selected node
3275         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3276         double temp = n->p.pos[axis];
3277         n->p.pos[axis] = n->n.pos[axis];
3278         n->n.pos[axis] = temp;
3279         sp_node_update_handles(n, false);
3280     } else {
3281         // scale nodes as an "object":
3283         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3284         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3285         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3286             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3287             box.expandTo (n->pos); // contain all selected nodes
3288         }
3290         NR::Matrix t =
3291             NR::Matrix (NR::translate(-box.midpoint())) *
3292             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3293             NR::Matrix (NR::translate(box.midpoint()));
3295         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3296             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3297             n->pos *= t;
3298             n->n.pos *= t;
3299             n->p.pos *= t;
3300             sp_node_update_handles(n, false);
3301         }
3302     }
3304     sp_nodepath_update_repr(nodepath);
3307 //-----------------------------------------------
3308 /**
3309  * Return new subpath under given nodepath.
3310  */
3311 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3313     g_assert(nodepath);
3314     g_assert(nodepath->desktop);
3316    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3318     s->nodepath = nodepath;
3319     s->closed = FALSE;
3320     s->nodes = NULL;
3321     s->first = NULL;
3322     s->last = NULL;
3324     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3325     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3326     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3328     return s;
3331 /**
3332  * Destroy nodes in subpath, then subpath itself.
3333  */
3334 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3336     g_assert(subpath);
3337     g_assert(subpath->nodepath);
3338     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3340     while (subpath->nodes) {
3341         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3342     }
3344     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3346     g_free(subpath);
3349 /**
3350  * Link head to tail in subpath.
3351  */
3352 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3354     g_assert(!sp->closed);
3355     g_assert(sp->last != sp->first);
3356     g_assert(sp->first->code == NR_MOVETO);
3358     sp->closed = TRUE;
3360     //Link the head to the tail
3361     sp->first->p.other = sp->last;
3362     sp->last->n.other  = sp->first;
3363     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3364     sp->first          = sp->last;
3366     //Remove the extra end node
3367     sp_nodepath_node_destroy(sp->last->n.other);
3370 /**
3371  * Open closed (loopy) subpath at node.
3372  */
3373 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3375     g_assert(sp->closed);
3376     g_assert(n->subpath == sp);
3377     g_assert(sp->first == sp->last);
3379     /* We create new startpoint, current node will become last one */
3381    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3382                                                 &n->pos, &n->pos, &n->n.pos);
3385     sp->closed        = FALSE;
3387     //Unlink to make a head and tail
3388     sp->first         = new_path;
3389     sp->last          = n;
3390     n->n.other        = NULL;
3391     new_path->p.other = NULL;
3394 /**
3395  * Returns area in triangle given by points; may be negative.
3396  */
3397 inline double
3398 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3400     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]);
3403 /**
3404  * Return new node in subpath with given properties.
3405  * \param pos Position of node.
3406  * \param ppos Handle position in previous direction
3407  * \param npos Handle position in previous direction
3408  */
3409 Inkscape::NodePath::Node *
3410 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)
3412     g_assert(sp);
3413     g_assert(sp->nodepath);
3414     g_assert(sp->nodepath->desktop);
3416     if (nodechunk == NULL)
3417         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3419     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3421     n->subpath  = sp;
3423     if (type != Inkscape::NodePath::NODE_NONE) {
3424         // use the type from sodipodi:nodetypes
3425         n->type = type;
3426     } else {
3427         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3428             // points are (almost) collinear
3429             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3430                 // endnode, or a node with a retracted handle
3431                 n->type = Inkscape::NodePath::NODE_CUSP;
3432             } else {
3433                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3434             }
3435         } else {
3436             n->type = Inkscape::NodePath::NODE_CUSP;
3437         }
3438     }
3440     n->code     = code;
3441     n->selected = FALSE;
3442     n->pos      = *pos;
3443     n->p.pos    = *ppos;
3444     n->n.pos    = *npos;
3446     n->dragging_out = NULL;
3448     Inkscape::NodePath::Node *prev;
3449     if (next) {
3450         //g_assert(g_list_find(sp->nodes, next));
3451         prev = next->p.other;
3452     } else {
3453         prev = sp->last;
3454     }
3456     if (prev)
3457         prev->n.other = n;
3458     else
3459         sp->first = n;
3461     if (next)
3462         next->p.other = n;
3463     else
3464         sp->last = n;
3466     n->p.other = prev;
3467     n->n.other = next;
3469     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"));
3470     sp_knot_set_position(n->knot, pos, 0);
3472     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3473     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3474     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3475     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3476     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3477     sp_knot_update_ctrl(n->knot);
3479     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3480     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3481     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3482     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3483     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3484     sp_knot_show(n->knot);
3486     // We only create handle knots and lines on demand
3487     n->p.knot = NULL;
3488     n->p.line = NULL;
3489     n->n.knot = NULL;
3490     n->n.line = NULL;
3492     sp->nodes = g_list_prepend(sp->nodes, n);
3494     return n;
3497 /**
3498  * Destroy node and its knots, link neighbors in subpath.
3499  */
3500 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3502     g_assert(node);
3503     g_assert(node->subpath);
3504     g_assert(SP_IS_KNOT(node->knot));
3506    Inkscape::NodePath::SubPath *sp = node->subpath;
3508     if (node->selected) { // first, deselect
3509         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3510         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3511     }
3513     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3515     g_object_unref(G_OBJECT(node->knot));
3516     if (node->p.knot)
3517         g_object_unref(G_OBJECT(node->p.knot));
3518     if (node->n.knot)
3519         g_object_unref(G_OBJECT(node->n.knot));
3521     if (node->p.line)
3522         gtk_object_destroy(GTK_OBJECT(node->p.line));
3523     if (node->n.line)
3524         gtk_object_destroy(GTK_OBJECT(node->n.line));
3526     if (sp->nodes) { // there are others nodes on the subpath
3527         if (sp->closed) {
3528             if (sp->first == node) {
3529                 g_assert(sp->last == node);
3530                 sp->first = node->n.other;
3531                 sp->last = sp->first;
3532             }
3533             node->p.other->n.other = node->n.other;
3534             node->n.other->p.other = node->p.other;
3535         } else {
3536             if (sp->first == node) {
3537                 sp->first = node->n.other;
3538                 sp->first->code = NR_MOVETO;
3539             }
3540             if (sp->last == node) sp->last = node->p.other;
3541             if (node->p.other) node->p.other->n.other = node->n.other;
3542             if (node->n.other) node->n.other->p.other = node->p.other;
3543         }
3544     } else { // this was the last node on subpath
3545         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
3546     }
3548     g_mem_chunk_free(nodechunk, node);
3551 /**
3552  * Returns one of the node's two sides.
3553  * \param which Indicates which side.
3554  * \return Pointer to previous node side if which==-1, next if which==1.
3555  */
3556 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
3558     g_assert(node);
3560     switch (which) {
3561         case -1:
3562             return &node->p;
3563         case 1:
3564             return &node->n;
3565         default:
3566             break;
3567     }
3569     g_assert_not_reached();
3571     return NULL;
3574 /**
3575  * Return the other side of the node, given one of its sides.
3576  */
3577 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
3579     g_assert(node);
3581     if (me == &node->p) return &node->n;
3582     if (me == &node->n) return &node->p;
3584     g_assert_not_reached();
3586     return NULL;
3589 /**
3590  * Return NRPathcode on the given side of the node.
3591  */
3592 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
3594     g_assert(node);
3596     if (me == &node->p) {
3597         if (node->p.other) return (NRPathcode)node->code;
3598         return NR_MOVETO;
3599     }
3601     if (me == &node->n) {
3602         if (node->n.other) return (NRPathcode)node->n.other->code;
3603         return NR_MOVETO;
3604     }
3606     g_assert_not_reached();
3608     return NR_END;
3611 /**
3612  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
3613  */
3614 Inkscape::NodePath::Node *
3615 sp_nodepath_get_node_by_index(int index)
3617     Inkscape::NodePath::Node *e = NULL;
3619     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
3620     if (!nodepath) {
3621         return e;
3622     }
3624     //find segment
3625     for (GList *l = nodepath->subpaths; l ; l=l->next) {
3627         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
3628         int n = g_list_length(sp->nodes);
3629         if (sp->closed) {
3630             n++;
3631         }
3633         //if the piece belongs to this subpath grab it
3634         //otherwise move onto the next subpath
3635         if (index < n) {
3636             e = sp->first;
3637             for (int i = 0; i < index; ++i) {
3638                 e = e->n.other;
3639             }
3640             break;
3641         } else {
3642             if (sp->closed) {
3643                 index -= (n+1);
3644             } else {
3645                 index -= n;
3646             }
3647         }
3648     }
3650     return e;
3653 /**
3654  * Returns plain text meaning of node type.
3655  */
3656 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
3658     unsigned retracted = 0;
3659     bool endnode = false;
3661     for (int which = -1; which <= 1; which += 2) {
3662         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
3663         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
3664             retracted ++;
3665         if (!side->other)
3666             endnode = true;
3667     }
3669     if (retracted == 0) {
3670         if (endnode) {
3671                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
3672                 return _("end node");
3673         } else {
3674             switch (node->type) {
3675                 case Inkscape::NodePath::NODE_CUSP:
3676                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
3677                     return _("cusp");
3678                 case Inkscape::NodePath::NODE_SMOOTH:
3679                     // TRANSLATORS: "smooth" is an adjective here
3680                     return _("smooth");
3681                 case Inkscape::NodePath::NODE_SYMM:
3682                     return _("symmetric");
3683             }
3684         }
3685     } else if (retracted == 1) {
3686         if (endnode) {
3687             // TRANSLATORS: "end" is an adjective here (NOT a verb)
3688             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
3689         } else {
3690             return _("one handle retracted (drag with <b>Shift</b> to extend)");
3691         }
3692     } else {
3693         return _("both handles retracted (drag with <b>Shift</b> to extend)");
3694     }
3696     return NULL;
3699 /**
3700  * Handles content of statusbar as long as node tool is active.
3701  */
3702 void
3703 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
3705     gchar const *when_selected = _("<b>Drag</b> nodes or node handles; <b>arrow</b> keys to move nodes");
3706     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
3708     gint total = 0;
3709     gint selected = 0;
3710     SPDesktop *desktop = NULL;
3712     if (nodepath) {
3713         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3714             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3715             total += g_list_length(subpath->nodes);
3716         }
3717         selected = g_list_length(nodepath->selected);
3718         desktop = nodepath->desktop;
3719     } else {
3720         desktop = SP_ACTIVE_DESKTOP;
3721     }
3723     SPEventContext *ec = desktop->event_context;
3724     if (!ec) return;
3725     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3726     if (!mc) return;
3728     if (selected == 0) {
3729         Inkscape::Selection *sel = desktop->selection;
3730         if (!sel || sel->isEmpty()) {
3731             mc->setF(Inkscape::NORMAL_MESSAGE,
3732                      _("Select a single object to edit its nodes or handles."));
3733         } else {
3734             if (nodepath) {
3735             mc->setF(Inkscape::NORMAL_MESSAGE,
3736                      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.",
3737                               "<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.",
3738                               total),
3739                      total);
3740             } else {
3741                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
3742                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
3743                 } else {
3744                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
3745                 }
3746             }
3747         }
3748     } else if (nodepath && selected == 1) {
3749         mc->setF(Inkscape::NORMAL_MESSAGE,
3750                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
3751                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
3752                           total),
3753                  selected, total, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
3754     } else {
3755         mc->setF(Inkscape::NORMAL_MESSAGE,
3756                  ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
3757                           "<b>%i</b> of <b>%i</b> nodes selected. %s.",
3758                           total),
3759                  selected, total, when_selected);
3760     }
3764 /*
3765   Local Variables:
3766   mode:c++
3767   c-file-style:"stroustrup"
3768   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
3769   indent-tabs-mode:nil
3770   fill-column:99
3771   End:
3772 */
3773 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :