Code

patch 1484602 by Niko Kiirala
[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(SP_CURVE_BPATH(curve));
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(SP_CURVE_BPATH(curve));
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 = NR_HUGE;
925     NR::Point delta(dx, dy);
926     NR::Point best_pt = delta;
928     if (snap) {
929         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
930         
931         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
932             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
933             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAP_POINT, n->pos + delta, NULL);
934             if (s.getDistance() < best) {
935                 best = s.getDistance();
936                 best_pt = s.getPoint() - n->pos;
937             }
938         }
939     }
941     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
942        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
943         sp_node_moveto(n, n->pos + best_pt);
944     }
946     // do not update repr here so that node dragging is acceptably fast
947     update_object(nodepath);
950 /**
951  * Move node selection to point, adjust its and neighbouring handles,
952  * handle possible snapping, and commit the change with possible undo.
953  */
954 void
955 sp_node_selected_move(gdouble dx, gdouble dy)
957     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
958     if (!nodepath) return;
960     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
962     if (dx == 0) {
963         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
964     } else if (dy == 0) {
965         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
966     } else {
967         sp_nodepath_update_repr(nodepath);
968     }
971 /**
972  * Move node selection off screen and commit the change.
973  */
974 void
975 sp_node_selected_move_screen(gdouble dx, gdouble dy)
977     // borrowed from sp_selection_move_screen in selection-chemistry.c
978     // we find out the current zoom factor and divide deltas by it
979     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
981     gdouble zoom = desktop->current_zoom();
982     gdouble zdx = dx / zoom;
983     gdouble zdy = dy / zoom;
985     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
986     if (!nodepath) return;
988     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
990     if (dx == 0) {
991         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
992     } else if (dy == 0) {
993         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
994     } else {
995         sp_nodepath_update_repr(nodepath);
996     }
999 /** If they don't yet exist, creates knot and line for the given side of the node */
1000 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1002     if (!side->knot) {
1003         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"));
1005         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1006         side->knot->setSize (7);
1007         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1008         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1009         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1010         sp_knot_update_ctrl(side->knot);
1012         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1013         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1014         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1015         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1016         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1017         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1018     }
1020     if (!side->line) {
1021         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1022                                         SP_TYPE_CTRLLINE, NULL);
1023     }
1026 /**
1027  * Ensure the given handle of the node is visible/invisible, update its screen position
1028  */
1029 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1031     g_assert(node != NULL);
1033    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1034     NRPathcode code = sp_node_path_code_from_side(node, side);
1036     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1038     if (show_handle) {
1039         if (!side->knot) { // No handle knot at all
1040             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1041             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1042             side->knot->pos = side->pos;
1043             if (side->knot->item) 
1044                 SP_CTRL(side->knot->item)->moveto(side->pos);
1045             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1046             sp_knot_show(side->knot);
1047         } else {
1048             if (side->knot->pos != side->pos) { // only if it's really moved
1049                 if (fire_move_signals) {
1050                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1051                 } else {
1052                     sp_knot_moveto(side->knot, &side->pos);
1053                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1054                 }
1055             }
1056             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1057                 sp_knot_show(side->knot);
1058             }
1059         }
1060         sp_canvas_item_show(side->line);
1061     } else {
1062         if (side->knot) {
1063             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1064                 sp_knot_hide(side->knot);
1065             }
1066         }
1067         if (side->line) {
1068             sp_canvas_item_hide(side->line);
1069         }
1070     }
1073 /**
1074  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1075  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1076  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1077  * updated; otherwise, just move the knots silently (used in batch moves).
1078  */
1079 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1081     g_assert(node != NULL);
1083     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1084         sp_knot_show(node->knot);
1085     }
1087     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1088         if (fire_move_signals)
1089             sp_knot_set_position(node->knot, &node->pos, 0);
1090         else 
1091             sp_knot_moveto(node->knot, &node->pos);
1092     }
1094     gboolean show_handles = node->selected;
1095     if (node->p.other != NULL) {
1096         if (node->p.other->selected) show_handles = TRUE;
1097     }
1098     if (node->n.other != NULL) {
1099         if (node->n.other->selected) show_handles = TRUE;
1100     }
1102     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1103     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1106 /**
1107  * Call sp_node_update_handles() for all nodes on subpath.
1108  */
1109 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1111     g_assert(subpath != NULL);
1113     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1114         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1115     }
1118 /**
1119  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1120  */
1121 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1123     g_assert(nodepath != NULL);
1125     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1126         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1127     }
1130 /**
1131  * Adds all selected nodes in nodepath to list.
1132  */
1133 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1135     StlConv<Node *>::list(l, selected);
1136 /// \todo this adds a copying, rework when the selection becomes a stl list
1139 /**
1140  * Align selected nodes on the specified axis.
1141  */
1142 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1144     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1145         return;
1146     }
1148     if ( !nodepath->selected->next ) { // only one node selected
1149         return;
1150     }
1151    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1152     NR::Point dest(pNode->pos);
1153     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1154         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1155         if (pNode) {
1156             dest[axis] = pNode->pos[axis];
1157             sp_node_moveto(pNode, dest);
1158         }
1159     }
1161     sp_nodepath_update_repr(nodepath);
1164 /// Helper struct.
1165 struct NodeSort
1167    Inkscape::NodePath::Node *_node;
1168     NR::Coord _coord;
1169     /// \todo use vectorof pointers instead of calling copy ctor
1170     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1171         _node(node), _coord(node->pos[axis])
1172     {}
1174 };
1176 static bool operator<(NodeSort const &a, NodeSort const &b)
1178     return (a._coord < b._coord);
1181 /**
1182  * Distribute selected nodes on the specified axis.
1183  */
1184 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1186     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1187         return;
1188     }
1190     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1191         return;
1192     }
1194    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1195     std::vector<NodeSort> sorted;
1196     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1197         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1198         if (pNode) {
1199             NodeSort n(pNode, axis);
1200             sorted.push_back(n);
1201             //dest[axis] = pNode->pos[axis];
1202             //sp_node_moveto(pNode, dest);
1203         }
1204     }
1205     std::sort(sorted.begin(), sorted.end());
1206     unsigned int len = sorted.size();
1207     //overall bboxes span
1208     float dist = (sorted.back()._coord -
1209                   sorted.front()._coord);
1210     //new distance between each bbox
1211     float step = (dist) / (len - 1);
1212     float pos = sorted.front()._coord;
1213     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1214           it < sorted.end();
1215           it ++ )
1216     {
1217         NR::Point dest((*it)._node->pos);
1218         dest[axis] = pos;
1219         sp_node_moveto((*it)._node, dest);
1220         pos += step;
1221     }
1223     sp_nodepath_update_repr(nodepath);
1227 /**
1228  * Call sp_nodepath_line_add_node() for all selected segments.
1229  */
1230 void
1231 sp_node_selected_add_node(void)
1233     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1234     if (!nodepath) {
1235         return;
1236     }
1238     GList *nl = NULL;
1240     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1241        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1242         g_assert(t->selected);
1243         if (t->p.other && t->p.other->selected) {
1244             nl = g_list_prepend(nl, t);
1245         }
1246     }
1248     while (nl) {
1249        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1250        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1251         sp_nodepath_node_select(n, TRUE, FALSE);
1252         nl = g_list_remove(nl, t);
1253     }
1255     /** \todo fixme: adjust ? */
1256     sp_nodepath_update_handles(nodepath);
1258     sp_nodepath_update_repr(nodepath);
1260     sp_nodepath_update_statusbar(nodepath);
1263 /**
1264  * Select segment nearest to point
1265  */
1266 void
1267 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1269     if (!nodepath) {
1270         return;
1271     }
1273     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1275     //find segment to segment
1276     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1278     gboolean force = FALSE;
1279     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1280         force = TRUE;
1281     }
1282     sp_nodepath_node_select(e, (gboolean) toggle, force);
1283     if (e->p.other)
1284         sp_nodepath_node_select(e->p.other, TRUE, force);
1286     sp_nodepath_update_handles(nodepath);
1288     sp_nodepath_update_statusbar(nodepath);
1291 /**
1292  * Add a node nearest to point
1293  */
1294 void
1295 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1297     if (!nodepath) {
1298         return;
1299     }
1301     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1303     //find segment to split
1304     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1306     //don't know why but t seems to flip for lines
1307     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1308         position.t = 1.0 - position.t;
1309     }
1310     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1311     sp_nodepath_node_select(n, FALSE, TRUE);
1313     /* fixme: adjust ? */
1314     sp_nodepath_update_handles(nodepath);
1316     sp_nodepath_update_repr(nodepath);
1318     sp_nodepath_update_statusbar(nodepath);
1321 /*
1322  * Adjusts a segment so that t moves by a certain delta for dragging
1323  * converts lines to curves
1324  *
1325  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1326  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1327  */
1328 void
1329 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta)
1331     /* feel good is an arbitrary parameter that distributes the delta between handles
1332      * if t of the drag point is less than 1/6 distance form the endpoint only
1333      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1334      */
1335     double feel_good;
1336     if (t <= 1.0 / 6.0)
1337         feel_good = 0;
1338     else if (t <= 0.5)
1339         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1340     else if (t <= 5.0 / 6.0)
1341         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1342     else
1343         feel_good = 1;
1345     //if we're dragging a line convert it to a curve
1346     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1347         sp_nodepath_set_line_type(e, NR_CURVETO);
1348     }
1350     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1351     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1352     e->p.other->n.pos += offsetcoord0;
1353     e->p.pos += offsetcoord1;
1355     // adjust handles of adjacent nodes where necessary
1356     sp_node_adjust_handle(e,1);
1357     sp_node_adjust_handle(e->p.other,-1);
1359     sp_nodepath_update_handles(e->subpath->nodepath);
1361     update_object(e->subpath->nodepath);
1363     sp_nodepath_update_statusbar(e->subpath->nodepath);
1367 /**
1368  * Call sp_nodepath_break() for all selected segments.
1369  */
1370 void sp_node_selected_break()
1372     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1373     if (!nodepath) return;
1375     GList *temp = NULL;
1376     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1377        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1378        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1379         if (nn == NULL) continue; // no break, no new node
1380         temp = g_list_prepend(temp, nn);
1381     }
1383     if (temp) {
1384         sp_nodepath_deselect(nodepath);
1385     }
1386     for (GList *l = temp; l != NULL; l = l->next) {
1387         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1388     }
1390     sp_nodepath_update_handles(nodepath);
1392     sp_nodepath_update_repr(nodepath);
1395 /**
1396  * Duplicate the selected node(s).
1397  */
1398 void sp_node_selected_duplicate()
1400     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1401     if (!nodepath) {
1402         return;
1403     }
1405     GList *temp = NULL;
1406     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1407        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1408        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1409         if (nn == NULL) continue; // could not duplicate
1410         temp = g_list_prepend(temp, nn);
1411     }
1413     if (temp) {
1414         sp_nodepath_deselect(nodepath);
1415     }
1416     for (GList *l = temp; l != NULL; l = l->next) {
1417         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1418     }
1420     sp_nodepath_update_handles(nodepath);
1422     sp_nodepath_update_repr(nodepath);
1425 /**
1426  *  Join two nodes by merging them into one.
1427  */
1428 void sp_node_selected_join()
1430     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1431     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1433     if (g_list_length(nodepath->selected) != 2) {
1434         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1435         return;
1436     }
1438    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1439    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1441     g_assert(a != b);
1442     g_assert(a->p.other || a->n.other);
1443     g_assert(b->p.other || b->n.other);
1445     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1446         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1447         return;
1448     }
1450     /* a and b are endpoints */
1452     NR::Point c;
1453     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1454         c = a->pos;
1455     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1456         c = b->pos;
1457     } else {
1458         c = (a->pos + b->pos) / 2;
1459     }
1461     if (a->subpath == b->subpath) {
1462        Inkscape::NodePath::SubPath *sp = a->subpath;
1463         sp_nodepath_subpath_close(sp);
1464         sp_node_moveto (sp->first, c);
1466         sp_nodepath_update_handles(sp->nodepath);
1467         sp_nodepath_update_repr(nodepath);
1468         return;
1469     }
1471     /* a and b are separate subpaths */
1472    Inkscape::NodePath::SubPath *sa = a->subpath;
1473    Inkscape::NodePath::SubPath *sb = b->subpath;
1474     NR::Point p;
1475    Inkscape::NodePath::Node *n;
1476     NRPathcode code;
1477     if (a == sa->first) {
1478         p = sa->first->n.pos;
1479         code = (NRPathcode)sa->first->n.other->code;
1480        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1481         n = sa->last;
1482         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1483         n = n->p.other;
1484         while (n) {
1485             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1486             n = n->p.other;
1487             if (n == sa->first) n = NULL;
1488         }
1489         sp_nodepath_subpath_destroy(sa);
1490         sa = t;
1491     } else if (a == sa->last) {
1492         p = sa->last->p.pos;
1493         code = (NRPathcode)sa->last->code;
1494         sp_nodepath_node_destroy(sa->last);
1495     } else {
1496         code = NR_END;
1497         g_assert_not_reached();
1498     }
1500     if (b == sb->first) {
1501         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1502         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1503             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1504         }
1505     } else if (b == sb->last) {
1506         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1507         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1508             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1509         }
1510     } else {
1511         g_assert_not_reached();
1512     }
1513     /* and now destroy sb */
1515     sp_nodepath_subpath_destroy(sb);
1517     sp_nodepath_update_handles(sa->nodepath);
1519     sp_nodepath_update_repr(nodepath);
1521     sp_nodepath_update_statusbar(nodepath);
1524 /**
1525  *  Join two nodes by adding a segment between them.
1526  */
1527 void sp_node_selected_join_segment()
1529     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1530     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1532     if (g_list_length(nodepath->selected) != 2) {
1533         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1534         return;
1535     }
1537    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1538    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1540     g_assert(a != b);
1541     g_assert(a->p.other || a->n.other);
1542     g_assert(b->p.other || b->n.other);
1544     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1545         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1546         return;
1547     }
1549     if (a->subpath == b->subpath) {
1550        Inkscape::NodePath::SubPath *sp = a->subpath;
1552         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1553         sp->closed = TRUE;
1555         sp->first->p.other = sp->last;
1556         sp->last->n.other  = sp->first;
1558         sp_node_handle_mirror_p_to_n(sp->last);
1559         sp_node_handle_mirror_n_to_p(sp->first);
1561         sp->first->code = sp->last->code;
1562         sp->first       = sp->last;
1564         sp_nodepath_update_handles(sp->nodepath);
1566         sp_nodepath_update_repr(nodepath);
1568         return;
1569     }
1571     /* a and b are separate subpaths */
1572    Inkscape::NodePath::SubPath *sa = a->subpath;
1573    Inkscape::NodePath::SubPath *sb = b->subpath;
1575    Inkscape::NodePath::Node *n;
1576     NR::Point p;
1577     NRPathcode code;
1578     if (a == sa->first) {
1579         code = (NRPathcode) sa->first->n.other->code;
1580        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1581         n = sa->last;
1582         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1583         for (n = n->p.other; n != NULL; n = n->p.other) {
1584             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1585         }
1586         sp_nodepath_subpath_destroy(sa);
1587         sa = t;
1588     } else if (a == sa->last) {
1589         code = (NRPathcode)sa->last->code;
1590     } else {
1591         code = NR_END;
1592         g_assert_not_reached();
1593     }
1595     if (b == sb->first) {
1596         n = sb->first;
1597         sp_node_handle_mirror_p_to_n(sa->last);
1598         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1599         sp_node_handle_mirror_n_to_p(sa->last);
1600         for (n = n->n.other; n != NULL; n = n->n.other) {
1601             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1602         }
1603     } else if (b == sb->last) {
1604         n = sb->last;
1605         sp_node_handle_mirror_p_to_n(sa->last);
1606         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1607         sp_node_handle_mirror_n_to_p(sa->last);
1608         for (n = n->p.other; n != NULL; n = n->p.other) {
1609             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1610         }
1611     } else {
1612         g_assert_not_reached();
1613     }
1614     /* and now destroy sb */
1616     sp_nodepath_subpath_destroy(sb);
1618     sp_nodepath_update_handles(sa->nodepath);
1620     sp_nodepath_update_repr(nodepath);
1623 /**
1624  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1625  */
1626 void sp_node_delete_preserve(GList *nodes_to_delete)
1628     
1629     while (nodes_to_delete) {
1630         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1631         Inkscape::NodePath::SubPath *sp = node->subpath;
1632         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1633         Inkscape::NodePath::Node *sample_cursor = NULL;
1634         Inkscape::NodePath::Node *sample_end = NULL;
1635         Inkscape::NodePath::Node *delete_cursor = node;
1636         bool just_delete = false;
1637         
1638         //find the start of this contiguous selection
1639         //move left to the first node that is not selected
1640         //or the start of the non-closed path
1641         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1642             delete_cursor = curr;
1643         }
1645         //just delete at the beginning of an open path
1646         if (!delete_cursor->p.other) {
1647             sample_cursor = delete_cursor;
1648             just_delete = true;
1649         } else {
1650             sample_cursor = delete_cursor->p.other;
1651         }
1652         
1653         //calculate points for each segment
1654         int rate = 5;
1655         float period = 1.0 / rate;
1656         std::vector<NR::Point> data;
1657         if (!just_delete) {
1658             data.push_back(sample_cursor->pos);
1659             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1660                 //just delete at the end of an open path
1661                 if (!sp->closed && curr->n.other == sp->last) {
1662                     just_delete = true;
1663                     break;
1664                 }
1665                 
1666                 //sample points on the contiguous selected segment
1667                 NR::Point *bez;
1668                 bez = new NR::Point [4];
1669                 bez[0] = curr->pos;
1670                 bez[1] = curr->n.pos;
1671                 bez[2] = curr->n.other->p.pos;
1672                 bez[3] = curr->n.other->pos;
1673                 for (int i=1; i<rate; i++) {
1674                     gdouble t = i * period;
1675                     NR::Point p = bezier_pt(3, bez, t);
1676                     data.push_back(p);
1677                 }
1678                 data.push_back(curr->n.other->pos);
1680                 sample_end = curr->n.other;
1681                 //break if we've come full circle or hit the end of the selection
1682                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
1683                     break;
1684                 }
1685             }
1686         }
1688         if (!just_delete) {
1689             //calculate the best fitting single segment and adjust the endpoints
1690             NR::Point *adata;
1691             adata = new NR::Point [data.size()];
1692             copy(data.begin(), data.end(), adata);
1693             
1694             NR::Point *bez;
1695             bez = new NR::Point [4];
1696             //would decreasing error create a better fitting approximation?
1697             gdouble error = 1.0;
1698             gint ret;
1699             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
1701             //adjust endpoints
1702             sample_cursor->n.pos = bez[1];
1703             sample_end->p.pos = bez[2];
1704         }
1705        
1706         //destroy this contiguous selection
1707         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
1708             Inkscape::NodePath::Node *temp = delete_cursor;
1709             if (delete_cursor->n.other == delete_cursor) {
1710                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
1711                 delete_cursor = NULL; 
1712             } else {
1713                 delete_cursor = delete_cursor->n.other;
1714             }
1715             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
1716             sp_nodepath_node_destroy(temp);
1717         }
1719         //clean up the nodepath (such as for trivial subpaths)
1720         sp_nodepath_cleanup(nodepath);
1722         sp_nodepath_update_handles(nodepath);
1724         // if the entire nodepath is removed, delete the selected object.
1725         if (nodepath->subpaths == NULL ||
1726             sp_nodepath_get_node_count(nodepath) < 2) {
1727             SPDocument *document = sp_desktop_document (nodepath->desktop);
1728             sp_nodepath_destroy(nodepath);
1729             g_list_free(nodes_to_delete);
1730             nodes_to_delete = NULL;
1731             //is the next line necessary?
1732             sp_selection_delete();
1733             sp_document_done (document);
1734             return;
1735         }
1737         sp_nodepath_update_repr(nodepath);
1739         sp_nodepath_update_statusbar(nodepath);
1740     }
1743 /**
1744  * Delete one or more selected nodes.
1745  */
1746 void sp_node_selected_delete()
1748     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1749     if (!nodepath) return;
1750     if (!nodepath->selected) return;
1752     /** \todo fixme: do it the right way */
1753     while (nodepath->selected) {
1754        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
1755         sp_nodepath_node_destroy(node);
1756     }
1759     //clean up the nodepath (such as for trivial subpaths)
1760     sp_nodepath_cleanup(nodepath);
1762     sp_nodepath_update_handles(nodepath);
1764     // if the entire nodepath is removed, delete the selected object.
1765     if (nodepath->subpaths == NULL ||
1766         sp_nodepath_get_node_count(nodepath) < 2) {
1767         SPDocument *document = sp_desktop_document (nodepath->desktop);
1768         sp_nodepath_destroy(nodepath);
1769         sp_selection_delete();
1770         sp_document_done (document);
1771         return;
1772     }
1774     sp_nodepath_update_repr(nodepath);
1776     sp_nodepath_update_statusbar(nodepath);
1779 /**
1780  * Delete one or more segments between two selected nodes.
1781  * This is the code for 'split'.
1782  */
1783 void
1784 sp_node_selected_delete_segment(void)
1786    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
1787    Inkscape::NodePath::Node *curr, *next;     //Iterators
1789     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1790     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1792     if (g_list_length(nodepath->selected) != 2) {
1793         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1794                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1795         return;
1796     }
1798     //Selected nodes, not inclusive
1799    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1800    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1802     if ( ( a==b)                       ||  //same node
1803          (a->subpath  != b->subpath )  ||  //not the same path
1804          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
1805          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
1806     {
1807         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1808                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1809         return;
1810     }
1812     //###########################################
1813     //# BEGIN EDITS
1814     //###########################################
1815     //##################################
1816     //# CLOSED PATH
1817     //##################################
1818     if (a->subpath->closed) {
1821         gboolean reversed = FALSE;
1823         //Since we can go in a circle, we need to find the shorter distance.
1824         //  a->b or b->a
1825         start = end = NULL;
1826         int distance    = 0;
1827         int minDistance = 0;
1828         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
1829             if (curr==b) {
1830                 //printf("a to b:%d\n", distance);
1831                 start = a;//go from a to b
1832                 end   = b;
1833                 minDistance = distance;
1834                 //printf("A to B :\n");
1835                 break;
1836             }
1837             distance++;
1838         }
1840         //try again, the other direction
1841         distance = 0;
1842         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
1843             if (curr==a) {
1844                 //printf("b to a:%d\n", distance);
1845                 if (distance < minDistance) {
1846                     start    = b;  //we go from b to a
1847                     end      = a;
1848                     reversed = TRUE;
1849                     //printf("B to A\n");
1850                 }
1851                 break;
1852             }
1853             distance++;
1854         }
1857         //Copy everything from 'end' to 'start' to a new subpath
1858        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
1859         for (curr=end ; curr ; curr=curr->n.other) {
1860             NRPathcode code = (NRPathcode) curr->code;
1861             if (curr == end)
1862                 code = NR_MOVETO;
1863             sp_nodepath_node_new(t, NULL,
1864                                  (Inkscape::NodePath::NodeType)curr->type, code,
1865                                  &curr->p.pos, &curr->pos, &curr->n.pos);
1866             if (curr == start)
1867                 break;
1868         }
1869         sp_nodepath_subpath_destroy(a->subpath);
1872     }
1876     //##################################
1877     //# OPEN PATH
1878     //##################################
1879     else {
1881         //We need to get the direction of the list between A and B
1882         //Can we walk from a to b?
1883         start = end = NULL;
1884         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
1885             if (curr==b) {
1886                 start = a;  //did it!  we go from a to b
1887                 end   = b;
1888                 //printf("A to B\n");
1889                 break;
1890             }
1891         }
1892         if (!start) {//didn't work?  let's try the other direction
1893             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
1894                 if (curr==a) {
1895                     start = b;  //did it!  we go from b to a
1896                     end   = a;
1897                     //printf("B to A\n");
1898                     break;
1899                 }
1900             }
1901         }
1902         if (!start) {
1903             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1904                                                      _("Cannot find path between nodes."));
1905             return;
1906         }
1910         //Copy everything after 'end' to a new subpath
1911        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
1912         for (curr=end ; curr ; curr=curr->n.other) {
1913             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
1914                                  &curr->p.pos, &curr->pos, &curr->n.pos);
1915         }
1917         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
1918         for (curr = start->n.other ; curr  ; curr=next) {
1919             next = curr->n.other;
1920             sp_nodepath_node_destroy(curr);
1921         }
1923     }
1924     //###########################################
1925     //# END EDITS
1926     //###########################################
1928     //clean up the nodepath (such as for trivial subpaths)
1929     sp_nodepath_cleanup(nodepath);
1931     sp_nodepath_update_handles(nodepath);
1933     sp_nodepath_update_repr(nodepath);
1935     // if the entire nodepath is removed, delete the selected object.
1936     if (nodepath->subpaths == NULL ||
1937         sp_nodepath_get_node_count(nodepath) < 2) {
1938         sp_nodepath_destroy(nodepath);
1939         sp_selection_delete();
1940         return;
1941     }
1943     sp_nodepath_update_statusbar(nodepath);
1946 /**
1947  * Call sp_nodepath_set_line() for all selected segments.
1948  */
1949 void
1950 sp_node_selected_set_line_type(NRPathcode code)
1952     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1953     if (nodepath == NULL) return;
1955     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1956        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1957         g_assert(n->selected);
1958         if (n->p.other && n->p.other->selected) {
1959             sp_nodepath_set_line_type(n, code);
1960         }
1961     }
1963     sp_nodepath_update_repr(nodepath);
1966 /**
1967  * Call sp_nodepath_convert_node_type() for all selected nodes.
1968  */
1969 void
1970 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
1972     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1973     if (nodepath == NULL) return;
1975     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1976         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
1977     }
1979     sp_nodepath_update_repr(nodepath);
1982 /**
1983  * Change select status of node, update its own and neighbour handles.
1984  */
1985 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
1987     node->selected = selected;
1989     if (selected) {
1990         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
1991         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
1992         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
1993         sp_knot_update_ctrl(node->knot);
1994     } else {
1995         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
1996         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
1997         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
1998         sp_knot_update_ctrl(node->knot);
1999     }
2001     sp_node_update_handles(node);
2002     if (node->n.other) sp_node_update_handles(node->n.other);
2003     if (node->p.other) sp_node_update_handles(node->p.other);
2006 /**
2007 \brief Select a node
2008 \param node     The node to select
2009 \param incremental   If true, add to selection, otherwise deselect others
2010 \param override   If true, always select this node, otherwise toggle selected status
2011 */
2012 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2014     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2016     if (incremental) {
2017         if (override) {
2018             if (!g_list_find(nodepath->selected, node)) {
2019                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2020             }
2021             sp_node_set_selected(node, TRUE);
2022         } else { // toggle
2023             if (node->selected) {
2024                 g_assert(g_list_find(nodepath->selected, node));
2025                 nodepath->selected = g_list_remove(nodepath->selected, node);
2026             } else {
2027                 g_assert(!g_list_find(nodepath->selected, node));
2028                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2029             }
2030             sp_node_set_selected(node, !node->selected);
2031         }
2032     } else {
2033         sp_nodepath_deselect(nodepath);
2034         nodepath->selected = g_list_prepend(nodepath->selected, node);
2035         sp_node_set_selected(node, TRUE);
2036     }
2038     sp_nodepath_update_statusbar(nodepath);
2042 /**
2043 \brief Deselect all nodes in the nodepath
2044 */
2045 void
2046 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2048     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2050     while (nodepath->selected) {
2051         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2052         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2053     }
2054     sp_nodepath_update_statusbar(nodepath);
2057 /**
2058 \brief Select or invert selection of all nodes in the nodepath
2059 */
2060 void
2061 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2063     if (!nodepath) return;
2065     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2066        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2067         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2068            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2069            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2070         }
2071     }
2074 /**
2075  * If nothing selected, does the same as sp_nodepath_select_all();
2076  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2077  * (i.e., similar to "select all in layer", with the "selected" subpaths
2078  * being treated as "layers" in the path).
2079  */
2080 void
2081 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2083     if (!nodepath) return;
2085     if (g_list_length (nodepath->selected) == 0) {
2086         sp_nodepath_select_all (nodepath, invert);
2087         return;
2088     }
2090     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2091     GSList *subpaths = NULL;
2093     for (GList *l = copy; l != NULL; l = l->next) {
2094         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2095         Inkscape::NodePath::SubPath *subpath = n->subpath;
2096         if (!g_slist_find (subpaths, subpath))
2097             subpaths = g_slist_prepend (subpaths, subpath);
2098     }
2100     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2101         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2102         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2103             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2104             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2105         }
2106     }
2108     g_slist_free (subpaths);
2109     g_list_free (copy);
2112 /**
2113  * \brief Select the node after the last selected; if none is selected,
2114  * select the first within path.
2115  */
2116 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2118     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2120    Inkscape::NodePath::Node *last = NULL;
2121     if (nodepath->selected) {
2122         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2123            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2124             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2125             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2126                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2127                 if (node->selected) {
2128                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2129                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2130                             if (spl->next) { // there's a next subpath
2131                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2132                                 last = subpath_next->first;
2133                             } else if (spl->prev) { // there's a previous subpath
2134                                 last = NULL; // to be set later to the first node of first subpath
2135                             } else {
2136                                 last = node->n.other;
2137                             }
2138                         } else {
2139                             last = node->n.other;
2140                         }
2141                     } else {
2142                         if (node->n.other) {
2143                             last = node->n.other;
2144                         } else {
2145                             if (spl->next) { // there's a next subpath
2146                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2147                                 last = subpath_next->first;
2148                             } else if (spl->prev) { // there's a previous subpath
2149                                 last = NULL; // to be set later to the first node of first subpath
2150                             } else {
2151                                 last = (Inkscape::NodePath::Node *) subpath->first;
2152                             }
2153                         }
2154                     }
2155                 }
2156             }
2157         }
2158         sp_nodepath_deselect(nodepath);
2159     }
2161     if (last) { // there's at least one more node after selected
2162         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2163     } else { // no more nodes, select the first one in first subpath
2164        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2165         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2166     }
2169 /**
2170  * \brief Select the node before the first selected; if none is selected,
2171  * select the last within path
2172  */
2173 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2175     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2177    Inkscape::NodePath::Node *last = NULL;
2178     if (nodepath->selected) {
2179         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2180            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2181             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2182                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2183                 if (node->selected) {
2184                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2185                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2186                             if (spl->prev) { // there's a prev subpath
2187                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2188                                 last = subpath_prev->last;
2189                             } else if (spl->next) { // there's a next subpath
2190                                 last = NULL; // to be set later to the last node of last subpath
2191                             } else {
2192                                 last = node->p.other;
2193                             }
2194                         } else {
2195                             last = node->p.other;
2196                         }
2197                     } else {
2198                         if (node->p.other) {
2199                             last = node->p.other;
2200                         } else {
2201                             if (spl->prev) { // there's a prev subpath
2202                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2203                                 last = subpath_prev->last;
2204                             } else if (spl->next) { // there's a next subpath
2205                                 last = NULL; // to be set later to the last node of last subpath
2206                             } else {
2207                                 last = (Inkscape::NodePath::Node *) subpath->last;
2208                             }
2209                         }
2210                     }
2211                 }
2212             }
2213         }
2214         sp_nodepath_deselect(nodepath);
2215     }
2217     if (last) { // there's at least one more node before selected
2218         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2219     } else { // no more nodes, select the last one in last subpath
2220         GList *spl = g_list_last(nodepath->subpaths);
2221        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2222         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2223     }
2226 /**
2227  * \brief Select all nodes that are within the rectangle.
2228  */
2229 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2231     if (!incremental) {
2232         sp_nodepath_deselect(nodepath);
2233     }
2235     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2236        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2237         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2238            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2240             if (b.contains(node->pos)) {
2241                 sp_nodepath_node_select(node, TRUE, TRUE);
2242             }
2243         }
2244     }
2247 /**
2248 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2249 */
2250 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2252     if (!nodepath->selected) {
2253         return NULL;
2254     }
2256     GList *r = NULL;
2257     guint i = 0;
2258     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2259        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2260         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2261            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2262             i++;
2263             if (node->selected) {
2264                 r = g_list_append(r, GINT_TO_POINTER(i));
2265             }
2266         }
2267     }
2268     return r;
2271 /**
2272 \brief  Restores selection by selecting nodes whose positions are in the list
2273 */
2274 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2276     sp_nodepath_deselect(nodepath);
2278     guint i = 0;
2279     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2280        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2281         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2282            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2283             i++;
2284             if (g_list_find(r, GINT_TO_POINTER(i))) {
2285                 sp_nodepath_node_select(node, TRUE, TRUE);
2286             }
2287         }
2288     }
2292 /**
2293 \brief Adjusts handle according to node type and line code.
2294 */
2295 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2297     double len, otherlen, linelen;
2299     g_assert(node);
2301    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2302    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2304     /** \todo fixme: */
2305     if (me->other == NULL) return;
2306     if (other->other == NULL) return;
2308     /* I have line */
2310     NRPathcode mecode, ocode;
2311     if (which_adjust == 1) {
2312         mecode = (NRPathcode)me->other->code;
2313         ocode = (NRPathcode)node->code;
2314     } else {
2315         mecode = (NRPathcode)node->code;
2316         ocode = (NRPathcode)other->other->code;
2317     }
2319     if (mecode == NR_LINETO) return;
2321     /* I am curve */
2323     if (other->other == NULL) return;
2325     /* Other has line */
2327     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2329     NR::Point delta;
2330     if (ocode == NR_LINETO) {
2331         /* other is lineto, we are either smooth or symm */
2332        Inkscape::NodePath::Node *othernode = other->other;
2333         len = NR::L2(me->pos - node->pos);
2334         delta = node->pos - othernode->pos;
2335         linelen = NR::L2(delta);
2336         if (linelen < 1e-18) 
2337             return;
2338         me->pos = node->pos + (len / linelen)*delta;
2339         return;
2340     }
2342     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2344         me->pos = 2 * node->pos - other->pos;
2345         return;
2346     }
2348     /* We are smooth */
2350     len = NR::L2(me->pos - node->pos);
2351     delta = other->pos - node->pos;
2352     otherlen = NR::L2(delta);
2353     if (otherlen < 1e-18) return;
2355     me->pos = node->pos - (len / otherlen) * delta;
2358 /**
2359  \brief Adjusts both handles according to node type and line code
2360  */
2361 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2363     g_assert(node);
2365     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2367     /* we are either smooth or symm */
2369     if (node->p.other == NULL) return;
2371     if (node->n.other == NULL) return;
2373     if (node->code == NR_LINETO) {
2374         if (node->n.other->code == NR_LINETO) return;
2375         sp_node_adjust_handle(node, 1);
2376         return;
2377     }
2379     if (node->n.other->code == NR_LINETO) {
2380         if (node->code == NR_LINETO) return;
2381         sp_node_adjust_handle(node, -1);
2382         return;
2383     }
2385     /* both are curves */
2386     NR::Point const delta( node->n.pos - node->p.pos );
2388     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2389         node->p.pos = node->pos - delta / 2;
2390         node->n.pos = node->pos + delta / 2;
2391         return;
2392     }
2394     /* We are smooth */
2395     double plen = NR::L2(node->p.pos - node->pos);
2396     if (plen < 1e-18) return;
2397     double nlen = NR::L2(node->n.pos - node->pos);
2398     if (nlen < 1e-18) return;
2399     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2400     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2403 /**
2404  * Node event callback.
2405  */
2406 static gboolean node_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2408     gboolean ret = FALSE;
2409     switch (event->type) {
2410         case GDK_ENTER_NOTIFY:
2411             active_node = n;
2412             break;
2413         case GDK_LEAVE_NOTIFY:
2414             active_node = NULL;
2415             break;
2416         case GDK_KEY_PRESS:
2417             switch (get_group0_keyval (&event->key)) {
2418                 case GDK_space:
2419                     if (event->key.state & GDK_BUTTON1_MASK) {
2420                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2421                         stamp_repr(nodepath);
2422                         ret = TRUE;
2423                     }
2424                     break;
2425                 default:
2426                     break;
2427             }
2428             break;
2429         default:
2430             break;
2431     }
2433     return ret;
2436 /**
2437  * Handle keypress on node; directly called.
2438  */
2439 gboolean node_key(GdkEvent *event)
2441     Inkscape::NodePath::Path *np;
2443     // there is no way to verify nodes so set active_node to nil when deleting!!
2444     if (active_node == NULL) return FALSE;
2446     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2447         gint ret = FALSE;
2448         switch (get_group0_keyval (&event->key)) {
2449             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2450             case GDK_BackSpace:
2451                 np = active_node->subpath->nodepath;
2452                 sp_nodepath_node_destroy(active_node);
2453                 sp_nodepath_update_repr(np);
2454                 active_node = NULL;
2455                 ret = TRUE;
2456                 break;
2457             case GDK_c:
2458                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2459                 ret = TRUE;
2460                 break;
2461             case GDK_s:
2462                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2463                 ret = TRUE;
2464                 break;
2465             case GDK_y:
2466                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2467                 ret = TRUE;
2468                 break;
2469             case GDK_b:
2470                 sp_nodepath_node_break(active_node);
2471                 ret = TRUE;
2472                 break;
2473         }
2474         return ret;
2475     }
2476     return FALSE;
2479 /**
2480  * Mouseclick on node callback.
2481  */
2482 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2484    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2486     if (state & GDK_CONTROL_MASK) {
2487         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2489         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2490             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2491                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2492             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2493                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2494             } else {
2495                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2496             }
2497             sp_nodepath_update_repr(nodepath);
2498             sp_nodepath_update_statusbar(nodepath);
2500         } else { //ctrl+alt+click: delete node
2501             GList *node_to_delete = NULL;
2502             node_to_delete = g_list_append(node_to_delete, n);
2503             sp_node_delete_preserve(node_to_delete);
2504         }
2506     } else {
2507         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2508     }
2511 /**
2512  * Mouse grabbed node callback.
2513  */
2514 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2516    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2518     n->origin = knot->pos;
2520     if (!n->selected) {
2521         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2522     }
2525 /**
2526  * Mouse ungrabbed node callback.
2527  */
2528 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
2530    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2532    n->dragging_out = NULL;
2534    sp_nodepath_update_repr(n->subpath->nodepath);
2537 /**
2538  * The point on a line, given by its angle, closest to the given point.
2539  * \param p  A point.
2540  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2541  * \param closest  Pointer to the point struct where the result is stored.
2542  * \todo FIXME: use dot product perhaps?
2543  */
2544 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
2546     if (a == HUGE_VAL) { // vertical
2547         *closest = NR::Point(0, (*p)[NR::Y]);
2548     } else {
2549         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
2550         (*closest)[NR::Y] = a * (*closest)[NR::X];
2551     }
2554 /**
2555  * Distance from the point to a line given by its angle.
2556  * \param p  A point.
2557  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2558  */
2559 static double point_line_distance(NR::Point *p, double a)
2561     NR::Point c;
2562     point_line_closest(p, a, &c);
2563     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]));
2566 /**
2567  * Callback for node "request" signal.
2568  * \todo fixme: This goes to "moved" event? (lauris)
2569  */
2570 static gboolean
2571 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2573     double yn, xn, yp, xp;
2574     double an, ap, na, pa;
2575     double d_an, d_ap, d_na, d_pa;
2576     gboolean collinear = FALSE;
2577     NR::Point c;
2578     NR::Point pr;
2580    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2582    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
2583    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
2585        NR::Point mouse = (*p);
2587        if (!n->dragging_out) {
2588            // This is the first drag-out event; find out which handle to drag out
2589            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
2590            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
2592            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
2593                return FALSE;
2595            Inkscape::NodePath::NodeSide *opposite;
2596            if (appr_p > appr_n) { // closer to p
2597                n->dragging_out = &n->p;
2598                opposite = &n->n;
2599                n->code = NR_CURVETO;
2600            } else if (appr_p < appr_n) { // closer to n
2601                n->dragging_out = &n->n;
2602                opposite = &n->p;
2603                n->n.other->code = NR_CURVETO;
2604            } else { // p and n nodes are the same
2605                if (n->n.pos != n->pos) { // n handle already dragged, drag p
2606                    n->dragging_out = &n->p;
2607                    opposite = &n->n;
2608                    n->code = NR_CURVETO;
2609                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
2610                    n->dragging_out = &n->n;
2611                    opposite = &n->p;
2612                    n->n.other->code = NR_CURVETO;
2613                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
2614                    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);
2615                    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);
2616                    if (appr_other_p > appr_other_n) { // closer to other's p handle
2617                        n->dragging_out = &n->n;
2618                        opposite = &n->p;
2619                        n->n.other->code = NR_CURVETO;
2620                    } else { // closer to other's n handle
2621                        n->dragging_out = &n->p;
2622                        opposite = &n->n;
2623                        n->code = NR_CURVETO;
2624                    }
2625                }
2626            }
2628            // if there's another handle, make sure the one we drag out starts parallel to it
2629            if (opposite->pos != n->pos) {
2630                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
2631            }
2633            // knots might not be created yet!
2634            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
2635            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
2636        }
2638        // pass this on to the handle-moved callback
2639        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
2640        sp_node_update_handles(n);
2641        return TRUE;
2642    }
2644     if (state & GDK_CONTROL_MASK) { // constrained motion
2646         // calculate relative distances of handles
2647         // n handle:
2648         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
2649         xn = n->n.pos[NR::X] - n->pos[NR::X];
2650         // if there's no n handle (straight line), see if we can use the direction to the next point on path
2651         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
2652             if (n->n.other) { // if there is the next point
2653                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
2654                     yn = n->n.other->pos[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
2655                     xn = n->n.other->pos[NR::X] - n->origin[NR::X];
2656             }
2657         }
2658         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
2659         if (yn < 0) { xn = -xn; yn = -yn; }
2661         // p handle:
2662         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
2663         xp = n->p.pos[NR::X] - n->pos[NR::X];
2664         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
2665         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
2666             if (n->p.other) {
2667                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
2668                     yp = n->p.other->pos[NR::Y] - n->origin[NR::Y];
2669                     xp = n->p.other->pos[NR::X] - n->origin[NR::X];
2670             }
2671         }
2672         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
2673         if (yp < 0) { xp = -xp; yp = -yp; }
2675         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
2676             // sliding on handles, only if at least one of the handles is non-vertical
2677             // (otherwise it's the same as ctrl+drag anyway)
2679             // calculate angles of the handles
2680             if (xn == 0) {
2681                 if (yn == 0) { // no handle, consider it the continuation of the other one
2682                     an = 0;
2683                     collinear = TRUE;
2684                 }
2685                 else an = 0; // vertical; set the angle to horizontal
2686             } else an = yn/xn;
2688             if (xp == 0) {
2689                 if (yp == 0) { // no handle, consider it the continuation of the other one
2690                     ap = an;
2691                 }
2692                 else ap = 0; // vertical; set the angle to horizontal
2693             } else  ap = yp/xp;
2695             if (collinear) an = ap;
2697             // angles of the perpendiculars; HUGE_VAL means vertical
2698             if (an == 0) na = HUGE_VAL; else na = -1/an;
2699             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
2701             //g_print("an %g    ap %g\n", an, ap);
2703             // mouse point relative to the node's original pos
2704             pr = (*p) - n->origin;
2706             // distances to the four lines (two handles and two perpendiculars)
2707             d_an = point_line_distance(&pr, an);
2708             d_na = point_line_distance(&pr, na);
2709             d_ap = point_line_distance(&pr, ap);
2710             d_pa = point_line_distance(&pr, pa);
2712             // find out which line is the closest, save its closest point in c
2713             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
2714                 point_line_closest(&pr, an, &c);
2715             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
2716                 point_line_closest(&pr, ap, &c);
2717             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
2718                 point_line_closest(&pr, na, &c);
2719             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
2720                 point_line_closest(&pr, pa, &c);
2721             }
2723             // move the node to the closest point
2724             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2725                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
2726                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
2728         } else {  // constraining to hor/vert
2730             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
2731                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
2732             } else { // snap to vert
2733                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
2734             }
2735         }
2736     } else { // move freely
2737         sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2738                                         (*p)[NR::X] - n->pos[NR::X],
2739                                         (*p)[NR::Y] - n->pos[NR::Y],
2740                                         (state & GDK_SHIFT_MASK) == 0);
2741     }
2743     n->subpath->nodepath->desktop->scroll_to_point(p);
2745     return TRUE;
2748 /**
2749  * Node handle clicked callback.
2750  */
2751 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
2753    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2755     if (state & GDK_CONTROL_MASK) { // "delete" handle
2756         if (n->p.knot == knot) {
2757             n->p.pos = n->pos;
2758         } else if (n->n.knot == knot) {
2759             n->n.pos = n->pos;
2760         }
2761         sp_node_update_handles(n);
2762         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2763         sp_nodepath_update_repr(nodepath);
2764         sp_nodepath_update_statusbar(nodepath);
2766     } else { // just select or add to selection, depending in Shift
2767         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2768     }
2771 /**
2772  * Node handle grabbed callback.
2773  */
2774 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
2776    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2778     if (!n->selected) {
2779         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2780     }
2782     // remember the origin point of the handle
2783     if (n->p.knot == knot) {
2784         n->p.origin = n->p.pos - n->pos;
2785     } else if (n->n.knot == knot) {
2786         n->n.origin = n->n.pos - n->pos;
2787     } else {
2788         g_assert_not_reached();
2789     }
2793 /**
2794  * Node handle ungrabbed callback.
2795  */
2796 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
2798    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2800     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
2801     if (n->p.knot == knot) {
2802         n->p.origin.a = 0;
2803         sp_knot_set_position(knot, &n->p.pos, state);
2804     } else if (n->n.knot == knot) {
2805         n->n.origin.a = 0;
2806         sp_knot_set_position(knot, &n->n.pos, state);
2807     } else {
2808         g_assert_not_reached();
2809     }
2811     sp_nodepath_update_repr(n->subpath->nodepath);
2814 /**
2815  * Node handle "request" signal callback.
2816  */
2817 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2819     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2821     Inkscape::NodePath::NodeSide *me, *opposite;
2822     gint which;
2823     if (n->p.knot == knot) {
2824         me = &n->p;
2825         opposite = &n->n;
2826         which = -1;
2827     } else if (n->n.knot == knot) {
2828         me = &n->n;
2829         opposite = &n->p;
2830         which = 1;
2831     } else {
2832         me = opposite = NULL;
2833         which = 0;
2834         g_assert_not_reached();
2835     }
2837     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
2839     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
2841     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
2842         /* We are smooth node adjacent with line */
2843         NR::Point const delta = *p - n->pos;
2844         NR::Coord const len = NR::L2(delta);
2845         Inkscape::NodePath::Node *othernode = opposite->other;
2846         NR::Point const ndelta = n->pos - othernode->pos;
2847         NR::Coord const linelen = NR::L2(ndelta);
2848         if (len > NR_EPSILON && linelen > NR_EPSILON) {
2849             NR::Coord const scal = dot(delta, ndelta) / linelen;
2850             (*p) = n->pos + (scal / linelen) * ndelta;
2851         }
2852         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
2853     } else {
2854         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
2855     }
2857     sp_node_adjust_handle(n, -which);
2859     return FALSE;
2862 /**
2863  * Node handle moved callback.
2864  */
2865 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2867    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2869    Inkscape::NodePath::NodeSide *me;
2870    Inkscape::NodePath::NodeSide *other;
2871     if (n->p.knot == knot) {
2872         me = &n->p;
2873         other = &n->n;
2874     } else if (n->n.knot == knot) {
2875         me = &n->n;
2876         other = &n->p;
2877     } else {
2878         me = NULL;
2879         other = NULL;
2880         g_assert_not_reached();
2881     }
2883     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
2884     Radial rme(me->pos - n->pos);
2885     Radial rother(other->pos - n->pos);
2886     Radial rnew(*p - n->pos);
2888     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
2889         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
2890         /* 0 interpreted as "no snapping". */
2892         // The closest PI/snaps angle, starting from zero.
2893         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
2894         if (me->origin.a == HUGE_VAL) {
2895             // ortho doesn't exist: original handle was zero length.
2896             rnew.a = a_snapped;
2897         } else {
2898             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
2899              * its opposite and perpendiculars). */
2900             double const a_ortho = me->origin.a + floor((rnew.a - me->origin.a)/(M_PI/2) + 0.5) * (M_PI/2);
2902             // Snap to the closest.
2903             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
2904                        ? a_snapped
2905                        : a_ortho );
2906         }
2907     }
2909     if (state & GDK_MOD1_MASK) {
2910         // lock handle length
2911         rnew.r = me->origin.r;
2912     }
2914     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
2915         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
2916         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
2917         rother.a += rnew.a - rme.a;
2918         other->pos = NR::Point(rother) + n->pos;
2919         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
2920         sp_knot_set_position(other->knot, &other->pos, 0);
2921     }
2923     me->pos = NR::Point(rnew) + n->pos;
2924     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
2926     // this is what sp_knot_set_position does, but without emitting the signal:
2927     // we cannot emit a "moved" signal because we're now processing it
2928     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
2930     knot->desktop->set_coordinate_status(me->pos);
2932     update_object(n->subpath->nodepath);
2934     /* status text */
2935     SPDesktop *desktop = n->subpath->nodepath->desktop;
2936     if (!desktop) return;
2937     SPEventContext *ec = desktop->event_context;
2938     if (!ec) return;
2939     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
2940     if (!mc) return;
2942     double degrees = 180 / M_PI * rnew.a;
2943     if (degrees > 180) degrees -= 360;
2944     if (degrees < -180) degrees += 360;
2945     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
2946         degrees = angle_to_compass (degrees);
2948     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
2950     mc->setF(Inkscape::NORMAL_MESSAGE,
2951          _("<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);
2953     g_string_free(length, TRUE);
2956 /**
2957  * Node handle event callback.
2958  */
2959 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2961     gboolean ret = FALSE;
2962     switch (event->type) {
2963         case GDK_KEY_PRESS:
2964             switch (get_group0_keyval (&event->key)) {
2965                 case GDK_space:
2966                     if (event->key.state & GDK_BUTTON1_MASK) {
2967                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2968                         stamp_repr(nodepath);
2969                         ret = TRUE;
2970                     }
2971                     break;
2972                 default:
2973                     break;
2974             }
2975             break;
2976         default:
2977             break;
2978     }
2980     return ret;
2983 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
2984                                  Radial &rme, Radial &rother, gboolean const both)
2986     rme.a += angle;
2987     if ( both
2988          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2989          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2990     {
2991         rother.a += angle;
2992     }
2995 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
2996                                         Radial &rme, Radial &rother, gboolean const both)
2998     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3000     gdouble r;
3001     if ( both
3002          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3003          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3004     {
3005         r = MAX(rme.r, rother.r);
3006     } else {
3007         r = rme.r;
3008     }
3010     gdouble const weird_angle = atan2(norm_angle, r);
3011 /* Bulia says norm_angle is just the visible distance that the
3012  * object's end must travel on the screen.  Left as 'angle' for want of
3013  * a better name.*/
3015     rme.a += weird_angle;
3016     if ( both
3017          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3018          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3019     {
3020         rother.a += weird_angle;
3021     }
3024 /**
3025  * Rotate one node.
3026  */
3027 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3029     Inkscape::NodePath::NodeSide *me, *other;
3030     bool both = false;
3032     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3033     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3035     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3036         me = &(n->p);
3037         other = &(n->n);
3038     } else if (!n->p.other) {
3039         me = &(n->n);
3040         other = &(n->p);
3041     } else {
3042         if (which > 0) { // right handle
3043             if (xn > xp) {
3044                 me = &(n->n);
3045                 other = &(n->p);
3046             } else {
3047                 me = &(n->p);
3048                 other = &(n->n);
3049             }
3050         } else if (which < 0){ // left handle
3051             if (xn <= xp) {
3052                 me = &(n->n);
3053                 other = &(n->p);
3054             } else {
3055                 me = &(n->p);
3056                 other = &(n->n);
3057             }
3058         } else { // both handles
3059             me = &(n->n);
3060             other = &(n->p);
3061             both = true;
3062         }
3063     }
3065     Radial rme(me->pos - n->pos);
3066     Radial rother(other->pos - n->pos);
3068     if (screen) {
3069         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3070     } else {
3071         node_rotate_one_internal (*n, angle, rme, rother, both);
3072     }
3074     me->pos = n->pos + NR::Point(rme);
3076     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3077         other->pos =  n->pos + NR::Point(rother);
3078     }
3080     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3081     // so here we just move all the knots without emitting move signals, for speed
3082     sp_node_update_handles(n, false);
3085 /**
3086  * Rotate selected nodes.
3087  */
3088 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3090     if (!nodepath || !nodepath->selected) return;
3092     if (g_list_length(nodepath->selected) == 1) {
3093        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3094         node_rotate_one (n, angle, which, screen);
3095     } else {
3096        // rotate as an object:
3098         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3099         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3100         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3101             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3102             box.expandTo (n->pos); // contain all selected nodes
3103         }
3105         gdouble rot;
3106         if (screen) {
3107             gdouble const zoom = nodepath->desktop->current_zoom();
3108             gdouble const zmove = angle / zoom;
3109             gdouble const r = NR::L2(box.max() - box.midpoint());
3110             rot = atan2(zmove, r);
3111         } else {
3112             rot = angle;
3113         }
3115         NR::Matrix t =
3116             NR::Matrix (NR::translate(-box.midpoint())) *
3117             NR::Matrix (NR::rotate(rot)) *
3118             NR::Matrix (NR::translate(box.midpoint()));
3120         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3121             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3122             n->pos *= t;
3123             n->n.pos *= t;
3124             n->p.pos *= t;
3125             sp_node_update_handles(n, false);
3126         }
3127     }
3129     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n");
3132 /**
3133  * Scale one node.
3134  */
3135 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3137     bool both = false;
3138     Inkscape::NodePath::NodeSide *me, *other;
3140     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3141     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3143     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3144         me = &(n->p);
3145         other = &(n->n);
3146         n->code = NR_CURVETO;
3147     } else if (!n->p.other) {
3148         me = &(n->n);
3149         other = &(n->p);
3150         if (n->n.other)
3151             n->n.other->code = NR_CURVETO;
3152     } else {
3153         if (which > 0) { // right handle
3154             if (xn > xp) {
3155                 me = &(n->n);
3156                 other = &(n->p);
3157                 if (n->n.other)
3158                     n->n.other->code = NR_CURVETO;
3159             } else {
3160                 me = &(n->p);
3161                 other = &(n->n);
3162                 n->code = NR_CURVETO;
3163             }
3164         } else if (which < 0){ // left handle
3165             if (xn <= xp) {
3166                 me = &(n->n);
3167                 other = &(n->p);
3168                 if (n->n.other)
3169                     n->n.other->code = NR_CURVETO;
3170             } else {
3171                 me = &(n->p);
3172                 other = &(n->n);
3173                 n->code = NR_CURVETO;
3174             }
3175         } else { // both handles
3176             me = &(n->n);
3177             other = &(n->p);
3178             both = true;
3179             n->code = NR_CURVETO;
3180             if (n->n.other)
3181                 n->n.other->code = NR_CURVETO;
3182         }
3183     }
3185     Radial rme(me->pos - n->pos);
3186     Radial rother(other->pos - n->pos);
3188     rme.r += grow;
3189     if (rme.r < 0) rme.r = 0;
3190     if (rme.a == HUGE_VAL) {
3191         if (me->other) { // if direction is unknown, initialize it towards the next node
3192             Radial rme_next(me->other->pos - n->pos);
3193             rme.a = rme_next.a;
3194         } else { // if there's no next, initialize to 0
3195             rme.a = 0;
3196         }
3197     }
3198     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3199         rother.r += grow;
3200         if (rother.r < 0) rother.r = 0;
3201         if (rother.a == HUGE_VAL) {
3202             rother.a = rme.a + M_PI;
3203         }
3204     }
3206     me->pos = n->pos + NR::Point(rme);
3208     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3209         other->pos = n->pos + NR::Point(rother);
3210     }
3212     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3213     // so here we just move all the knots without emitting move signals, for speed
3214     sp_node_update_handles(n, false);
3217 /**
3218  * Scale selected nodes.
3219  */
3220 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3222     if (!nodepath || !nodepath->selected) return;
3224     if (g_list_length(nodepath->selected) == 1) {
3225         // scale handles of the single selected node
3226         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3227         node_scale_one (n, grow, which);
3228     } else {
3229         // scale nodes as an "object":
3231         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3232         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3233         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3234             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3235             box.expandTo (n->pos); // contain all selected nodes
3236         }
3238         double scale = (box.maxExtent() + grow)/box.maxExtent();
3240         NR::Matrix t =
3241             NR::Matrix (NR::translate(-box.midpoint())) *
3242             NR::Matrix (NR::scale(scale, scale)) *
3243             NR::Matrix (NR::translate(box.midpoint()));
3245         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3246             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3247             n->pos *= t;
3248             n->n.pos *= t;
3249             n->p.pos *= t;
3250             sp_node_update_handles(n, false);
3251         }
3252     }
3254     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n");
3257 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3259     if (!nodepath) return;
3260     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3263 /**
3264  * Flip selected nodes horizontally/vertically.
3265  */
3266 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3268     if (!nodepath || !nodepath->selected) return;
3270     if (g_list_length(nodepath->selected) == 1) {
3271         // flip handles of the single selected node
3272         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3273         double temp = n->p.pos[axis];
3274         n->p.pos[axis] = n->n.pos[axis];
3275         n->n.pos[axis] = temp;
3276         sp_node_update_handles(n, false);
3277     } else {
3278         // scale nodes as an "object":
3280         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3281         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3282         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3283             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3284             box.expandTo (n->pos); // contain all selected nodes
3285         }
3287         NR::Matrix t =
3288             NR::Matrix (NR::translate(-box.midpoint())) *
3289             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3290             NR::Matrix (NR::translate(box.midpoint()));
3292         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3293             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3294             n->pos *= t;
3295             n->n.pos *= t;
3296             n->p.pos *= t;
3297             sp_node_update_handles(n, false);
3298         }
3299     }
3301     sp_nodepath_update_repr(nodepath);
3304 //-----------------------------------------------
3305 /**
3306  * Return new subpath under given nodepath.
3307  */
3308 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3310     g_assert(nodepath);
3311     g_assert(nodepath->desktop);
3313    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3315     s->nodepath = nodepath;
3316     s->closed = FALSE;
3317     s->nodes = NULL;
3318     s->first = NULL;
3319     s->last = NULL;
3321     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3322     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3323     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3325     return s;
3328 /**
3329  * Destroy nodes in subpath, then subpath itself.
3330  */
3331 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3333     g_assert(subpath);
3334     g_assert(subpath->nodepath);
3335     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3337     while (subpath->nodes) {
3338         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3339     }
3341     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3343     g_free(subpath);
3346 /**
3347  * Link head to tail in subpath.
3348  */
3349 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3351     g_assert(!sp->closed);
3352     g_assert(sp->last != sp->first);
3353     g_assert(sp->first->code == NR_MOVETO);
3355     sp->closed = TRUE;
3357     //Link the head to the tail
3358     sp->first->p.other = sp->last;
3359     sp->last->n.other  = sp->first;
3360     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3361     sp->first          = sp->last;
3363     //Remove the extra end node
3364     sp_nodepath_node_destroy(sp->last->n.other);
3367 /**
3368  * Open closed (loopy) subpath at node.
3369  */
3370 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3372     g_assert(sp->closed);
3373     g_assert(n->subpath == sp);
3374     g_assert(sp->first == sp->last);
3376     /* We create new startpoint, current node will become last one */
3378    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3379                                                 &n->pos, &n->pos, &n->n.pos);
3382     sp->closed        = FALSE;
3384     //Unlink to make a head and tail
3385     sp->first         = new_path;
3386     sp->last          = n;
3387     n->n.other        = NULL;
3388     new_path->p.other = NULL;
3391 /**
3392  * Returns area in triangle given by points; may be negative.
3393  */
3394 inline double
3395 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3397     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]);
3400 /**
3401  * Return new node in subpath with given properties.
3402  * \param pos Position of node.
3403  * \param ppos Handle position in previous direction
3404  * \param npos Handle position in previous direction
3405  */
3406 Inkscape::NodePath::Node *
3407 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)
3409     g_assert(sp);
3410     g_assert(sp->nodepath);
3411     g_assert(sp->nodepath->desktop);
3413     if (nodechunk == NULL)
3414         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3416     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3418     n->subpath  = sp;
3420     if (type != Inkscape::NodePath::NODE_NONE) {
3421         // use the type from sodipodi:nodetypes
3422         n->type = type;
3423     } else {
3424         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3425             // points are (almost) collinear
3426             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3427                 // endnode, or a node with a retracted handle
3428                 n->type = Inkscape::NodePath::NODE_CUSP;
3429             } else {
3430                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3431             }
3432         } else {
3433             n->type = Inkscape::NodePath::NODE_CUSP;
3434         }
3435     }
3437     n->code     = code;
3438     n->selected = FALSE;
3439     n->pos      = *pos;
3440     n->p.pos    = *ppos;
3441     n->n.pos    = *npos;
3443     n->dragging_out = NULL;
3445     Inkscape::NodePath::Node *prev;
3446     if (next) {
3447         //g_assert(g_list_find(sp->nodes, next));
3448         prev = next->p.other;
3449     } else {
3450         prev = sp->last;
3451     }
3453     if (prev)
3454         prev->n.other = n;
3455     else
3456         sp->first = n;
3458     if (next)
3459         next->p.other = n;
3460     else
3461         sp->last = n;
3463     n->p.other = prev;
3464     n->n.other = next;
3466     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"));
3467     sp_knot_set_position(n->knot, pos, 0);
3469     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3470     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3471     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3472     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3473     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3474     sp_knot_update_ctrl(n->knot);
3476     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3477     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3478     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3479     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3480     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3481     sp_knot_show(n->knot);
3483     // We only create handle knots and lines on demand
3484     n->p.knot = NULL;
3485     n->p.line = NULL;
3486     n->n.knot = NULL;
3487     n->n.line = NULL;
3489     sp->nodes = g_list_prepend(sp->nodes, n);
3491     return n;
3494 /**
3495  * Destroy node and its knots, link neighbors in subpath.
3496  */
3497 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3499     g_assert(node);
3500     g_assert(node->subpath);
3501     g_assert(SP_IS_KNOT(node->knot));
3503    Inkscape::NodePath::SubPath *sp = node->subpath;
3505     if (node->selected) { // first, deselect
3506         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3507         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3508     }
3510     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3512     g_object_unref(G_OBJECT(node->knot));
3513     if (node->p.knot)
3514         g_object_unref(G_OBJECT(node->p.knot));
3515     if (node->n.knot)
3516         g_object_unref(G_OBJECT(node->n.knot));
3518     if (node->p.line)
3519         gtk_object_destroy(GTK_OBJECT(node->p.line));
3520     if (node->n.line)
3521         gtk_object_destroy(GTK_OBJECT(node->n.line));
3523     if (sp->nodes) { // there are others nodes on the subpath
3524         if (sp->closed) {
3525             if (sp->first == node) {
3526                 g_assert(sp->last == node);
3527                 sp->first = node->n.other;
3528                 sp->last = sp->first;
3529             }
3530             node->p.other->n.other = node->n.other;
3531             node->n.other->p.other = node->p.other;
3532         } else {
3533             if (sp->first == node) {
3534                 sp->first = node->n.other;
3535                 sp->first->code = NR_MOVETO;
3536             }
3537             if (sp->last == node) sp->last = node->p.other;
3538             if (node->p.other) node->p.other->n.other = node->n.other;
3539             if (node->n.other) node->n.other->p.other = node->p.other;
3540         }
3541     } else { // this was the last node on subpath
3542         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
3543     }
3545     g_mem_chunk_free(nodechunk, node);
3548 /**
3549  * Returns one of the node's two sides.
3550  * \param which Indicates which side.
3551  * \return Pointer to previous node side if which==-1, next if which==1.
3552  */
3553 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
3555     g_assert(node);
3557     switch (which) {
3558         case -1:
3559             return &node->p;
3560         case 1:
3561             return &node->n;
3562         default:
3563             break;
3564     }
3566     g_assert_not_reached();
3568     return NULL;
3571 /**
3572  * Return the other side of the node, given one of its sides.
3573  */
3574 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
3576     g_assert(node);
3578     if (me == &node->p) return &node->n;
3579     if (me == &node->n) return &node->p;
3581     g_assert_not_reached();
3583     return NULL;
3586 /**
3587  * Return NRPathcode on the given side of the node.
3588  */
3589 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
3591     g_assert(node);
3593     if (me == &node->p) {
3594         if (node->p.other) return (NRPathcode)node->code;
3595         return NR_MOVETO;
3596     }
3598     if (me == &node->n) {
3599         if (node->n.other) return (NRPathcode)node->n.other->code;
3600         return NR_MOVETO;
3601     }
3603     g_assert_not_reached();
3605     return NR_END;
3608 /**
3609  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
3610  */
3611 Inkscape::NodePath::Node *
3612 sp_nodepath_get_node_by_index(int index)
3614     Inkscape::NodePath::Node *e = NULL;
3616     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
3617     if (!nodepath) {
3618         return e;
3619     }
3621     //find segment
3622     for (GList *l = nodepath->subpaths; l ; l=l->next) {
3624         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
3625         int n = g_list_length(sp->nodes);
3626         if (sp->closed) {
3627             n++;
3628         }
3630         //if the piece belongs to this subpath grab it
3631         //otherwise move onto the next subpath
3632         if (index < n) {
3633             e = sp->first;
3634             for (int i = 0; i < index; ++i) {
3635                 e = e->n.other;
3636             }
3637             break;
3638         } else {
3639             if (sp->closed) {
3640                 index -= (n+1);
3641             } else {
3642                 index -= n;
3643             }
3644         }
3645     }
3647     return e;
3650 /**
3651  * Returns plain text meaning of node type.
3652  */
3653 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
3655     unsigned retracted = 0;
3656     bool endnode = false;
3658     for (int which = -1; which <= 1; which += 2) {
3659         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
3660         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
3661             retracted ++;
3662         if (!side->other)
3663             endnode = true;
3664     }
3666     if (retracted == 0) {
3667         if (endnode) {
3668                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
3669                 return _("end node");
3670         } else {
3671             switch (node->type) {
3672                 case Inkscape::NodePath::NODE_CUSP:
3673                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
3674                     return _("cusp");
3675                 case Inkscape::NodePath::NODE_SMOOTH:
3676                     // TRANSLATORS: "smooth" is an adjective here
3677                     return _("smooth");
3678                 case Inkscape::NodePath::NODE_SYMM:
3679                     return _("symmetric");
3680             }
3681         }
3682     } else if (retracted == 1) {
3683         if (endnode) {
3684             // TRANSLATORS: "end" is an adjective here (NOT a verb)
3685             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
3686         } else {
3687             return _("one handle retracted (drag with <b>Shift</b> to extend)");
3688         }
3689     } else {
3690         return _("both handles retracted (drag with <b>Shift</b> to extend)");
3691     }
3693     return NULL;
3696 /**
3697  * Handles content of statusbar as long as node tool is active.
3698  */
3699 void
3700 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
3702     gchar const *when_selected = _("<b>Drag</b> nodes or node handles; <b>arrow</b> keys to move nodes");
3703     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
3705     gint total = 0;
3706     gint selected = 0;
3707     SPDesktop *desktop = NULL;
3709     if (nodepath) {
3710         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3711             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3712             total += g_list_length(subpath->nodes);
3713         }
3714         selected = g_list_length(nodepath->selected);
3715         desktop = nodepath->desktop;
3716     } else {
3717         desktop = SP_ACTIVE_DESKTOP;
3718     }
3720     SPEventContext *ec = desktop->event_context;
3721     if (!ec) return;
3722     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3723     if (!mc) return;
3725     if (selected == 0) {
3726         Inkscape::Selection *sel = desktop->selection;
3727         if (!sel || sel->isEmpty()) {
3728             mc->setF(Inkscape::NORMAL_MESSAGE,
3729                      _("Select a single object to edit its nodes or handles."));
3730         } else {
3731             if (nodepath) {
3732             mc->setF(Inkscape::NORMAL_MESSAGE,
3733                      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.",
3734                               "<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.",
3735                               total),
3736                      total);
3737             } else {
3738                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
3739                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
3740                 } else {
3741                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
3742                 }
3743             }
3744         }
3745     } else if (nodepath && selected == 1) {
3746         mc->setF(Inkscape::NORMAL_MESSAGE,
3747                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
3748                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
3749                           total),
3750                  selected, total, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
3751     } else {
3752         mc->setF(Inkscape::NORMAL_MESSAGE,
3753                  ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
3754                           "<b>%i</b> of <b>%i</b> nodes selected. %s.",
3755                           total),
3756                  selected, total, when_selected);
3757     }
3761 /*
3762   Local Variables:
3763   mode:c++
3764   c-file-style:"stroustrup"
3765   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
3766   indent-tabs-mode:nil
3767   fill-column:99
3768   End:
3769 */
3770 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :