Code

1b67944d8c64b00980ed5e391749a759e6399740
[inkscape.git] / src / nodepath.cpp
1 #define __SP_NODEPATH_C__
3 /** \file
4  * Path handler in node edit mode
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *
10  * Portions of this code are in public domain; node sculpting functions written by bulia byak are under GNU GPL
11  */
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
17 #include <gdk/gdkkeysyms.h>
18 #include "display/curve.h"
19 #include "display/sp-ctrlline.h"
20 #include "display/sodipodi-ctrl.h"
21 #include <glibmm/i18n.h>
22 #include "libnr/n-art-bpath.h"
23 #include "helper/units.h"
24 #include "knot.h"
25 #include "inkscape.h"
26 #include "document.h"
27 #include "sp-namedview.h"
28 #include "desktop.h"
29 #include "desktop-handles.h"
30 #include "snap.h"
31 #include "message-stack.h"
32 #include "message-context.h"
33 #include "node-context.h"
34 #include "shape-editor.h"
35 #include "selection-chemistry.h"
36 #include "selection.h"
37 #include "xml/repr.h"
38 #include "prefs-utils.h"
39 #include "sp-metrics.h"
40 #include "sp-path.h"
41 #include "libnr/nr-matrix-ops.h"
42 #include "splivarot.h"
43 #include "svg/svg.h"
44 #include "verbs.h"
45 #include "display/bezier-utils.h"
46 #include <vector>
47 #include <algorithm>
49 class NR::Matrix;
51 /// \todo
52 /// evil evil evil. FIXME: conflict of two different Path classes!
53 /// There is a conflict in the namespace between two classes named Path.
54 /// #include "sp-flowtext.h"
55 /// #include "sp-flowregion.h"
57 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
58 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
59 GType sp_flowregion_get_type (void);
60 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
61 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
62 GType sp_flowtext_get_type (void);
63 // end evil workaround
65 #include "helper/stlport.h"
68 /// \todo fixme: Implement these via preferences */
70 #define NODE_FILL          0xbfbfbf00
71 #define NODE_STROKE        0x000000ff
72 #define NODE_FILL_HI       0xff000000
73 #define NODE_STROKE_HI     0x000000ff
74 #define NODE_FILL_SEL      0x0000ffff
75 #define NODE_STROKE_SEL    0x000000ff
76 #define NODE_FILL_SEL_HI   0xff000000
77 #define NODE_STROKE_SEL_HI 0x000000ff
78 #define KNOT_FILL          0xffffffff
79 #define KNOT_STROKE        0x000000ff
80 #define KNOT_FILL_HI       0xff000000
81 #define KNOT_STROKE_HI     0x000000ff
83 static GMemChunk *nodechunk = NULL;
85 /* Creation from object */
87 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
88 static gchar *parse_nodetypes(gchar const *types, gint length);
90 /* Object updating */
92 static void stamp_repr(Inkscape::NodePath::Path *np);
93 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
94 static gchar *create_typestr(Inkscape::NodePath::Path *np);
96 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
98 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
100 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
102 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
104 /* Adjust handle placement, if the node or the other handle is moved */
105 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
106 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
108 /* Node event callbacks */
109 static void node_clicked(SPKnot *knot, guint state, gpointer data);
110 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
111 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
112 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
114 /* Handle event callbacks */
115 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
116 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
117 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
118 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
119 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
120 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
122 /* Constructors and destructors */
124 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
125 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
126 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
127 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
128 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
129                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
130 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
132 /* Helpers */
134 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
135 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
136 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
138 // active_node indicates mouseover node
139 static Inkscape::NodePath::Node *active_node = NULL;
141 /**
142  * \brief Creates new nodepath from item
143  */
144 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPItem *item, bool show_handles)
146     Inkscape::XML::Node *repr = SP_OBJECT(item)->repr;
148     /** \todo
149      * FIXME: remove this. We don't want to edit paths inside flowtext.
150      * Instead we will build our flowtext with cloned paths, so that the
151      * real paths are outside the flowtext and thus editable as usual.
152      */
153     if (SP_IS_FLOWTEXT(item)) {
154         for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
155             if SP_IS_FLOWREGION(child) {
156                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
157                 if (grandchild && SP_IS_PATH(grandchild)) {
158                     item = SP_ITEM(grandchild);
159                     break;
160                 }
161             }
162         }
163     }
165     if (!SP_IS_PATH(item))
166         return NULL;
167     SPPath *path = SP_PATH(item);
168     SPCurve *curve = sp_shape_get_curve(SP_SHAPE(path));
169     if (curve == NULL)
170         return NULL;
172     NArtBpath *bpath = sp_curve_first_bpath(curve);
173     gint length = curve->end;
174     if (length == 0)
175         return NULL; // prevent crash for one-node paths
177     gchar const *nodetypes = repr->attribute("sodipodi:nodetypes");
178     gchar *typestr = parse_nodetypes(nodetypes, length);
180     //Create new nodepath
181     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
182     if (!np)
183         return NULL;
185     // Set defaults
186     np->desktop     = desktop;
187     np->path        = path;
188     np->subpaths    = NULL;
189     np->selected    = NULL;
190     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
191     np->livarot_path = NULL;
192     np->local_change = 0;
193     np->show_handles = show_handles;
195     // we need to update item's transform from the repr here,
196     // because they may be out of sync when we respond
197     // to a change in repr by regenerating nodepath     --bb
198     sp_object_read_attr(SP_OBJECT(item), "transform");
200     np->i2d  = sp_item_i2d_affine(SP_ITEM(path));
201     np->d2i  = np->i2d.inverse();
202     np->repr = repr;
204     // create the subpath(s) from the bpath
205     NArtBpath *b = bpath;
206     while (b->code != NR_END) {
207         b = subpath_from_bpath(np, b, typestr + (b - bpath));
208     }
210     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
211     np->subpaths = g_list_reverse(np->subpaths);
213     g_free(typestr);
214     sp_curve_unref(curve);
216     // create the livarot representation from the same item
217     sp_nodepath_ensure_livarot_path(np);
219     return np;
222 /**
223  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
224  */
225 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
227     if (!np)  //soft fail, like delete
228         return;
230     while (np->subpaths) {
231         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
232     }
234     //Inform the ShapeEditor that made me, if any, that I am gone.
235     if (np->shape_editor)
236         np->shape_editor->nodepath_destroyed();
238     g_assert(!np->selected);
240     if (np->livarot_path) {
241         delete np->livarot_path;
242         np->livarot_path = NULL;
243     }
245     np->desktop = NULL;
247     g_free(np);
251 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
253     if (np && np->livarot_path == NULL && np->path && SP_IS_ITEM(np->path)) {
254         np->livarot_path = Path_for_item (np->path, true, true);
255         if (np->livarot_path)
256             np->livarot_path->ConvertWithBackData(0.01);
257     }
261 /**
262  *  Return the node count of a given NodeSubPath.
263  */
264 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
266     if (!subpath)
267         return 0;
268     gint nodeCount = g_list_length(subpath->nodes);
269     return nodeCount;
272 /**
273  *  Return the node count of a given NodePath.
274  */
275 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
277     if (!np)
278         return 0;
279     gint nodeCount = 0;
280     for (GList *item = np->subpaths ; item ; item=item->next) {
281        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
282         nodeCount += g_list_length(subpath->nodes);
283     }
284     return nodeCount;
287 /**
288  *  Return the subpath count of a given NodePath.
289  */
290 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
292     if (!np)
293         return 0;
294     return g_list_length (np->subpaths);
297 /**
298  *  Return the selected node count of a given NodePath.
299  */
300 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
302     if (!np)
303         return 0;
304     return g_list_length (np->selected);
307 /**
308  *  Return the number of subpaths where nodes are selected in a given NodePath.
309  */
310 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
312     if (!np)
313         return 0;
314     if (!np->selected)
315         return 0;
316     if (!np->selected->next)
317         return 1;
318     gint count = 0;
319     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
320         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
321         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
322             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
323             if (node->selected) {
324                 count ++;
325                 break;
326             }
327         }
328     }
329     return count;
331  
332 /**
333  * Clean up a nodepath after editing.
334  *
335  * Currently we are deleting trivial subpaths.
336  */
337 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
339     GList *badSubPaths = NULL;
341     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
342     for (GList *l = nodepath->subpaths; l ; l=l->next) {
343        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
344        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
345             badSubPaths = g_list_append(badSubPaths, sp);
346     }
348     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
349     //also removes the subpath from nodepath->subpaths
350     for (GList *l = badSubPaths; l ; l=l->next) {
351        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
352         sp_nodepath_subpath_destroy(sp);
353     }
355     g_list_free(badSubPaths);
358 /**
359  * Create new nodepath from b, make it subpath of np.
360  * \param t The node type.
361  * \todo Fixme: t should be a proper type, rather than gchar
362  */
363 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
365     NR::Point ppos, pos, npos;
367     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
369     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
370     bool const closed = (b->code == NR_MOVETO);
372     pos = NR::Point(b->x3, b->y3) * np->i2d;
373     if (b[1].code == NR_CURVETO) {
374         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
375     } else {
376         npos = pos;
377     }
378     Inkscape::NodePath::Node *n;
379     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
380     g_assert(sp->first == n);
381     g_assert(sp->last  == n);
383     b++;
384     t++;
385     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
386         pos = NR::Point(b->x3, b->y3) * np->i2d;
387         if (b->code == NR_CURVETO) {
388             ppos = NR::Point(b->x2, b->y2) * np->i2d;
389         } else {
390             ppos = pos;
391         }
392         if (b[1].code == NR_CURVETO) {
393             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
394         } else {
395             npos = pos;
396         }
397         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
398         b++;
399         t++;
400     }
402     if (closed) sp_nodepath_subpath_close(sp);
404     return b;
407 /**
408  * Convert from sodipodi:nodetypes to new style type string.
409  */
410 static gchar *parse_nodetypes(gchar const *types, gint length)
412     g_assert(length > 0);
414     gchar *typestr = g_new(gchar, length + 1);
416     gint pos = 0;
418     if (types) {
419         for (gint i = 0; types[i] && ( i < length ); i++) {
420             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
421             if (types[i] != '\0') {
422                 switch (types[i]) {
423                     case 's':
424                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
425                         break;
426                     case 'z':
427                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
428                         break;
429                     case 'c':
430                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
431                         break;
432                     default:
433                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
434                         break;
435                 }
436             }
437         }
438     }
440     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
442     return typestr;
445 /**
446  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
447  * updated but repr is not (for speed). Used during curve and node drag.
448  */
449 static void update_object(Inkscape::NodePath::Path *np)
451     g_assert(np);
453     SPCurve *curve = create_curve(np);
455     sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
457     sp_curve_unref(curve);
460 /**
461  * Update XML path node with data from path object.
462  */
463 static void update_repr_internal(Inkscape::NodePath::Path *np)
465     g_assert(np);
467     Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
469     SPCurve *curve = create_curve(np);
470     gchar *typestr = create_typestr(np);
471     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
473     if (repr->attribute("d") == NULL || strcmp(svgpath, repr->attribute("d"))) { // d changed
474         np->local_change++;
475         repr->setAttribute("d", svgpath);
476     }
478     if (repr->attribute("sodipodi:nodetypes") == NULL || strcmp(typestr, repr->attribute("sodipodi:nodetypes"))) { // nodetypes changed
479         np->local_change++;
480         repr->setAttribute("sodipodi:nodetypes", typestr);
481     }
483     g_free(svgpath);
484     g_free(typestr);
485     sp_curve_unref(curve);
488 /**
489  * Update XML path node with data from path object, commit changes forever.
490  */
491 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
493     //fixme: np can be NULL, so check before proceeding
494     g_return_if_fail(np != NULL);
496     if (np->livarot_path) {
497         delete np->livarot_path;
498         np->livarot_path = NULL;
499     }
501     update_repr_internal(np);
502     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
503     
504     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE, 
505                      annotation);
508 /**
509  * Update XML path node with data from path object, commit changes with undo.
510  */
511 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
513     if (np->livarot_path) {
514         delete np->livarot_path;
515         np->livarot_path = NULL;
516     }
518     update_repr_internal(np);
519     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE, 
520                            annotation);
523 /**
524  * Make duplicate of path, replace corresponding XML node in tree, commit.
525  */
526 static void stamp_repr(Inkscape::NodePath::Path *np)
528     g_assert(np);
530     Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
531     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
533     // remember the position of the item
534     gint pos = old_repr->position();
535     // remember parent
536     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
538     SPCurve *curve = create_curve(np);
539     gchar *typestr = create_typestr(np);
541     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
543     new_repr->setAttribute("d", svgpath);
544     new_repr->setAttribute("sodipodi:nodetypes", typestr);
546     // add the new repr to the parent
547     parent->appendChild(new_repr);
548     // move to the saved position
549     new_repr->setPosition(pos > 0 ? pos : 0);
551     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
552                      _("Stamp"));
554     Inkscape::GC::release(new_repr);
555     g_free(svgpath);
556     g_free(typestr);
557     sp_curve_unref(curve);
560 /**
561  * Create curve from path.
562  */
563 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
565     SPCurve *curve = sp_curve_new();
567     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
568        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
569         sp_curve_moveto(curve,
570                         sp->first->pos * np->d2i);
571        Inkscape::NodePath::Node *n = sp->first->n.other;
572         while (n) {
573             NR::Point const end_pt = n->pos * np->d2i;
574             switch (n->code) {
575                 case NR_LINETO:
576                     sp_curve_lineto(curve, end_pt);
577                     break;
578                 case NR_CURVETO:
579                     sp_curve_curveto(curve,
580                                      n->p.other->n.pos * np->d2i,
581                                      n->p.pos * np->d2i,
582                                      end_pt);
583                     break;
584                 default:
585                     g_assert_not_reached();
586                     break;
587             }
588             if (n != sp->last) {
589                 n = n->n.other;
590             } else {
591                 n = NULL;
592             }
593         }
594         if (sp->closed) {
595             sp_curve_closepath(curve);
596         }
597     }
599     return curve;
602 /**
603  * Convert path type string to sodipodi:nodetypes style.
604  */
605 static gchar *create_typestr(Inkscape::NodePath::Path *np)
607     gchar *typestr = g_new(gchar, 32);
608     gint len = 32;
609     gint pos = 0;
611     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
612        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
614         if (pos >= len) {
615             typestr = g_renew(gchar, typestr, len + 32);
616             len += 32;
617         }
619         typestr[pos++] = 'c';
621        Inkscape::NodePath::Node *n;
622         n = sp->first->n.other;
623         while (n) {
624             gchar code;
626             switch (n->type) {
627                 case Inkscape::NodePath::NODE_CUSP:
628                     code = 'c';
629                     break;
630                 case Inkscape::NodePath::NODE_SMOOTH:
631                     code = 's';
632                     break;
633                 case Inkscape::NodePath::NODE_SYMM:
634                     code = 'z';
635                     break;
636                 default:
637                     g_assert_not_reached();
638                     code = '\0';
639                     break;
640             }
642             if (pos >= len) {
643                 typestr = g_renew(gchar, typestr, len + 32);
644                 len += 32;
645             }
647             typestr[pos++] = code;
649             if (n != sp->last) {
650                 n = n->n.other;
651             } else {
652                 n = NULL;
653             }
654         }
655     }
657     if (pos >= len) {
658         typestr = g_renew(gchar, typestr, len + 1);
659         len += 1;
660     }
662     typestr[pos++] = '\0';
664     return typestr;
667 /**
668  * Returns current path in context. // later eliminate this function at all!
669  */
670 static Inkscape::NodePath::Path *sp_nodepath_current()
672     if (!SP_ACTIVE_DESKTOP) {
673         return NULL;
674     }
676     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
678     if (!SP_IS_NODE_CONTEXT(event_context)) {
679         return NULL;
680     }
682     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
687 /**
688  \brief Fills node and handle positions for three nodes, splitting line
689   marked by end at distance t.
690  */
691 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
693     g_assert(new_path != NULL);
694     g_assert(end      != NULL);
696     g_assert(end->p.other == new_path);
697    Inkscape::NodePath::Node *start = new_path->p.other;
698     g_assert(start);
700     if (end->code == NR_LINETO) {
701         new_path->type =Inkscape::NodePath::NODE_CUSP;
702         new_path->code = NR_LINETO;
703         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
704     } else {
705         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
706         new_path->code = NR_CURVETO;
707         gdouble s      = 1 - t;
708         for (int dim = 0; dim < 2; dim++) {
709             NR::Coord const f000 = start->pos[dim];
710             NR::Coord const f001 = start->n.pos[dim];
711             NR::Coord const f011 = end->p.pos[dim];
712             NR::Coord const f111 = end->pos[dim];
713             NR::Coord const f00t = s * f000 + t * f001;
714             NR::Coord const f01t = s * f001 + t * f011;
715             NR::Coord const f11t = s * f011 + t * f111;
716             NR::Coord const f0tt = s * f00t + t * f01t;
717             NR::Coord const f1tt = s * f01t + t * f11t;
718             NR::Coord const fttt = s * f0tt + t * f1tt;
719             start->n.pos[dim]    = f00t;
720             new_path->p.pos[dim] = f0tt;
721             new_path->pos[dim]   = fttt;
722             new_path->n.pos[dim] = f1tt;
723             end->p.pos[dim]      = f11t;
724         }
725     }
728 /**
729  * Adds new node on direct line between two nodes, activates handles of all
730  * three nodes.
731  */
732 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
734     g_assert(end);
735     g_assert(end->subpath);
736     g_assert(g_list_find(end->subpath->nodes, end));
738    Inkscape::NodePath::Node *start = end->p.other;
739     g_assert( start->n.other == end );
740    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
741                                                end,
742                                                (NRPathcode)end->code == NR_LINETO? 
743                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
744                                                (NRPathcode)end->code,
745                                                &start->pos, &start->pos, &start->n.pos);
746     sp_nodepath_line_midpoint(newnode, end, t);
748     sp_node_update_handles(start);
749     sp_node_update_handles(newnode);
750     sp_node_update_handles(end);
752     return newnode;
755 /**
756 \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
757 */
758 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
760     g_assert(node);
761     g_assert(node->subpath);
762     g_assert(g_list_find(node->subpath->nodes, node));
764    Inkscape::NodePath::SubPath *sp = node->subpath;
765     Inkscape::NodePath::Path *np    = sp->nodepath;
767     if (sp->closed) {
768         sp_nodepath_subpath_open(sp, node);
769         return sp->first;
770     } else {
771         // no break for end nodes
772         if (node == sp->first) return NULL;
773         if (node == sp->last ) return NULL;
775         // create a new subpath
776        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
778         // duplicate the break node as start of the new subpath
779        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
781         while (node->n.other) { // copy the remaining nodes into the new subpath
782            Inkscape::NodePath::Node *n  = node->n.other;
783            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);
784             if (n->selected) {
785                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
786             }
787             sp_nodepath_node_destroy(n); // remove the point on the original subpath
788         }
790         return newnode;
791     }
794 /**
795  * Duplicate node and connect to neighbours.
796  */
797 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
799     g_assert(node);
800     g_assert(node->subpath);
801     g_assert(g_list_find(node->subpath->nodes, node));
803    Inkscape::NodePath::SubPath *sp = node->subpath;
805     NRPathcode code = (NRPathcode) node->code;
806     if (code == NR_MOVETO) { // if node is the endnode,
807         node->code = NR_LINETO; // new one is inserted before it, so change that to line
808     }
810     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
812     if (!node->n.other || !node->p.other) // if node is an endnode, select it
813         return node;
814     else
815         return newnode; // otherwise select the newly created node
818 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
820     node->p.pos = (node->pos + (node->pos - node->n.pos));
823 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
825     node->n.pos = (node->pos + (node->pos - node->p.pos));
828 /**
829  * Change line type at node, with side effects on neighbours.
830  */
831 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
833     g_assert(end);
834     g_assert(end->subpath);
835     g_assert(end->p.other);
837     if (end->code == static_cast< guint > ( code ) )
838         return;
840    Inkscape::NodePath::Node *start = end->p.other;
842     end->code = code;
844     if (code == NR_LINETO) {
845         if (start->code == NR_LINETO) {
846             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
847         }
848         if (end->n.other) {
849             if (end->n.other->code == NR_LINETO) {
850                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
851             }
852         }
853     } else {
854         NR::Point delta = end->pos - start->pos;
855         start->n.pos = start->pos + delta / 3;
856         end->p.pos = end->pos - delta / 3;
857         sp_node_adjust_handle(start, 1);
858         sp_node_adjust_handle(end, -1);
859     }
861     sp_node_update_handles(start);
862     sp_node_update_handles(end);
865 /**
866  * Change node type, and its handles accordingly.
867  */
868 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
870     g_assert(node);
871     g_assert(node->subpath);
873     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
874         return node;
876     if ((node->p.other != NULL) && (node->n.other != NULL)) {
877         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
878             type =Inkscape::NodePath::NODE_CUSP;
879         }
880     }
882     node->type = type;
884     if (node->type == Inkscape::NodePath::NODE_CUSP) {
885         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
886         node->knot->setSize (node->selected? 11 : 9);
887         sp_knot_update_ctrl(node->knot);
888     } else {
889         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
890         node->knot->setSize (node->selected? 9 : 7);
891         sp_knot_update_ctrl(node->knot);
892     }
894     // if one of handles is mouseovered, preserve its position
895     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
896         sp_node_adjust_handle(node, 1);
897     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
898         sp_node_adjust_handle(node, -1);
899     } else {
900         sp_node_adjust_handles(node);
901     }
903     sp_node_update_handles(node);
905     sp_nodepath_update_statusbar(node->subpath->nodepath);
907     return node;
910 /**
911  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
912  * adjacent segments from lines to curves.
913 */
914 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
916     bool p_line = (node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos);
917     bool n_line = (node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos);
919     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
920         if (p_line && n_line) {
921             // only if both adjacent segments are lines, 
922             // convert both to curves:
924             // BEFORE:
925             {
926             node->code = NR_CURVETO;
927             NR::Point delta = node->n.other->pos - node->p.other->pos;
928             node->p.pos = node->pos - delta / 4;
929             }
931             // AFTER:
932             {
933             node->n.other->code = NR_CURVETO;
934             NR::Point delta = node->p.other->pos - node->n.other->pos;
935             node->n.pos = node->pos - delta / 4;
936             }
938             sp_node_update_handles(node);
939         }
940     }
942     sp_nodepath_set_node_type (node, type);
945 /**
946  * Move node to point, and adjust its and neighbouring handles.
947  */
948 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
950     NR::Point delta = p - node->pos;
951     node->pos = p;
953     node->p.pos += delta;
954     node->n.pos += delta;
956     Inkscape::NodePath::Node *node_p = NULL;
957     Inkscape::NodePath::Node *node_n = NULL;
959     if (node->p.other) {
960         if (node->code == NR_LINETO) {
961             sp_node_adjust_handle(node, 1);
962             sp_node_adjust_handle(node->p.other, -1);
963             node_p = node->p.other;
964         }
965     }
966     if (node->n.other) {
967         if (node->n.other->code == NR_LINETO) {
968             sp_node_adjust_handle(node, -1);
969             sp_node_adjust_handle(node->n.other, 1);
970             node_n = node->n.other;
971         }
972     }
974     // this function is only called from batch movers that will update display at the end
975     // themselves, so here we just move all the knots without emitting move signals, for speed
976     sp_node_update_handles(node, false);
977     if (node_n) {
978         sp_node_update_handles(node_n, false);
979     }
980     if (node_p) {
981         sp_node_update_handles(node_p, false);
982     }
985 /**
986  * Call sp_node_moveto() for node selection and handle possible snapping.
987  */
988 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
989                                             bool const snap = true)
991     NR::Coord best = NR_HUGE;
992     NR::Point delta(dx, dy);
993     NR::Point best_pt = delta;
995     if (snap) {
996         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
997         
998         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
999             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1000             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAP_POINT, n->pos + delta, NULL);
1001             if (s.getDistance() < best) {
1002                 best = s.getDistance();
1003                 best_pt = s.getPoint() - n->pos;
1004             }
1005         }
1006     }
1008     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1009         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1010         sp_node_moveto(n, n->pos + best_pt);
1011     }
1013     // do not update repr here so that node dragging is acceptably fast
1014     update_object(nodepath);
1017 /**
1018 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1019 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1020 near x = 0.
1021  */
1022 double
1023 sculpt_profile (double x, double alpha, guint profile)
1025     if (x >= 1)
1026         return 0;
1027     if (x <= 0)
1028         return 1;
1030     switch (profile) {
1031         case SCULPT_PROFILE_LINEAR:
1032         return 1 - x;
1033         case SCULPT_PROFILE_BELL:
1034         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1035         case SCULPT_PROFILE_ELLIPTIC:
1036         return sqrt(1 - x*x);
1037     }
1039     return 1;
1042 double
1043 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1045     // extremely primitive for now, don't have time to look for the real one
1046     double lower = NR::L2(b - a);
1047     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1048     return (lower + upper)/2;
1051 void
1052 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1054     n->pos = n->origin + delta;
1055     n->n.pos = n->n.origin + delta_n;
1056     n->p.pos = n->p.origin + delta_p;
1057     sp_node_adjust_handles(n);
1058     sp_node_update_handles(n, false);
1061 /**
1062  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1063  * on how far they are from the dragged node n.
1064  */
1065 static void 
1066 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1068     g_assert (n);
1069     g_assert (nodepath);
1070     g_assert (n->subpath->nodepath == nodepath);
1072     double pressure = n->knot->pressure;
1073     if (pressure == 0)
1074         pressure = 0.5; // default
1075     pressure = CLAMP (pressure, 0.2, 0.8);
1077     // map pressure to alpha = 1/5 ... 5
1078     double alpha = 1 - 2 * fabs(pressure - 0.5);
1079     if (pressure > 0.5)
1080         alpha = 1/alpha;
1082     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1084     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1085         // Only one subpath has selected nodes:
1086         // use linear mode, where the distance from n to node being dragged is calculated along the path
1088         double n_sel_range = 0, p_sel_range = 0;
1089         guint n_nodes = 0, p_nodes = 0;
1090         guint n_sel_nodes = 0, p_sel_nodes = 0;
1092         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1093         {
1094             double n_range = 0, p_range = 0;
1095             bool n_going = true, p_going = true;
1096             Inkscape::NodePath::Node *n_node = n;
1097             Inkscape::NodePath::Node *p_node = n;
1098             do {
1099                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1100                 if (n_node && n_going)
1101                     n_node = n_node->n.other;
1102                 if (n_node == NULL) {
1103                     n_going = false;
1104                 } else {
1105                     n_nodes ++;
1106                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1107                     if (n_node->selected) {
1108                         n_sel_nodes ++;
1109                         n_sel_range = n_range;
1110                     }
1111                     if (n_node == p_node) {
1112                         n_going = false;
1113                         p_going = false;
1114                     }
1115                 }
1116                 if (p_node && p_going)
1117                     p_node = p_node->p.other;
1118                 if (p_node == NULL) {
1119                     p_going = false;
1120                 } else {
1121                     p_nodes ++;
1122                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1123                     if (p_node->selected) {
1124                         p_sel_nodes ++;
1125                         p_sel_range = p_range;
1126                     }
1127                     if (p_node == n_node) {
1128                         n_going = false;
1129                         p_going = false;
1130                     }
1131                 }
1132             } while (n_going || p_going);
1133         }
1135         // Second pass: actually move nodes in this subpath
1136         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1137         {
1138             double n_range = 0, p_range = 0;
1139             bool n_going = true, p_going = true;
1140             Inkscape::NodePath::Node *n_node = n;
1141             Inkscape::NodePath::Node *p_node = n;
1142             do {
1143                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1144                 if (n_node && n_going)
1145                     n_node = n_node->n.other;
1146                 if (n_node == NULL) {
1147                     n_going = false;
1148                 } else {
1149                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1150                     if (n_node->selected) {
1151                         sp_nodepath_move_node_and_handles (n_node, 
1152                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1153                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1154                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1155                     }
1156                     if (n_node == p_node) {
1157                         n_going = false;
1158                         p_going = false;
1159                     }
1160                 }
1161                 if (p_node && p_going)
1162                     p_node = p_node->p.other;
1163                 if (p_node == NULL) {
1164                     p_going = false;
1165                 } else {
1166                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1167                     if (p_node->selected) {
1168                         sp_nodepath_move_node_and_handles (p_node, 
1169                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1170                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1171                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1172                     }
1173                     if (p_node == n_node) {
1174                         n_going = false;
1175                         p_going = false;
1176                     }
1177                 }
1178             } while (n_going || p_going);
1179         }
1181     } else {
1182         // Multiple subpaths have selected nodes:
1183         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1184         // TODO: correct these distances taking into account their angle relative to the bisector, so as to 
1185         // fix the pear-like shape when sculpting e.g. a ring
1187         // First pass: calculate range
1188         gdouble direct_range = 0;
1189         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1190             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1191             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1192                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1193                 if (node->selected) {
1194                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1195                 }
1196             }
1197         }
1199         // Second pass: actually move nodes
1200         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1201             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1202             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1203                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1204                 if (node->selected) {
1205                     if (direct_range > 1e-6) {
1206                         sp_nodepath_move_node_and_handles (node,
1207                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1208                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1209                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1210                     } else {
1211                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1212                     }
1214                 }
1215             }
1216         }
1217     }
1219     // do not update repr here so that node dragging is acceptably fast
1220     update_object(nodepath);
1224 /**
1225  * Move node selection to point, adjust its and neighbouring handles,
1226  * handle possible snapping, and commit the change with possible undo.
1227  */
1228 void
1229 sp_node_selected_move(gdouble dx, gdouble dy)
1231     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1232     if (!nodepath) return;
1234     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1236     if (dx == 0) {
1237         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1238     } else if (dy == 0) {
1239         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1240     } else {
1241         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1242     }
1245 /**
1246  * Move node selection off screen and commit the change.
1247  */
1248 void
1249 sp_node_selected_move_screen(gdouble dx, gdouble dy)
1251     // borrowed from sp_selection_move_screen in selection-chemistry.c
1252     // we find out the current zoom factor and divide deltas by it
1253     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1255     gdouble zoom = desktop->current_zoom();
1256     gdouble zdx = dx / zoom;
1257     gdouble zdy = dy / zoom;
1259     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1260     if (!nodepath) return;
1262     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1264     if (dx == 0) {
1265         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1266     } else if (dy == 0) {
1267         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1268     } else {
1269         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1270     }
1273 /** If they don't yet exist, creates knot and line for the given side of the node */
1274 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1276     if (!side->knot) {
1277         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"));
1279         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1280         side->knot->setSize (7);
1281         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1282         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1283         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1284         sp_knot_update_ctrl(side->knot);
1286         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1287         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1288         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1289         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1290         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1291         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1292     }
1294     if (!side->line) {
1295         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1296                                         SP_TYPE_CTRLLINE, NULL);
1297     }
1300 /**
1301  * Ensure the given handle of the node is visible/invisible, update its screen position
1302  */
1303 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1305     g_assert(node != NULL);
1307    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1308     NRPathcode code = sp_node_path_code_from_side(node, side);
1310     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1312     if (show_handle) {
1313         if (!side->knot) { // No handle knot at all
1314             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1315             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1316             side->knot->pos = side->pos;
1317             if (side->knot->item) 
1318                 SP_CTRL(side->knot->item)->moveto(side->pos);
1319             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1320             sp_knot_show(side->knot);
1321         } else {
1322             if (side->knot->pos != side->pos) { // only if it's really moved
1323                 if (fire_move_signals) {
1324                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1325                 } else {
1326                     sp_knot_moveto(side->knot, &side->pos);
1327                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1328                 }
1329             }
1330             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1331                 sp_knot_show(side->knot);
1332             }
1333         }
1334         sp_canvas_item_show(side->line);
1335     } else {
1336         if (side->knot) {
1337             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1338                 sp_knot_hide(side->knot);
1339             }
1340         }
1341         if (side->line) {
1342             sp_canvas_item_hide(side->line);
1343         }
1344     }
1347 /**
1348  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1349  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1350  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1351  * updated; otherwise, just move the knots silently (used in batch moves).
1352  */
1353 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1355     g_assert(node != NULL);
1357     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1358         sp_knot_show(node->knot);
1359     }
1361     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1362         if (fire_move_signals)
1363             sp_knot_set_position(node->knot, &node->pos, 0);
1364         else 
1365             sp_knot_moveto(node->knot, &node->pos);
1366     }
1368     gboolean show_handles = node->selected;
1369     if (node->p.other != NULL) {
1370         if (node->p.other->selected) show_handles = TRUE;
1371     }
1372     if (node->n.other != NULL) {
1373         if (node->n.other->selected) show_handles = TRUE;
1374     }
1376     if (node->subpath->nodepath->show_handles == false)
1377         show_handles = FALSE;
1379     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1380     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1383 /**
1384  * Call sp_node_update_handles() for all nodes on subpath.
1385  */
1386 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1388     g_assert(subpath != NULL);
1390     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1391         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1392     }
1395 /**
1396  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1397  */
1398 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1400     g_assert(nodepath != NULL);
1402     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1403         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1404     }
1407 void
1408 sp_nodepath_show_handles(bool show)
1410     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1411     if (nodepath == NULL) return;
1413     nodepath->show_handles = show;
1414     sp_nodepath_update_handles(nodepath);
1417 /**
1418  * Adds all selected nodes in nodepath to list.
1419  */
1420 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1422     StlConv<Node *>::list(l, selected);
1423 /// \todo this adds a copying, rework when the selection becomes a stl list
1426 /**
1427  * Align selected nodes on the specified axis.
1428  */
1429 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1431     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1432         return;
1433     }
1435     if ( !nodepath->selected->next ) { // only one node selected
1436         return;
1437     }
1438    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1439     NR::Point dest(pNode->pos);
1440     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1441         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1442         if (pNode) {
1443             dest[axis] = pNode->pos[axis];
1444             sp_node_moveto(pNode, dest);
1445         }
1446     }
1448     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1451 /// Helper struct.
1452 struct NodeSort
1454    Inkscape::NodePath::Node *_node;
1455     NR::Coord _coord;
1456     /// \todo use vectorof pointers instead of calling copy ctor
1457     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1458         _node(node), _coord(node->pos[axis])
1459     {}
1461 };
1463 static bool operator<(NodeSort const &a, NodeSort const &b)
1465     return (a._coord < b._coord);
1468 /**
1469  * Distribute selected nodes on the specified axis.
1470  */
1471 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1473     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1474         return;
1475     }
1477     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1478         return;
1479     }
1481    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1482     std::vector<NodeSort> sorted;
1483     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1484         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1485         if (pNode) {
1486             NodeSort n(pNode, axis);
1487             sorted.push_back(n);
1488             //dest[axis] = pNode->pos[axis];
1489             //sp_node_moveto(pNode, dest);
1490         }
1491     }
1492     std::sort(sorted.begin(), sorted.end());
1493     unsigned int len = sorted.size();
1494     //overall bboxes span
1495     float dist = (sorted.back()._coord -
1496                   sorted.front()._coord);
1497     //new distance between each bbox
1498     float step = (dist) / (len - 1);
1499     float pos = sorted.front()._coord;
1500     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1501           it < sorted.end();
1502           it ++ )
1503     {
1504         NR::Point dest((*it)._node->pos);
1505         dest[axis] = pos;
1506         sp_node_moveto((*it)._node, dest);
1507         pos += step;
1508     }
1510     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1514 /**
1515  * Call sp_nodepath_line_add_node() for all selected segments.
1516  */
1517 void
1518 sp_node_selected_add_node(void)
1520     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1521     if (!nodepath) {
1522         return;
1523     }
1525     GList *nl = NULL;
1527     int n_added = 0;
1529     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1530        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1531         g_assert(t->selected);
1532         if (t->p.other && t->p.other->selected) {
1533             nl = g_list_prepend(nl, t);
1534         }
1535     }
1537     while (nl) {
1538        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1539        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1540        sp_nodepath_node_select(n, TRUE, FALSE);
1541        n_added ++;
1542        nl = g_list_remove(nl, t);
1543     }
1545     /** \todo fixme: adjust ? */
1546     sp_nodepath_update_handles(nodepath);
1548     if (n_added > 1) {
1549         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1550     } else if (n_added > 0) {
1551         sp_nodepath_update_repr(nodepath, _("Add node"));
1552     }
1554     sp_nodepath_update_statusbar(nodepath);
1557 /**
1558  * Select segment nearest to point
1559  */
1560 void
1561 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1563     if (!nodepath) {
1564         return;
1565     }
1567     sp_nodepath_ensure_livarot_path(nodepath);
1568     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1569     if (!maybe_position) {
1570         return;
1571     }
1572     Path::cut_position position = *maybe_position;
1574     //find segment to segment
1575     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1577     //fixme: this can return NULL, so check before proceeding.
1578     g_return_if_fail(e != NULL);
1579     
1580     gboolean force = FALSE;
1581     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1582         force = TRUE;
1583     }
1584     sp_nodepath_node_select(e, (gboolean) toggle, force);
1585     if (e->p.other)
1586         sp_nodepath_node_select(e->p.other, TRUE, force);
1588     sp_nodepath_update_handles(nodepath);
1590     sp_nodepath_update_statusbar(nodepath);
1593 /**
1594  * Add a node nearest to point
1595  */
1596 void
1597 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1599     if (!nodepath) {
1600         return;
1601     }
1603     sp_nodepath_ensure_livarot_path(nodepath);
1604     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1605     if (!maybe_position) {
1606         return;
1607     }
1608     Path::cut_position position = *maybe_position;
1610     //find segment to split
1611     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1613     //don't know why but t seems to flip for lines
1614     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1615         position.t = 1.0 - position.t;
1616     }
1617     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1618     sp_nodepath_node_select(n, FALSE, TRUE);
1620     /* fixme: adjust ? */
1621     sp_nodepath_update_handles(nodepath);
1623     sp_nodepath_update_repr(nodepath, _("Add node"));
1625     sp_nodepath_update_statusbar(nodepath);
1628 /*
1629  * Adjusts a segment so that t moves by a certain delta for dragging
1630  * converts lines to curves
1631  *
1632  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1633  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1634  */
1635 void
1636 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1638     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1640     //fixme: e and e->p can be NULL, so check for those before proceeding
1641     g_return_if_fail(e != NULL);
1642     g_return_if_fail(&e->p != NULL);
1643     
1644     /* feel good is an arbitrary parameter that distributes the delta between handles
1645      * if t of the drag point is less than 1/6 distance form the endpoint only
1646      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1647      */
1648     double feel_good;
1649     if (t <= 1.0 / 6.0)
1650         feel_good = 0;
1651     else if (t <= 0.5)
1652         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1653     else if (t <= 5.0 / 6.0)
1654         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1655     else
1656         feel_good = 1;
1658     //if we're dragging a line convert it to a curve
1659     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1660         sp_nodepath_set_line_type(e, NR_CURVETO);
1661     }
1663     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1664     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1665     e->p.other->n.pos += offsetcoord0;
1666     e->p.pos += offsetcoord1;
1668     // adjust handles of adjacent nodes where necessary
1669     sp_node_adjust_handle(e,1);
1670     sp_node_adjust_handle(e->p.other,-1);
1672     sp_nodepath_update_handles(e->subpath->nodepath);
1674     update_object(e->subpath->nodepath);
1676     sp_nodepath_update_statusbar(e->subpath->nodepath);
1680 /**
1681  * Call sp_nodepath_break() for all selected segments.
1682  */
1683 void sp_node_selected_break()
1685     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1686     if (!nodepath) return;
1688     GList *temp = NULL;
1689     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1690        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1691        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1692         if (nn == NULL) continue; // no break, no new node
1693         temp = g_list_prepend(temp, nn);
1694     }
1696     if (temp) {
1697         sp_nodepath_deselect(nodepath);
1698     }
1699     for (GList *l = temp; l != NULL; l = l->next) {
1700         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1701     }
1703     sp_nodepath_update_handles(nodepath);
1705     sp_nodepath_update_repr(nodepath, _("Break path"));
1708 /**
1709  * Duplicate the selected node(s).
1710  */
1711 void sp_node_selected_duplicate()
1713     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1714     if (!nodepath) {
1715         return;
1716     }
1718     GList *temp = NULL;
1719     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1720        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1721        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1722         if (nn == NULL) continue; // could not duplicate
1723         temp = g_list_prepend(temp, nn);
1724     }
1726     if (temp) {
1727         sp_nodepath_deselect(nodepath);
1728     }
1729     for (GList *l = temp; l != NULL; l = l->next) {
1730         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1731     }
1733     sp_nodepath_update_handles(nodepath);
1735     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1738 /**
1739  *  Join two nodes by merging them into one.
1740  */
1741 void sp_node_selected_join()
1743     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1744     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1746     if (g_list_length(nodepath->selected) != 2) {
1747         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1748         return;
1749     }
1751    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1752    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1754     g_assert(a != b);
1755     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) { 
1756         // someone tried to join an orphan node (i.e. a single-node subpath).
1757         // this is not worth an error message, just fail silently.
1758         return;
1759     }
1761     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1762         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1763         return;
1764     }
1766     /* a and b are endpoints */
1768     NR::Point c;
1769     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1770         c = a->pos;
1771     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1772         c = b->pos;
1773     } else {
1774         c = (a->pos + b->pos) / 2;
1775     }
1777     if (a->subpath == b->subpath) {
1778        Inkscape::NodePath::SubPath *sp = a->subpath;
1779         sp_nodepath_subpath_close(sp);
1780         sp_node_moveto (sp->first, c);
1782         sp_nodepath_update_handles(sp->nodepath);
1783         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1784         return;
1785     }
1787     /* a and b are separate subpaths */
1788    Inkscape::NodePath::SubPath *sa = a->subpath;
1789    Inkscape::NodePath::SubPath *sb = b->subpath;
1790     NR::Point p;
1791    Inkscape::NodePath::Node *n;
1792     NRPathcode code;
1793     if (a == sa->first) {
1794         p = sa->first->n.pos;
1795         code = (NRPathcode)sa->first->n.other->code;
1796        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1797         n = sa->last;
1798         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1799         n = n->p.other;
1800         while (n) {
1801             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1802             n = n->p.other;
1803             if (n == sa->first) n = NULL;
1804         }
1805         sp_nodepath_subpath_destroy(sa);
1806         sa = t;
1807     } else if (a == sa->last) {
1808         p = sa->last->p.pos;
1809         code = (NRPathcode)sa->last->code;
1810         sp_nodepath_node_destroy(sa->last);
1811     } else {
1812         code = NR_END;
1813         g_assert_not_reached();
1814     }
1816     if (b == sb->first) {
1817         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1818         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1819             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1820         }
1821     } else if (b == sb->last) {
1822         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1823         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1824             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1825         }
1826     } else {
1827         g_assert_not_reached();
1828     }
1829     /* and now destroy sb */
1831     sp_nodepath_subpath_destroy(sb);
1833     sp_nodepath_update_handles(sa->nodepath);
1835     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1837     sp_nodepath_update_statusbar(nodepath);
1840 /**
1841  *  Join two nodes by adding a segment between them.
1842  */
1843 void sp_node_selected_join_segment()
1845     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1846     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1848     if (g_list_length(nodepath->selected) != 2) {
1849         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1850         return;
1851     }
1853    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1854    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1856     g_assert(a != b);
1857     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) { 
1858         // someone tried to join an orphan node (i.e. a single-node subpath).
1859         // this is not worth an error message, just fail silently.
1860         return;
1861     }
1863     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1864         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1865         return;
1866     }
1868     if (a->subpath == b->subpath) {
1869        Inkscape::NodePath::SubPath *sp = a->subpath;
1871         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1872         sp->closed = TRUE;
1874         sp->first->p.other = sp->last;
1875         sp->last->n.other  = sp->first;
1877         sp_node_handle_mirror_p_to_n(sp->last);
1878         sp_node_handle_mirror_n_to_p(sp->first);
1880         sp->first->code = sp->last->code;
1881         sp->first       = sp->last;
1883         sp_nodepath_update_handles(sp->nodepath);
1885         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
1887         return;
1888     }
1890     /* a and b are separate subpaths */
1891    Inkscape::NodePath::SubPath *sa = a->subpath;
1892    Inkscape::NodePath::SubPath *sb = b->subpath;
1894    Inkscape::NodePath::Node *n;
1895     NR::Point p;
1896     NRPathcode code;
1897     if (a == sa->first) {
1898         code = (NRPathcode) sa->first->n.other->code;
1899        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1900         n = sa->last;
1901         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1902         for (n = n->p.other; n != NULL; n = n->p.other) {
1903             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1904         }
1905         sp_nodepath_subpath_destroy(sa);
1906         sa = t;
1907     } else if (a == sa->last) {
1908         code = (NRPathcode)sa->last->code;
1909     } else {
1910         code = NR_END;
1911         g_assert_not_reached();
1912     }
1914     if (b == sb->first) {
1915         n = sb->first;
1916         sp_node_handle_mirror_p_to_n(sa->last);
1917         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1918         sp_node_handle_mirror_n_to_p(sa->last);
1919         for (n = n->n.other; n != NULL; n = n->n.other) {
1920             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1921         }
1922     } else if (b == sb->last) {
1923         n = sb->last;
1924         sp_node_handle_mirror_p_to_n(sa->last);
1925         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1926         sp_node_handle_mirror_n_to_p(sa->last);
1927         for (n = n->p.other; n != NULL; n = n->p.other) {
1928             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1929         }
1930     } else {
1931         g_assert_not_reached();
1932     }
1933     /* and now destroy sb */
1935     sp_nodepath_subpath_destroy(sb);
1937     sp_nodepath_update_handles(sa->nodepath);
1939     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
1942 /**
1943  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1944  */
1945 void sp_node_delete_preserve(GList *nodes_to_delete)
1947     GSList *nodepaths = NULL;
1948     
1949     while (nodes_to_delete) {
1950         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1951         Inkscape::NodePath::SubPath *sp = node->subpath;
1952         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1953         Inkscape::NodePath::Node *sample_cursor = NULL;
1954         Inkscape::NodePath::Node *sample_end = NULL;
1955         Inkscape::NodePath::Node *delete_cursor = node;
1956         bool just_delete = false;
1957         
1958         //find the start of this contiguous selection
1959         //move left to the first node that is not selected
1960         //or the start of the non-closed path
1961         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1962             delete_cursor = curr;
1963         }
1965         //just delete at the beginning of an open path
1966         if (!delete_cursor->p.other) {
1967             sample_cursor = delete_cursor;
1968             just_delete = true;
1969         } else {
1970             sample_cursor = delete_cursor->p.other;
1971         }
1972         
1973         //calculate points for each segment
1974         int rate = 5;
1975         float period = 1.0 / rate;
1976         std::vector<NR::Point> data;
1977         if (!just_delete) {
1978             data.push_back(sample_cursor->pos);
1979             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1980                 //just delete at the end of an open path
1981                 if (!sp->closed && curr == sp->last) {
1982                     just_delete = true;
1983                     break;
1984                 }
1985                 
1986                 //sample points on the contiguous selected segment
1987                 NR::Point *bez;
1988                 bez = new NR::Point [4];
1989                 bez[0] = curr->pos;
1990                 bez[1] = curr->n.pos;
1991                 bez[2] = curr->n.other->p.pos;
1992                 bez[3] = curr->n.other->pos;
1993                 for (int i=1; i<rate; i++) {
1994                     gdouble t = i * period;
1995                     NR::Point p = bezier_pt(3, bez, t);
1996                     data.push_back(p);
1997                 }
1998                 data.push_back(curr->n.other->pos);
2000                 sample_end = curr->n.other;
2001                 //break if we've come full circle or hit the end of the selection
2002                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2003                     break;
2004                 }
2005             }
2006         }
2008         if (!just_delete) {
2009             //calculate the best fitting single segment and adjust the endpoints
2010             NR::Point *adata;
2011             adata = new NR::Point [data.size()];
2012             copy(data.begin(), data.end(), adata);
2013             
2014             NR::Point *bez;
2015             bez = new NR::Point [4];
2016             //would decreasing error create a better fitting approximation?
2017             gdouble error = 1.0;
2018             gint ret;
2019             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2021             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2022             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2023             //the resulting nodes behave as expected.
2024             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2025             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2026             
2027             //adjust endpoints
2028             sample_cursor->n.pos = bez[1];
2029             sample_end->p.pos = bez[2];
2030         }
2031        
2032         //destroy this contiguous selection
2033         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2034             Inkscape::NodePath::Node *temp = delete_cursor;
2035             if (delete_cursor->n.other == delete_cursor) {
2036                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2037                 delete_cursor = NULL; 
2038             } else {
2039                 delete_cursor = delete_cursor->n.other;
2040             }
2041             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2042             sp_nodepath_node_destroy(temp);
2043         }
2045         sp_nodepath_update_handles(nodepath);
2047         if (!g_slist_find(nodepaths, nodepath))
2048             nodepaths = g_slist_prepend (nodepaths, nodepath);
2049     }
2051     for (GSList *i = nodepaths; i; i = i->next) {
2052         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2053         // different nodepaths will give us one undo event per nodepath
2054         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2056         // if the entire nodepath is removed, delete the selected object.
2057         if (nodepath->subpaths == NULL ||
2058             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2059             //at least 2
2060             sp_nodepath_get_node_count(nodepath) < 2) {
2061             SPDocument *document = sp_desktop_document (nodepath->desktop);
2062             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2063             //delete this nodepath's object, not the entire selection! (though at this time, this
2064             //does not matter)
2065             sp_selection_delete();
2066             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2067                               _("Delete nodes"));
2068         } else {
2069             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2070             sp_nodepath_update_statusbar(nodepath);
2071         }
2072     }
2074     g_slist_free (nodepaths);
2077 /**
2078  * Delete one or more selected nodes.
2079  */
2080 void sp_node_selected_delete()
2082     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2083     if (!nodepath) return;
2084     if (!nodepath->selected) return;
2086     /** \todo fixme: do it the right way */
2087     while (nodepath->selected) {
2088        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2089         sp_nodepath_node_destroy(node);
2090     }
2093     //clean up the nodepath (such as for trivial subpaths)
2094     sp_nodepath_cleanup(nodepath);
2096     sp_nodepath_update_handles(nodepath);
2098     // if the entire nodepath is removed, delete the selected object.
2099     if (nodepath->subpaths == NULL ||
2100         sp_nodepath_get_node_count(nodepath) < 2) {
2101         SPDocument *document = sp_desktop_document (nodepath->desktop);
2102         sp_selection_delete();
2103         sp_document_done (document, SP_VERB_CONTEXT_NODE, 
2104                           _("Delete nodes"));
2105         return;
2106     }
2108     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2110     sp_nodepath_update_statusbar(nodepath);
2113 /**
2114  * Delete one or more segments between two selected nodes.
2115  * This is the code for 'split'.
2116  */
2117 void
2118 sp_node_selected_delete_segment(void)
2120    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2121    Inkscape::NodePath::Node *curr, *next;     //Iterators
2123     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2124     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2126     if (g_list_length(nodepath->selected) != 2) {
2127         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2128                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2129         return;
2130     }
2132     //Selected nodes, not inclusive
2133    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2134    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2136     if ( ( a==b)                       ||  //same node
2137          (a->subpath  != b->subpath )  ||  //not the same path
2138          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2139          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2140     {
2141         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2142                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2143         return;
2144     }
2146     //###########################################
2147     //# BEGIN EDITS
2148     //###########################################
2149     //##################################
2150     //# CLOSED PATH
2151     //##################################
2152     if (a->subpath->closed) {
2155         gboolean reversed = FALSE;
2157         //Since we can go in a circle, we need to find the shorter distance.
2158         //  a->b or b->a
2159         start = end = NULL;
2160         int distance    = 0;
2161         int minDistance = 0;
2162         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2163             if (curr==b) {
2164                 //printf("a to b:%d\n", distance);
2165                 start = a;//go from a to b
2166                 end   = b;
2167                 minDistance = distance;
2168                 //printf("A to B :\n");
2169                 break;
2170             }
2171             distance++;
2172         }
2174         //try again, the other direction
2175         distance = 0;
2176         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2177             if (curr==a) {
2178                 //printf("b to a:%d\n", distance);
2179                 if (distance < minDistance) {
2180                     start    = b;  //we go from b to a
2181                     end      = a;
2182                     reversed = TRUE;
2183                     //printf("B to A\n");
2184                 }
2185                 break;
2186             }
2187             distance++;
2188         }
2191         //Copy everything from 'end' to 'start' to a new subpath
2192        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2193         for (curr=end ; curr ; curr=curr->n.other) {
2194             NRPathcode code = (NRPathcode) curr->code;
2195             if (curr == end)
2196                 code = NR_MOVETO;
2197             sp_nodepath_node_new(t, NULL,
2198                                  (Inkscape::NodePath::NodeType)curr->type, code,
2199                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2200             if (curr == start)
2201                 break;
2202         }
2203         sp_nodepath_subpath_destroy(a->subpath);
2206     }
2210     //##################################
2211     //# OPEN PATH
2212     //##################################
2213     else {
2215         //We need to get the direction of the list between A and B
2216         //Can we walk from a to b?
2217         start = end = NULL;
2218         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2219             if (curr==b) {
2220                 start = a;  //did it!  we go from a to b
2221                 end   = b;
2222                 //printf("A to B\n");
2223                 break;
2224             }
2225         }
2226         if (!start) {//didn't work?  let's try the other direction
2227             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2228                 if (curr==a) {
2229                     start = b;  //did it!  we go from b to a
2230                     end   = a;
2231                     //printf("B to A\n");
2232                     break;
2233                 }
2234             }
2235         }
2236         if (!start) {
2237             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2238                                                      _("Cannot find path between nodes."));
2239             return;
2240         }
2244         //Copy everything after 'end' to a new subpath
2245        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2246         for (curr=end ; curr ; curr=curr->n.other) {
2247             NRPathcode code = (NRPathcode) curr->code;
2248             if (curr == end)
2249                 code = NR_MOVETO;
2250             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2251                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2252         }
2254         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2255         for (curr = start->n.other ; curr  ; curr=next) {
2256             next = curr->n.other;
2257             sp_nodepath_node_destroy(curr);
2258         }
2260     }
2261     //###########################################
2262     //# END EDITS
2263     //###########################################
2265     //clean up the nodepath (such as for trivial subpaths)
2266     sp_nodepath_cleanup(nodepath);
2268     sp_nodepath_update_handles(nodepath);
2270     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2272     sp_nodepath_update_statusbar(nodepath);
2275 /**
2276  * Call sp_nodepath_set_line() for all selected segments.
2277  */
2278 void
2279 sp_node_selected_set_line_type(NRPathcode code)
2281     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2282     if (nodepath == NULL) return;
2284     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2285        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2286         g_assert(n->selected);
2287         if (n->p.other && n->p.other->selected) {
2288             sp_nodepath_set_line_type(n, code);
2289         }
2290     }
2292     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2295 /**
2296  * Call sp_nodepath_convert_node_type() for all selected nodes.
2297  */
2298 void
2299 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
2301     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2302     if (nodepath == NULL) return;
2304     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2305         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2306     }
2308     sp_nodepath_update_repr(nodepath, _("Change node type"));
2311 /**
2312  * Change select status of node, update its own and neighbour handles.
2313  */
2314 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2316     node->selected = selected;
2318     if (selected) {
2319         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2320         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2321         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2322         sp_knot_update_ctrl(node->knot);
2323     } else {
2324         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2325         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2326         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2327         sp_knot_update_ctrl(node->knot);
2328     }
2330     sp_node_update_handles(node);
2331     if (node->n.other) sp_node_update_handles(node->n.other);
2332     if (node->p.other) sp_node_update_handles(node->p.other);
2335 /**
2336 \brief Select a node
2337 \param node     The node to select
2338 \param incremental   If true, add to selection, otherwise deselect others
2339 \param override   If true, always select this node, otherwise toggle selected status
2340 */
2341 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2343     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2345     if (incremental) {
2346         if (override) {
2347             if (!g_list_find(nodepath->selected, node)) {
2348                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2349             }
2350             sp_node_set_selected(node, TRUE);
2351         } else { // toggle
2352             if (node->selected) {
2353                 g_assert(g_list_find(nodepath->selected, node));
2354                 nodepath->selected = g_list_remove(nodepath->selected, node);
2355             } else {
2356                 g_assert(!g_list_find(nodepath->selected, node));
2357                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2358             }
2359             sp_node_set_selected(node, !node->selected);
2360         }
2361     } else {
2362         sp_nodepath_deselect(nodepath);
2363         nodepath->selected = g_list_prepend(nodepath->selected, node);
2364         sp_node_set_selected(node, TRUE);
2365     }
2367     sp_nodepath_update_statusbar(nodepath);
2371 /**
2372 \brief Deselect all nodes in the nodepath
2373 */
2374 void
2375 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2377     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2379     while (nodepath->selected) {
2380         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2381         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2382     }
2383     sp_nodepath_update_statusbar(nodepath);
2386 /**
2387 \brief Select or invert selection of all nodes in the nodepath
2388 */
2389 void
2390 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2392     if (!nodepath) return;
2394     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2395        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2396         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2397            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2398            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2399         }
2400     }
2403 /**
2404  * If nothing selected, does the same as sp_nodepath_select_all();
2405  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2406  * (i.e., similar to "select all in layer", with the "selected" subpaths
2407  * being treated as "layers" in the path).
2408  */
2409 void
2410 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2412     if (!nodepath) return;
2414     if (g_list_length (nodepath->selected) == 0) {
2415         sp_nodepath_select_all (nodepath, invert);
2416         return;
2417     }
2419     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2420     GSList *subpaths = NULL;
2422     for (GList *l = copy; l != NULL; l = l->next) {
2423         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2424         Inkscape::NodePath::SubPath *subpath = n->subpath;
2425         if (!g_slist_find (subpaths, subpath))
2426             subpaths = g_slist_prepend (subpaths, subpath);
2427     }
2429     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2430         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2431         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2432             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2433             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2434         }
2435     }
2437     g_slist_free (subpaths);
2438     g_list_free (copy);
2441 /**
2442  * \brief Select the node after the last selected; if none is selected,
2443  * select the first within path.
2444  */
2445 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2447     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2449    Inkscape::NodePath::Node *last = NULL;
2450     if (nodepath->selected) {
2451         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2452            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2453             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2454             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2455                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2456                 if (node->selected) {
2457                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2458                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2459                             if (spl->next) { // there's a next subpath
2460                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2461                                 last = subpath_next->first;
2462                             } else if (spl->prev) { // there's a previous subpath
2463                                 last = NULL; // to be set later to the first node of first subpath
2464                             } else {
2465                                 last = node->n.other;
2466                             }
2467                         } else {
2468                             last = node->n.other;
2469                         }
2470                     } else {
2471                         if (node->n.other) {
2472                             last = node->n.other;
2473                         } else {
2474                             if (spl->next) { // there's a next subpath
2475                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2476                                 last = subpath_next->first;
2477                             } else if (spl->prev) { // there's a previous subpath
2478                                 last = NULL; // to be set later to the first node of first subpath
2479                             } else {
2480                                 last = (Inkscape::NodePath::Node *) subpath->first;
2481                             }
2482                         }
2483                     }
2484                 }
2485             }
2486         }
2487         sp_nodepath_deselect(nodepath);
2488     }
2490     if (last) { // there's at least one more node after selected
2491         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2492     } else { // no more nodes, select the first one in first subpath
2493        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2494         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2495     }
2498 /**
2499  * \brief Select the node before the first selected; if none is selected,
2500  * select the last within path
2501  */
2502 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2504     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2506    Inkscape::NodePath::Node *last = NULL;
2507     if (nodepath->selected) {
2508         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2509            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2510             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2511                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2512                 if (node->selected) {
2513                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2514                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2515                             if (spl->prev) { // there's a prev subpath
2516                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2517                                 last = subpath_prev->last;
2518                             } else if (spl->next) { // there's a next subpath
2519                                 last = NULL; // to be set later to the last node of last subpath
2520                             } else {
2521                                 last = node->p.other;
2522                             }
2523                         } else {
2524                             last = node->p.other;
2525                         }
2526                     } else {
2527                         if (node->p.other) {
2528                             last = node->p.other;
2529                         } else {
2530                             if (spl->prev) { // there's a prev subpath
2531                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2532                                 last = subpath_prev->last;
2533                             } else if (spl->next) { // there's a next subpath
2534                                 last = NULL; // to be set later to the last node of last subpath
2535                             } else {
2536                                 last = (Inkscape::NodePath::Node *) subpath->last;
2537                             }
2538                         }
2539                     }
2540                 }
2541             }
2542         }
2543         sp_nodepath_deselect(nodepath);
2544     }
2546     if (last) { // there's at least one more node before selected
2547         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2548     } else { // no more nodes, select the last one in last subpath
2549         GList *spl = g_list_last(nodepath->subpaths);
2550        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2551         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2552     }
2555 /**
2556  * \brief Select all nodes that are within the rectangle.
2557  */
2558 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2560     if (!incremental) {
2561         sp_nodepath_deselect(nodepath);
2562     }
2564     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2565        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2566         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2567            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2569             if (b.contains(node->pos)) {
2570                 sp_nodepath_node_select(node, TRUE, TRUE);
2571             }
2572         }
2573     }
2577 void
2578 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2580     g_assert (n);
2581     g_assert (nodepath);
2582     g_assert (n->subpath->nodepath == nodepath);
2584     if (g_list_length (nodepath->selected) == 0) {
2585         if (grow > 0) {
2586             sp_nodepath_node_select(n, TRUE, TRUE);
2587         }
2588         return;
2589     }
2591     if (g_list_length (nodepath->selected) == 1) {
2592         if (grow < 0) {
2593             sp_nodepath_deselect (nodepath);
2594             return;
2595         }
2596     }
2598         double n_sel_range = 0, p_sel_range = 0;
2599             Inkscape::NodePath::Node *farthest_n_node = n;
2600             Inkscape::NodePath::Node *farthest_p_node = n;
2602         // Calculate ranges
2603         {
2604             double n_range = 0, p_range = 0;
2605             bool n_going = true, p_going = true;
2606             Inkscape::NodePath::Node *n_node = n;
2607             Inkscape::NodePath::Node *p_node = n;
2608             do {
2609                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2610                 if (n_node && n_going)
2611                     n_node = n_node->n.other;
2612                 if (n_node == NULL) {
2613                     n_going = false;
2614                 } else {
2615                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2616                     if (n_node->selected) {
2617                         n_sel_range = n_range;
2618                         farthest_n_node = n_node;
2619                     }
2620                     if (n_node == p_node) {
2621                         n_going = false;
2622                         p_going = false;
2623                     }
2624                 }
2625                 if (p_node && p_going)
2626                     p_node = p_node->p.other;
2627                 if (p_node == NULL) {
2628                     p_going = false;
2629                 } else {
2630                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2631                     if (p_node->selected) {
2632                         p_sel_range = p_range;
2633                         farthest_p_node = p_node;
2634                     }
2635                     if (p_node == n_node) {
2636                         n_going = false;
2637                         p_going = false;
2638                     }
2639                 }
2640             } while (n_going || p_going);
2641         }
2643     if (grow > 0) {
2644         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2645                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2646         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2647                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2648         }
2649     } else {
2650         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2651                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2652         } else if (farthest_p_node && farthest_p_node->selected) {
2653                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2654         }
2655     }
2658 void
2659 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2661     g_assert (n);
2662     g_assert (nodepath);
2663     g_assert (n->subpath->nodepath == nodepath);
2665     if (g_list_length (nodepath->selected) == 0) {
2666         if (grow > 0) {
2667             sp_nodepath_node_select(n, TRUE, TRUE);
2668         }
2669         return;
2670     }
2672     if (g_list_length (nodepath->selected) == 1) {
2673         if (grow < 0) {
2674             sp_nodepath_deselect (nodepath);
2675             return;
2676         }
2677     }
2679     Inkscape::NodePath::Node *farthest_selected = NULL;
2680     double farthest_dist = 0;
2682     Inkscape::NodePath::Node *closest_unselected = NULL;
2683     double closest_dist = NR_HUGE;
2685     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2686        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2687         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2688            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2689            if (node == n)
2690                continue;
2691            if (node->selected) {
2692                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2693                    farthest_dist = NR::L2(node->pos - n->pos);
2694                    farthest_selected = node;
2695                }
2696            } else {
2697                if (NR::L2(node->pos - n->pos) < closest_dist) {
2698                    closest_dist = NR::L2(node->pos - n->pos);
2699                    closest_unselected = node;
2700                }
2701            }
2702         }
2703     }
2705     if (grow > 0) {
2706         if (closest_unselected) {
2707             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2708         }
2709     } else {
2710         if (farthest_selected) {
2711             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2712         }
2713     }
2717 /**
2718 \brief  Saves all nodes' and handles' current positions in their origin members
2719 */
2720 void
2721 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2723     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2724        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2725         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2726            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2727            n->origin = n->pos;
2728            n->p.origin = n->p.pos;
2729            n->n.origin = n->n.pos;
2730         }
2731     }
2732
2734 /**
2735 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2736 */
2737 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2739     if (!nodepath->selected) {
2740         return NULL;
2741     }
2743     GList *r = NULL;
2744     guint i = 0;
2745     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2746        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2747         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2748            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2749             i++;
2750             if (node->selected) {
2751                 r = g_list_append(r, GINT_TO_POINTER(i));
2752             }
2753         }
2754     }
2755     return r;
2758 /**
2759 \brief  Restores selection by selecting nodes whose positions are in the list
2760 */
2761 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2763     sp_nodepath_deselect(nodepath);
2765     guint i = 0;
2766     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2767        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2768         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2769            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2770             i++;
2771             if (g_list_find(r, GINT_TO_POINTER(i))) {
2772                 sp_nodepath_node_select(node, TRUE, TRUE);
2773             }
2774         }
2775     }
2779 /**
2780 \brief Adjusts handle according to node type and line code.
2781 */
2782 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2784     double len, otherlen, linelen;
2786     g_assert(node);
2788    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2789    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2791     /** \todo fixme: */
2792     if (me->other == NULL) return;
2793     if (other->other == NULL) return;
2795     /* I have line */
2797     NRPathcode mecode, ocode;
2798     if (which_adjust == 1) {
2799         mecode = (NRPathcode)me->other->code;
2800         ocode = (NRPathcode)node->code;
2801     } else {
2802         mecode = (NRPathcode)node->code;
2803         ocode = (NRPathcode)other->other->code;
2804     }
2806     if (mecode == NR_LINETO) return;
2808     /* I am curve */
2810     if (other->other == NULL) return;
2812     /* Other has line */
2814     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2816     NR::Point delta;
2817     if (ocode == NR_LINETO) {
2818         /* other is lineto, we are either smooth or symm */
2819        Inkscape::NodePath::Node *othernode = other->other;
2820         len = NR::L2(me->pos - node->pos);
2821         delta = node->pos - othernode->pos;
2822         linelen = NR::L2(delta);
2823         if (linelen < 1e-18) 
2824             return;
2825         me->pos = node->pos + (len / linelen)*delta;
2826         return;
2827     }
2829     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2831         me->pos = 2 * node->pos - other->pos;
2832         return;
2833     }
2835     /* We are smooth */
2837     len = NR::L2(me->pos - node->pos);
2838     delta = other->pos - node->pos;
2839     otherlen = NR::L2(delta);
2840     if (otherlen < 1e-18) return;
2842     me->pos = node->pos - (len / otherlen) * delta;
2845 /**
2846  \brief Adjusts both handles according to node type and line code
2847  */
2848 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2850     g_assert(node);
2852     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2854     /* we are either smooth or symm */
2856     if (node->p.other == NULL) return;
2858     if (node->n.other == NULL) return;
2860     if (node->code == NR_LINETO) {
2861         if (node->n.other->code == NR_LINETO) return;
2862         sp_node_adjust_handle(node, 1);
2863         return;
2864     }
2866     if (node->n.other->code == NR_LINETO) {
2867         if (node->code == NR_LINETO) return;
2868         sp_node_adjust_handle(node, -1);
2869         return;
2870     }
2872     /* both are curves */
2873     NR::Point const delta( node->n.pos - node->p.pos );
2875     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2876         node->p.pos = node->pos - delta / 2;
2877         node->n.pos = node->pos + delta / 2;
2878         return;
2879     }
2881     /* We are smooth */
2882     double plen = NR::L2(node->p.pos - node->pos);
2883     if (plen < 1e-18) return;
2884     double nlen = NR::L2(node->n.pos - node->pos);
2885     if (nlen < 1e-18) return;
2886     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2887     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2890 /**
2891  * Node event callback.
2892  */
2893 static gboolean node_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n)
2895     gboolean ret = FALSE;
2896     switch (event->type) {
2897         case GDK_ENTER_NOTIFY:
2898             active_node = n;
2899             break;
2900         case GDK_LEAVE_NOTIFY:
2901             active_node = NULL;
2902             break;
2903         case GDK_SCROLL:
2904             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
2905                 switch (event->scroll.direction) {
2906                     case GDK_SCROLL_UP:
2907                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2908                         break;
2909                     case GDK_SCROLL_DOWN:
2910                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2911                         break;
2912                     default:
2913                         break;
2914                 }
2915                 ret = TRUE;
2916             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
2917                 switch (event->scroll.direction) {
2918                     case GDK_SCROLL_UP:
2919                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2920                         break;
2921                     case GDK_SCROLL_DOWN:
2922                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2923                         break;
2924                     default:
2925                         break;
2926                 }
2927                 ret = TRUE;
2928             }
2929             break;
2930         case GDK_KEY_PRESS:
2931             switch (get_group0_keyval (&event->key)) {
2932                 case GDK_space:
2933                     if (event->key.state & GDK_BUTTON1_MASK) {
2934                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2935                         stamp_repr(nodepath);
2936                         ret = TRUE;
2937                     }
2938                     break;
2939                 case GDK_Page_Up:
2940                     if (event->key.state & GDK_CONTROL_MASK) {
2941                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2942                     } else {
2943                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2944                     }
2945                     break;
2946                 case GDK_Page_Down:
2947                     if (event->key.state & GDK_CONTROL_MASK) {
2948                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2949                     } else {
2950                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2951                     }
2952                     break;
2953                 default:
2954                     break;
2955             }
2956             break;
2957         default:
2958             break;
2959     }
2961     return ret;
2964 /**
2965  * Handle keypress on node; directly called.
2966  */
2967 gboolean node_key(GdkEvent *event)
2969     Inkscape::NodePath::Path *np;
2971     // there is no way to verify nodes so set active_node to nil when deleting!!
2972     if (active_node == NULL) return FALSE;
2974     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2975         gint ret = FALSE;
2976         switch (get_group0_keyval (&event->key)) {
2977             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2978             case GDK_BackSpace:
2979                 np = active_node->subpath->nodepath;
2980                 sp_nodepath_node_destroy(active_node);
2981                 sp_nodepath_update_repr(np, _("Delete node"));
2982                 active_node = NULL;
2983                 ret = TRUE;
2984                 break;
2985             case GDK_c:
2986                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2987                 ret = TRUE;
2988                 break;
2989             case GDK_s:
2990                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2991                 ret = TRUE;
2992                 break;
2993             case GDK_y:
2994                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2995                 ret = TRUE;
2996                 break;
2997             case GDK_b:
2998                 sp_nodepath_node_break(active_node);
2999                 ret = TRUE;
3000                 break;
3001         }
3002         return ret;
3003     }
3004     return FALSE;
3007 /**
3008  * Mouseclick on node callback.
3009  */
3010 static void node_clicked(SPKnot *knot, guint state, gpointer data)
3012    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3014     if (state & GDK_CONTROL_MASK) {
3015         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3017         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3018             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3019                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3020             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3021                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3022             } else {
3023                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3024             }
3025             sp_nodepath_update_repr(nodepath, _("Change node type"));
3026             sp_nodepath_update_statusbar(nodepath);
3028         } else { //ctrl+alt+click: delete node
3029             GList *node_to_delete = NULL;
3030             node_to_delete = g_list_append(node_to_delete, n);
3031             sp_node_delete_preserve(node_to_delete);
3032         }
3034     } else {
3035         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3036     }
3039 /**
3040  * Mouse grabbed node callback.
3041  */
3042 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3044    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3046     if (!n->selected) {
3047         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3048     }
3050     n->is_dragging = true;
3051     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3053     sp_nodepath_remember_origins (n->subpath->nodepath);
3056 /**
3057  * Mouse ungrabbed node callback.
3058  */
3059 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
3061    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3063    n->dragging_out = NULL;
3064    n->is_dragging = false;
3065    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3067    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3070 /**
3071  * The point on a line, given by its angle, closest to the given point.
3072  * \param p  A point.
3073  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3074  * \param closest  Pointer to the point struct where the result is stored.
3075  * \todo FIXME: use dot product perhaps?
3076  */
3077 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3079     if (a == HUGE_VAL) { // vertical
3080         *closest = NR::Point(0, (*p)[NR::Y]);
3081     } else {
3082         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3083         (*closest)[NR::Y] = a * (*closest)[NR::X];
3084     }
3087 /**
3088  * Distance from the point to a line given by its angle.
3089  * \param p  A point.
3090  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3091  */
3092 static double point_line_distance(NR::Point *p, double a)
3094     NR::Point c;
3095     point_line_closest(p, a, &c);
3096     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]));
3099 /**
3100  * Callback for node "request" signal.
3101  * \todo fixme: This goes to "moved" event? (lauris)
3102  */
3103 static gboolean
3104 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3106     double yn, xn, yp, xp;
3107     double an, ap, na, pa;
3108     double d_an, d_ap, d_na, d_pa;
3109     gboolean collinear = FALSE;
3110     NR::Point c;
3111     NR::Point pr;
3113    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3115    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3116    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
3118        NR::Point mouse = (*p);
3120        if (!n->dragging_out) {
3121            // This is the first drag-out event; find out which handle to drag out
3122            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3123            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3125            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3126                return FALSE;
3128            Inkscape::NodePath::NodeSide *opposite;
3129            if (appr_p > appr_n) { // closer to p
3130                n->dragging_out = &n->p;
3131                opposite = &n->n;
3132                n->code = NR_CURVETO;
3133            } else if (appr_p < appr_n) { // closer to n
3134                n->dragging_out = &n->n;
3135                opposite = &n->p;
3136                n->n.other->code = NR_CURVETO;
3137            } else { // p and n nodes are the same
3138                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3139                    n->dragging_out = &n->p;
3140                    opposite = &n->n;
3141                    n->code = NR_CURVETO;
3142                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3143                    n->dragging_out = &n->n;
3144                    opposite = &n->p;
3145                    n->n.other->code = NR_CURVETO;
3146                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3147                    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);
3148                    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);
3149                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3150                        n->dragging_out = &n->n;
3151                        opposite = &n->p;
3152                        n->n.other->code = NR_CURVETO;
3153                    } else { // closer to other's n handle
3154                        n->dragging_out = &n->p;
3155                        opposite = &n->n;
3156                        n->code = NR_CURVETO;
3157                    }
3158                }
3159            }
3161            // if there's another handle, make sure the one we drag out starts parallel to it
3162            if (opposite->pos != n->pos) {
3163                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3164            }
3166            // knots might not be created yet!
3167            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3168            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3169        }
3171        // pass this on to the handle-moved callback
3172        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3173        sp_node_update_handles(n);
3174        return TRUE;
3175    }
3177     if (state & GDK_CONTROL_MASK) { // constrained motion
3179         // calculate relative distances of handles
3180         // n handle:
3181         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3182         xn = n->n.pos[NR::X] - n->pos[NR::X];
3183         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3184         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3185             if (n->n.other) { // if there is the next point
3186                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3187                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3188                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3189             }
3190         }
3191         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3192         if (yn < 0) { xn = -xn; yn = -yn; }
3194         // p handle:
3195         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3196         xp = n->p.pos[NR::X] - n->pos[NR::X];
3197         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3198         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3199             if (n->p.other) {
3200                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3201                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3202                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3203             }
3204         }
3205         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3206         if (yp < 0) { xp = -xp; yp = -yp; }
3208         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3209             // sliding on handles, only if at least one of the handles is non-vertical
3210             // (otherwise it's the same as ctrl+drag anyway)
3212             // calculate angles of the handles
3213             if (xn == 0) {
3214                 if (yn == 0) { // no handle, consider it the continuation of the other one
3215                     an = 0;
3216                     collinear = TRUE;
3217                 }
3218                 else an = 0; // vertical; set the angle to horizontal
3219             } else an = yn/xn;
3221             if (xp == 0) {
3222                 if (yp == 0) { // no handle, consider it the continuation of the other one
3223                     ap = an;
3224                 }
3225                 else ap = 0; // vertical; set the angle to horizontal
3226             } else  ap = yp/xp;
3228             if (collinear) an = ap;
3230             // angles of the perpendiculars; HUGE_VAL means vertical
3231             if (an == 0) na = HUGE_VAL; else na = -1/an;
3232             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3234             // mouse point relative to the node's original pos
3235             pr = (*p) - n->origin;
3237             // distances to the four lines (two handles and two perpendiculars)
3238             d_an = point_line_distance(&pr, an);
3239             d_na = point_line_distance(&pr, na);
3240             d_ap = point_line_distance(&pr, ap);
3241             d_pa = point_line_distance(&pr, pa);
3243             // find out which line is the closest, save its closest point in c
3244             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3245                 point_line_closest(&pr, an, &c);
3246             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3247                 point_line_closest(&pr, ap, &c);
3248             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3249                 point_line_closest(&pr, na, &c);
3250             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3251                 point_line_closest(&pr, pa, &c);
3252             }
3254             // move the node to the closest point
3255             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3256                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3257                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3259         } else {  // constraining to hor/vert
3261             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3262                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3263             } else { // snap to vert
3264                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3265             }
3266         }
3267     } else { // move freely
3268         if (n->is_dragging) {
3269             if (state & GDK_MOD1_MASK) { // sculpt
3270                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3271             } else {
3272                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3273                                             (*p)[NR::X] - n->pos[NR::X],
3274                                             (*p)[NR::Y] - n->pos[NR::Y],
3275                                             (state & GDK_SHIFT_MASK) == 0);
3276             }
3277         }
3278     }
3280     n->subpath->nodepath->desktop->scroll_to_point(p);
3282     return TRUE;
3285 /**
3286  * Node handle clicked callback.
3287  */
3288 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3290    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3292     if (state & GDK_CONTROL_MASK) { // "delete" handle
3293         if (n->p.knot == knot) {
3294             n->p.pos = n->pos;
3295         } else if (n->n.knot == knot) {
3296             n->n.pos = n->pos;
3297         }
3298         sp_node_update_handles(n);
3299         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3300         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3301         sp_nodepath_update_statusbar(nodepath);
3303     } else { // just select or add to selection, depending in Shift
3304         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3305     }
3308 /**
3309  * Node handle grabbed callback.
3310  */
3311 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3313    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3315     if (!n->selected) {
3316         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3317     }
3319     // remember the origin point of the handle
3320     if (n->p.knot == knot) {
3321         n->p.origin_radial = n->p.pos - n->pos;
3322     } else if (n->n.knot == knot) {
3323         n->n.origin_radial = n->n.pos - n->pos;
3324     } else {
3325         g_assert_not_reached();
3326     }
3328     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3331 /**
3332  * Node handle ungrabbed callback.
3333  */
3334 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3336    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3338     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3339     if (n->p.knot == knot) {
3340         n->p.origin_radial.a = 0;
3341         sp_knot_set_position(knot, &n->p.pos, state);
3342     } else if (n->n.knot == knot) {
3343         n->n.origin_radial.a = 0;
3344         sp_knot_set_position(knot, &n->n.pos, state);
3345     } else {
3346         g_assert_not_reached();
3347     }
3349     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3352 /**
3353  * Node handle "request" signal callback.
3354  */
3355 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3357     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3359     Inkscape::NodePath::NodeSide *me, *opposite;
3360     gint which;
3361     if (n->p.knot == knot) {
3362         me = &n->p;
3363         opposite = &n->n;
3364         which = -1;
3365     } else if (n->n.knot == knot) {
3366         me = &n->n;
3367         opposite = &n->p;
3368         which = 1;
3369     } else {
3370         me = opposite = NULL;
3371         which = 0;
3372         g_assert_not_reached();
3373     }
3375     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3377     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3379     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3380         /* We are smooth node adjacent with line */
3381         NR::Point const delta = *p - n->pos;
3382         NR::Coord const len = NR::L2(delta);
3383         Inkscape::NodePath::Node *othernode = opposite->other;
3384         NR::Point const ndelta = n->pos - othernode->pos;
3385         NR::Coord const linelen = NR::L2(ndelta);
3386         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3387             NR::Coord const scal = dot(delta, ndelta) / linelen;
3388             (*p) = n->pos + (scal / linelen) * ndelta;
3389         }
3390         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
3391     } else {
3392         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
3393     }
3395     sp_node_adjust_handle(n, -which);
3397     return FALSE;
3400 /**
3401  * Node handle moved callback.
3402  */
3403 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3405    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3407    Inkscape::NodePath::NodeSide *me;
3408    Inkscape::NodePath::NodeSide *other;
3409     if (n->p.knot == knot) {
3410         me = &n->p;
3411         other = &n->n;
3412     } else if (n->n.knot == knot) {
3413         me = &n->n;
3414         other = &n->p;
3415     } else {
3416         me = NULL;
3417         other = NULL;
3418         g_assert_not_reached();
3419     }
3421     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3422     Radial rme(me->pos - n->pos);
3423     Radial rother(other->pos - n->pos);
3424     Radial rnew(*p - n->pos);
3426     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3427         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3428         /* 0 interpreted as "no snapping". */
3430         // The closest PI/snaps angle, starting from zero.
3431         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3432         if (me->origin_radial.a == HUGE_VAL) {
3433             // ortho doesn't exist: original handle was zero length.
3434             rnew.a = a_snapped;
3435         } else {
3436             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3437              * its opposite and perpendiculars). */
3438             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3440             // Snap to the closest.
3441             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3442                        ? a_snapped
3443                        : a_ortho );
3444         }
3445     }
3447     if (state & GDK_MOD1_MASK) {
3448         // lock handle length
3449         rnew.r = me->origin_radial.r;
3450     }
3452     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3453         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
3454         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3455         rother.a += rnew.a - rme.a;
3456         other->pos = NR::Point(rother) + n->pos;
3457         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3458         sp_knot_set_position(other->knot, &other->pos, 0);
3459     }
3461     me->pos = NR::Point(rnew) + n->pos;
3462     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3464     // move knot, but without emitting the signal:
3465     // we cannot emit a "moved" signal because we're now processing it
3466     sp_knot_moveto(me->knot, &(me->pos));
3468     update_object(n->subpath->nodepath);
3470     /* status text */
3471     SPDesktop *desktop = n->subpath->nodepath->desktop;
3472     if (!desktop) return;
3473     SPEventContext *ec = desktop->event_context;
3474     if (!ec) return;
3475     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3476     if (!mc) return;
3478     double degrees = 180 / M_PI * rnew.a;
3479     if (degrees > 180) degrees -= 360;
3480     if (degrees < -180) degrees += 360;
3481     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3482         degrees = angle_to_compass (degrees);
3484     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3486     mc->setF(Inkscape::NORMAL_MESSAGE,
3487          _("<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);
3489     g_string_free(length, TRUE);
3492 /**
3493  * Node handle event callback.
3494  */
3495 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3497     gboolean ret = FALSE;
3498     switch (event->type) {
3499         case GDK_KEY_PRESS:
3500             switch (get_group0_keyval (&event->key)) {
3501                 case GDK_space:
3502                     if (event->key.state & GDK_BUTTON1_MASK) {
3503                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3504                         stamp_repr(nodepath);
3505                         ret = TRUE;
3506                     }
3507                     break;
3508                 default:
3509                     break;
3510             }
3511             break;
3512         default:
3513             break;
3514     }
3516     return ret;
3519 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3520                                  Radial &rme, Radial &rother, gboolean const both)
3522     rme.a += angle;
3523     if ( both
3524          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3525          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3526     {
3527         rother.a += angle;
3528     }
3531 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3532                                         Radial &rme, Radial &rother, gboolean const both)
3534     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3536     gdouble r;
3537     if ( both
3538          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3539          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3540     {
3541         r = MAX(rme.r, rother.r);
3542     } else {
3543         r = rme.r;
3544     }
3546     gdouble const weird_angle = atan2(norm_angle, r);
3547 /* Bulia says norm_angle is just the visible distance that the
3548  * object's end must travel on the screen.  Left as 'angle' for want of
3549  * a better name.*/
3551     rme.a += weird_angle;
3552     if ( both
3553          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3554          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3555     {
3556         rother.a += weird_angle;
3557     }
3560 /**
3561  * Rotate one node.
3562  */
3563 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3565     Inkscape::NodePath::NodeSide *me, *other;
3566     bool both = false;
3568     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3569     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3571     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3572         me = &(n->p);
3573         other = &(n->n);
3574     } else if (!n->p.other) {
3575         me = &(n->n);
3576         other = &(n->p);
3577     } else {
3578         if (which > 0) { // right handle
3579             if (xn > xp) {
3580                 me = &(n->n);
3581                 other = &(n->p);
3582             } else {
3583                 me = &(n->p);
3584                 other = &(n->n);
3585             }
3586         } else if (which < 0){ // left handle
3587             if (xn <= xp) {
3588                 me = &(n->n);
3589                 other = &(n->p);
3590             } else {
3591                 me = &(n->p);
3592                 other = &(n->n);
3593             }
3594         } else { // both handles
3595             me = &(n->n);
3596             other = &(n->p);
3597             both = true;
3598         }
3599     }
3601     Radial rme(me->pos - n->pos);
3602     Radial rother(other->pos - n->pos);
3604     if (screen) {
3605         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3606     } else {
3607         node_rotate_one_internal (*n, angle, rme, rother, both);
3608     }
3610     me->pos = n->pos + NR::Point(rme);
3612     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3613         other->pos =  n->pos + NR::Point(rother);
3614     }
3616     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3617     // so here we just move all the knots without emitting move signals, for speed
3618     sp_node_update_handles(n, false);
3621 /**
3622  * Rotate selected nodes.
3623  */
3624 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3626     if (!nodepath || !nodepath->selected) return;
3628     if (g_list_length(nodepath->selected) == 1) {
3629        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3630         node_rotate_one (n, angle, which, screen);
3631     } else {
3632        // rotate as an object:
3634         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3635         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3636         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3637             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3638             box.expandTo (n->pos); // contain all selected nodes
3639         }
3641         gdouble rot;
3642         if (screen) {
3643             gdouble const zoom = nodepath->desktop->current_zoom();
3644             gdouble const zmove = angle / zoom;
3645             gdouble const r = NR::L2(box.max() - box.midpoint());
3646             rot = atan2(zmove, r);
3647         } else {
3648             rot = angle;
3649         }
3651         NR::Matrix t =
3652             NR::Matrix (NR::translate(-box.midpoint())) *
3653             NR::Matrix (NR::rotate(rot)) *
3654             NR::Matrix (NR::translate(box.midpoint()));
3656         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3657             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3658             n->pos *= t;
3659             n->n.pos *= t;
3660             n->p.pos *= t;
3661             sp_node_update_handles(n, false);
3662         }
3663     }
3665     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3668 /**
3669  * Scale one node.
3670  */
3671 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3673     bool both = false;
3674     Inkscape::NodePath::NodeSide *me, *other;
3676     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3677     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3679     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3680         me = &(n->p);
3681         other = &(n->n);
3682         n->code = NR_CURVETO;
3683     } else if (!n->p.other) {
3684         me = &(n->n);
3685         other = &(n->p);
3686         if (n->n.other)
3687             n->n.other->code = NR_CURVETO;
3688     } else {
3689         if (which > 0) { // right handle
3690             if (xn > xp) {
3691                 me = &(n->n);
3692                 other = &(n->p);
3693                 if (n->n.other)
3694                     n->n.other->code = NR_CURVETO;
3695             } else {
3696                 me = &(n->p);
3697                 other = &(n->n);
3698                 n->code = NR_CURVETO;
3699             }
3700         } else if (which < 0){ // left handle
3701             if (xn <= xp) {
3702                 me = &(n->n);
3703                 other = &(n->p);
3704                 if (n->n.other)
3705                     n->n.other->code = NR_CURVETO;
3706             } else {
3707                 me = &(n->p);
3708                 other = &(n->n);
3709                 n->code = NR_CURVETO;
3710             }
3711         } else { // both handles
3712             me = &(n->n);
3713             other = &(n->p);
3714             both = true;
3715             n->code = NR_CURVETO;
3716             if (n->n.other)
3717                 n->n.other->code = NR_CURVETO;
3718         }
3719     }
3721     Radial rme(me->pos - n->pos);
3722     Radial rother(other->pos - n->pos);
3724     rme.r += grow;
3725     if (rme.r < 0) rme.r = 0;
3726     if (rme.a == HUGE_VAL) {
3727         if (me->other) { // if direction is unknown, initialize it towards the next node
3728             Radial rme_next(me->other->pos - n->pos);
3729             rme.a = rme_next.a;
3730         } else { // if there's no next, initialize to 0
3731             rme.a = 0;
3732         }
3733     }
3734     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3735         rother.r += grow;
3736         if (rother.r < 0) rother.r = 0;
3737         if (rother.a == HUGE_VAL) {
3738             rother.a = rme.a + M_PI;
3739         }
3740     }
3742     me->pos = n->pos + NR::Point(rme);
3744     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3745         other->pos = n->pos + NR::Point(rother);
3746     }
3748     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3749     // so here we just move all the knots without emitting move signals, for speed
3750     sp_node_update_handles(n, false);
3753 /**
3754  * Scale selected nodes.
3755  */
3756 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3758     if (!nodepath || !nodepath->selected) return;
3760     if (g_list_length(nodepath->selected) == 1) {
3761         // scale handles of the single selected node
3762         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3763         node_scale_one (n, grow, which);
3764     } else {
3765         // scale nodes as an "object":
3767         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3768         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3769         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3770             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3771             box.expandTo (n->pos); // contain all selected nodes
3772         }
3774         double scale = (box.maxExtent() + grow)/box.maxExtent();
3776         NR::Matrix t =
3777             NR::Matrix (NR::translate(-box.midpoint())) *
3778             NR::Matrix (NR::scale(scale, scale)) *
3779             NR::Matrix (NR::translate(box.midpoint()));
3781         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3782             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3783             n->pos *= t;
3784             n->n.pos *= t;
3785             n->p.pos *= t;
3786             sp_node_update_handles(n, false);
3787         }
3788     }
3790     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
3793 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3795     if (!nodepath) return;
3796     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3799 /**
3800  * Flip selected nodes horizontally/vertically.
3801  */
3802 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3804     if (!nodepath || !nodepath->selected) return;
3806     if (g_list_length(nodepath->selected) == 1) {
3807         // flip handles of the single selected node
3808         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3809         double temp = n->p.pos[axis];
3810         n->p.pos[axis] = n->n.pos[axis];
3811         n->n.pos[axis] = temp;
3812         sp_node_update_handles(n, false);
3813     } else {
3814         // scale nodes as an "object":
3816         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3817         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3818         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3819             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3820             box.expandTo (n->pos); // contain all selected nodes
3821         }
3823         NR::Matrix t =
3824             NR::Matrix (NR::translate(-box.midpoint())) *
3825             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3826             NR::Matrix (NR::translate(box.midpoint()));
3828         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3829             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3830             n->pos *= t;
3831             n->n.pos *= t;
3832             n->p.pos *= t;
3833             sp_node_update_handles(n, false);
3834         }
3835     }
3837     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
3840 //-----------------------------------------------
3841 /**
3842  * Return new subpath under given nodepath.
3843  */
3844 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3846     g_assert(nodepath);
3847     g_assert(nodepath->desktop);
3849    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3851     s->nodepath = nodepath;
3852     s->closed = FALSE;
3853     s->nodes = NULL;
3854     s->first = NULL;
3855     s->last = NULL;
3857     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3858     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3859     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3861     return s;
3864 /**
3865  * Destroy nodes in subpath, then subpath itself.
3866  */
3867 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3869     g_assert(subpath);
3870     g_assert(subpath->nodepath);
3871     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3873     while (subpath->nodes) {
3874         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3875     }
3877     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3879     g_free(subpath);
3882 /**
3883  * Link head to tail in subpath.
3884  */
3885 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3887     g_assert(!sp->closed);
3888     g_assert(sp->last != sp->first);
3889     g_assert(sp->first->code == NR_MOVETO);
3891     sp->closed = TRUE;
3893     //Link the head to the tail
3894     sp->first->p.other = sp->last;
3895     sp->last->n.other  = sp->first;
3896     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3897     sp->first          = sp->last;
3899     //Remove the extra end node
3900     sp_nodepath_node_destroy(sp->last->n.other);
3903 /**
3904  * Open closed (loopy) subpath at node.
3905  */
3906 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3908     g_assert(sp->closed);
3909     g_assert(n->subpath == sp);
3910     g_assert(sp->first == sp->last);
3912     /* We create new startpoint, current node will become last one */
3914    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3915                                                 &n->pos, &n->pos, &n->n.pos);
3918     sp->closed        = FALSE;
3920     //Unlink to make a head and tail
3921     sp->first         = new_path;
3922     sp->last          = n;
3923     n->n.other        = NULL;
3924     new_path->p.other = NULL;
3927 /**
3928  * Returns area in triangle given by points; may be negative.
3929  */
3930 inline double
3931 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3933     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]);
3936 /**
3937  * Return new node in subpath with given properties.
3938  * \param pos Position of node.
3939  * \param ppos Handle position in previous direction
3940  * \param npos Handle position in previous direction
3941  */
3942 Inkscape::NodePath::Node *
3943 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)
3945     g_assert(sp);
3946     g_assert(sp->nodepath);
3947     g_assert(sp->nodepath->desktop);
3949     if (nodechunk == NULL)
3950         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3952     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3954     n->subpath  = sp;
3956     if (type != Inkscape::NodePath::NODE_NONE) {
3957         // use the type from sodipodi:nodetypes
3958         n->type = type;
3959     } else {
3960         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3961             // points are (almost) collinear
3962             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3963                 // endnode, or a node with a retracted handle
3964                 n->type = Inkscape::NodePath::NODE_CUSP;
3965             } else {
3966                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3967             }
3968         } else {
3969             n->type = Inkscape::NodePath::NODE_CUSP;
3970         }
3971     }
3973     n->code     = code;
3974     n->selected = FALSE;
3975     n->pos      = *pos;
3976     n->p.pos    = *ppos;
3977     n->n.pos    = *npos;
3979     n->dragging_out = NULL;
3981     Inkscape::NodePath::Node *prev;
3982     if (next) {
3983         //g_assert(g_list_find(sp->nodes, next));
3984         prev = next->p.other;
3985     } else {
3986         prev = sp->last;
3987     }
3989     if (prev)
3990         prev->n.other = n;
3991     else
3992         sp->first = n;
3994     if (next)
3995         next->p.other = n;
3996     else
3997         sp->last = n;
3999     n->p.other = prev;
4000     n->n.other = next;
4002     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"));
4003     sp_knot_set_position(n->knot, pos, 0);
4005     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4006     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4007     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4008     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4009     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4010     sp_knot_update_ctrl(n->knot);
4012     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4013     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4014     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4015     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4016     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4017     sp_knot_show(n->knot);
4019     // We only create handle knots and lines on demand
4020     n->p.knot = NULL;
4021     n->p.line = NULL;
4022     n->n.knot = NULL;
4023     n->n.line = NULL;
4025     sp->nodes = g_list_prepend(sp->nodes, n);
4027     return n;
4030 /**
4031  * Destroy node and its knots, link neighbors in subpath.
4032  */
4033 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4035     g_assert(node);
4036     g_assert(node->subpath);
4037     g_assert(SP_IS_KNOT(node->knot));
4039    Inkscape::NodePath::SubPath *sp = node->subpath;
4041     if (node->selected) { // first, deselect
4042         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4043         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4044     }
4046     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4048     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4049     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4050     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4051     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4052     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4053     g_object_unref(G_OBJECT(node->knot));
4055     if (node->p.knot) {
4056         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4057         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4058         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4059         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4060         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4061         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4062         g_object_unref(G_OBJECT(node->p.knot));
4063         node->p.knot = NULL;
4064     }
4066     if (node->n.knot) {
4067         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4068         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4069         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4070         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4071         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4072         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4073         g_object_unref(G_OBJECT(node->n.knot));
4074         node->n.knot = NULL;
4075     }
4077     if (node->p.line)
4078         gtk_object_destroy(GTK_OBJECT(node->p.line));
4079     if (node->n.line)
4080         gtk_object_destroy(GTK_OBJECT(node->n.line));
4082     if (sp->nodes) { // there are others nodes on the subpath
4083         if (sp->closed) {
4084             if (sp->first == node) {
4085                 g_assert(sp->last == node);
4086                 sp->first = node->n.other;
4087                 sp->last = sp->first;
4088             }
4089             node->p.other->n.other = node->n.other;
4090             node->n.other->p.other = node->p.other;
4091         } else {
4092             if (sp->first == node) {
4093                 sp->first = node->n.other;
4094                 sp->first->code = NR_MOVETO;
4095             }
4096             if (sp->last == node) sp->last = node->p.other;
4097             if (node->p.other) node->p.other->n.other = node->n.other;
4098             if (node->n.other) node->n.other->p.other = node->p.other;
4099         }
4100     } else { // this was the last node on subpath
4101         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4102     }
4104     g_mem_chunk_free(nodechunk, node);
4107 /**
4108  * Returns one of the node's two sides.
4109  * \param which Indicates which side.
4110  * \return Pointer to previous node side if which==-1, next if which==1.
4111  */
4112 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4114     g_assert(node);
4116     switch (which) {
4117         case -1:
4118             return &node->p;
4119         case 1:
4120             return &node->n;
4121         default:
4122             break;
4123     }
4125     g_assert_not_reached();
4127     return NULL;
4130 /**
4131  * Return the other side of the node, given one of its sides.
4132  */
4133 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4135     g_assert(node);
4137     if (me == &node->p) return &node->n;
4138     if (me == &node->n) return &node->p;
4140     g_assert_not_reached();
4142     return NULL;
4145 /**
4146  * Return NRPathcode on the given side of the node.
4147  */
4148 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4150     g_assert(node);
4152     if (me == &node->p) {
4153         if (node->p.other) return (NRPathcode)node->code;
4154         return NR_MOVETO;
4155     }
4157     if (me == &node->n) {
4158         if (node->n.other) return (NRPathcode)node->n.other->code;
4159         return NR_MOVETO;
4160     }
4162     g_assert_not_reached();
4164     return NR_END;
4167 /**
4168  * Return node with the given index
4169  */
4170 Inkscape::NodePath::Node *
4171 sp_nodepath_get_node_by_index(int index)
4173     Inkscape::NodePath::Node *e = NULL;
4175     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4176     if (!nodepath) {
4177         return e;
4178     }
4180     //find segment
4181     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4183         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4184         int n = g_list_length(sp->nodes);
4185         if (sp->closed) {
4186             n++;
4187         }
4189         //if the piece belongs to this subpath grab it
4190         //otherwise move onto the next subpath
4191         if (index < n) {
4192             e = sp->first;
4193             for (int i = 0; i < index; ++i) {
4194                 e = e->n.other;
4195             }
4196             break;
4197         } else {
4198             if (sp->closed) {
4199                 index -= (n+1);
4200             } else {
4201                 index -= n;
4202             }
4203         }
4204     }
4206     return e;
4209 /**
4210  * Returns plain text meaning of node type.
4211  */
4212 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4214     unsigned retracted = 0;
4215     bool endnode = false;
4217     for (int which = -1; which <= 1; which += 2) {
4218         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4219         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4220             retracted ++;
4221         if (!side->other)
4222             endnode = true;
4223     }
4225     if (retracted == 0) {
4226         if (endnode) {
4227                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4228                 return _("end node");
4229         } else {
4230             switch (node->type) {
4231                 case Inkscape::NodePath::NODE_CUSP:
4232                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4233                     return _("cusp");
4234                 case Inkscape::NodePath::NODE_SMOOTH:
4235                     // TRANSLATORS: "smooth" is an adjective here
4236                     return _("smooth");
4237                 case Inkscape::NodePath::NODE_SYMM:
4238                     return _("symmetric");
4239             }
4240         }
4241     } else if (retracted == 1) {
4242         if (endnode) {
4243             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4244             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4245         } else {
4246             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4247         }
4248     } else {
4249         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4250     }
4252     return NULL;
4255 /**
4256  * Handles content of statusbar as long as node tool is active.
4257  */
4258 void
4259 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4261     gchar const *when_selected = _("<b>Drag</b> nodes or node handles; <b>Alt+drag</b> nodes to sculpt; <b>arrow</b> keys to move nodes, <b>&lt; &gt;</b> to scale, <b>[ ]</b> to rotate");
4262     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4264     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4265     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4266     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4267     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4269     SPDesktop *desktop = NULL;
4270     if (nodepath) {
4271         desktop = nodepath->desktop;
4272     } else {
4273         desktop = SP_ACTIVE_DESKTOP;
4274     }
4276     SPEventContext *ec = desktop->event_context;
4277     if (!ec) return;
4278     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4279     if (!mc) return;
4281     if (selected_nodes == 0) {
4282         Inkscape::Selection *sel = desktop->selection;
4283         if (!sel || sel->isEmpty()) {
4284             mc->setF(Inkscape::NORMAL_MESSAGE,
4285                      _("Select a single object to edit its nodes or handles."));
4286         } else {
4287             if (nodepath) {
4288             mc->setF(Inkscape::NORMAL_MESSAGE,
4289                      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.",
4290                               "<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.",
4291                               total_nodes),
4292                      total_nodes);
4293             } else {
4294                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4295                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4296                 } else {
4297                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4298                 }
4299             }
4300         }
4301     } else if (nodepath && selected_nodes == 1) {
4302         mc->setF(Inkscape::NORMAL_MESSAGE,
4303                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4304                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4305                           total_nodes),
4306                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4307     } else {
4308         if (selected_subpaths > 1) {
4309             mc->setF(Inkscape::NORMAL_MESSAGE,
4310                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4311                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4312                               total_nodes),
4313                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4314         } else {
4315             mc->setF(Inkscape::NORMAL_MESSAGE,
4316                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4317                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4318                               total_nodes),
4319                      selected_nodes, total_nodes, when_selected);
4320         }
4321     }
4325 /*
4326   Local Variables:
4327   mode:c++
4328   c-file-style:"stroustrup"
4329   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4330   indent-tabs-mode:nil
4331   fill-column:99
4332   End:
4333 */
4334 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :