Code

add mouse-over helperpath for rects
[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/canvas-bpath.h"
19 #include "display/curve.h"
20 #include "display/sp-ctrlline.h"
21 #include "display/sodipodi-ctrl.h"
22 #include "display/sp-canvas-util.h"
23 #include <glibmm/i18n.h>
24 #include "2geom/pathvector.h"
25 #include "2geom/sbasis-to-bezier.h"
26 #include "2geom/bezier-curve.h"
27 #include "2geom/hvlinesegment.h"
28 #include "helper/units.h"
29 #include "helper/geom.h"
30 #include "knot.h"
31 #include "inkscape.h"
32 #include "document.h"
33 #include "sp-namedview.h"
34 #include "desktop.h"
35 #include "desktop-handles.h"
36 #include "snap.h"
37 #include "message-stack.h"
38 #include "message-context.h"
39 #include "node-context.h"
40 #include "lpe-tool-context.h"
41 #include "shape-editor.h"
42 #include "selection-chemistry.h"
43 #include "selection.h"
44 #include "xml/repr.h"
45 #include "preferences.h"
46 #include "sp-metrics.h"
47 #include "sp-path.h"
48 #include "sp-rect.h"
49 #include "libnr/nr-matrix-ops.h"
50 #include "svg/svg.h"
51 #include "verbs.h"
52 #include <2geom/bezier-utils.h>
53 #include <vector>
54 #include <algorithm>
55 #include <cstring>
56 #include <cmath>
57 #include "live_effects/lpeobject.h"
58 #include "live_effects/lpeobject-reference.h"
59 #include "live_effects/effect.h"
60 #include "live_effects/parameter/parameter.h"
61 #include "live_effects/parameter/path.h"
62 #include "util/mathfns.h"
63 #include "display/snap-indicator.h"
64 #include "snapped-point.h"
66 namespace Geom { class Matrix; }
68 /// \todo
69 /// evil evil evil. FIXME: conflict of two different Path classes!
70 /// There is a conflict in the namespace between two classes named Path.
71 /// #include "sp-flowtext.h"
72 /// #include "sp-flowregion.h"
74 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
75 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
76 GType sp_flowregion_get_type (void);
77 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
78 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
79 GType sp_flowtext_get_type (void);
80 // end evil workaround
82 #include "helper/stlport.h"
85 /// \todo fixme: Implement these via preferences */
87 #define NODE_FILL          0xbfbfbf00
88 #define NODE_STROKE        0x000000ff
89 #define NODE_FILL_HI       0xff000000
90 #define NODE_STROKE_HI     0x000000ff
91 #define NODE_FILL_SEL      0x0000ffff
92 #define NODE_STROKE_SEL    0x000000ff
93 #define NODE_FILL_SEL_HI   0xff000000
94 #define NODE_STROKE_SEL_HI 0x000000ff
95 #define KNOT_FILL          0xffffffff
96 #define KNOT_STROKE        0x000000ff
97 #define KNOT_FILL_HI       0xff000000
98 #define KNOT_STROKE_HI     0x000000ff
100 static GMemChunk *nodechunk = NULL;
102 /* Creation from object */
104 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t);
105 static Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length);
107 /* Object updating */
109 static void stamp_repr(Inkscape::NodePath::Path *np);
110 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
111 static gchar *create_typestr(Inkscape::NodePath::Path *np);
113 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
115 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
117 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
119 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
121 /* Adjust handle placement, if the node or the other handle is moved */
122 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
123 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
124 static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node);
126 /* Node event callbacks */
127 static void node_clicked(SPKnot *knot, guint state, gpointer data);
128 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
129 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
130 static gboolean node_request(SPKnot *knot, Geom::Point const &p, guint state, gpointer data);
132 /* Handle event callbacks */
133 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
134 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
135 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
136 static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, gpointer data);
137 static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data);
138 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
140 /* Constructors and destructors */
142 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
143 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
144 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
145 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
146 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
147                                          Geom::Point *ppos, Geom::Point *pos, Geom::Point *npos);
148 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
150 /* Helpers */
152 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
153 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
154 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
156 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
157 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
159 // active_node indicates mouseover node
160 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
162 static SPCanvasItem *
163 sp_nodepath_make_helper_item(Inkscape::NodePath::Path *np, /*SPDesktop *desktop, */const SPCurve *curve, bool show = false) {
164     SPCurve *helper_curve = curve->copy();
165     helper_curve->transform(np->i2d);
166     SPCanvasItem *helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
167     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
168     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helper_path), 0, SP_WIND_RULE_NONZERO);
169     sp_canvas_item_move_to_z(helper_path, 0);
170     if (show) {
171         sp_canvas_item_show(helper_path);
172     }
173     helper_curve->unref();
174     return helper_path;
177 static SPCanvasItem *
178 canvasitem_from_pathvec(Inkscape::NodePath::Path *np, Geom::PathVector const &pathv, bool show) {
179     SPCurve *helper_curve = new SPCurve(pathv);
180     return sp_nodepath_make_helper_item(np, helper_curve, show);
183 static void
184 sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) {
185     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> > helper_path_vec;
186     if (!SP_IS_LPE_ITEM(np->item)) {
187         g_print ("Only LPEItems can have helperpaths!\n");
188         return;
189     }
191     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
192     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
193     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
194         Inkscape::LivePathEffect::LPEObjectReference *lperef = (*i);
195         Inkscape::LivePathEffect::Effect *lpe = lperef->lpeobject->get_lpe();
196         if (lpe) {
197             // create new canvas items from the effect's helper paths
198             std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
199             for (std::vector<Geom::PathVector>::iterator j = hpaths.begin(); j != hpaths.end(); ++j) {
200                 np->helper_path_vec[lpe].push_back(canvasitem_from_pathvec(np, *j, true));
201             }
202         }
203     }
206 void
207 sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np) {
208     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> > helper_path_vec;
209     if (!SP_IS_LPE_ITEM(np->item)) {
210         g_print ("Only LPEItems can have helperpaths!\n");
211         return;
212     }
214     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
215     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
216     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
217         Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->get_lpe();
218         if (lpe) {
219             /* update canvas items from the effect's helper paths; note that this code relies on the
220              * fact that getHelperPaths() will always return the same number of helperpaths in the same
221              * order as during their creation in sp_nodepath_create_helperpaths
222              */
223             std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
224             for (unsigned int j = 0; j < hpaths.size(); ++j) {
225                 SPCurve *curve = new SPCurve(hpaths[j]);
226                 curve->transform(np->i2d);
227                 sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH((np->helper_path_vec[lpe])[j]), curve);
228                 curve = curve->unref();
229             }
230         }
231     }
234 static void
235 sp_nodepath_destroy_helperpaths(Inkscape::NodePath::Path *np) {
236     for (HelperPathList::iterator i = np->helper_path_vec.begin(); i != np->helper_path_vec.end(); ++i) {
237         for (std::vector<SPCanvasItem *>::iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) {
238             GtkObject *temp = *j;
239             *j = NULL;
240             gtk_object_destroy(temp);
241         }
242     }
243     np->helper_path_vec.clear();
247 /**
248  * \brief Creates new nodepath from item
249  *
250  * \todo create proper constructor for nodepath::path, this method returns null a constructor cannot so this cannot be simply converted to constructor.
251  */
252 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
254     Inkscape::XML::Node *repr = object->repr;
256     /** \todo
257      * FIXME: remove this. We don't want to edit paths inside flowtext.
258      * Instead we will build our flowtext with cloned paths, so that the
259      * real paths are outside the flowtext and thus editable as usual.
260      */
261     if (SP_IS_FLOWTEXT(object)) {
262         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
263             if SP_IS_FLOWREGION(child) {
264                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
265                 if (grandchild && SP_IS_PATH(grandchild)) {
266                     object = SP_ITEM(grandchild);
267                     break;
268                 }
269             }
270         }
271     }
273     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
275     if (curve == NULL) {
276         return NULL;
277     }
279     if (curve->get_segment_count() < 1) {
280         curve->unref();
281         return NULL; // prevent crash for one-node paths
282     }
284     //Create new nodepath
285     Inkscape::NodePath::Path *np = new Inkscape::NodePath::Path();
286     if (!np) {
287         curve->unref();
288         return NULL;
289     }
291     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
293     // Set defaults
294     np->desktop     = desktop;
295     np->object      = object;
296     np->subpaths    = NULL;
297     np->selected    = NULL;
298     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
299     np->local_change = 0;
300     np->show_handles = show_handles;
301     np->helper_path = NULL;
302     np->helperpath_rgba = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
303     np->helperpath_width = 1.0;
304     np->curve = curve->copy();
305     np->show_helperpath = prefs->getBool("/tools/nodes/show_helperpath");
306     if (SP_IS_LPE_ITEM(object)) {
307         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
308         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
309             np->show_helperpath = true;
310         }
311     }
312     np->straight_path = false;
313     if (IS_LIVEPATHEFFECT(object) && item) {
314         np->item = item;
315     } else {
316         np->item = SP_ITEM(object);
317     }
319     np->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
321     // we need to update item's transform from the repr here,
322     // because they may be out of sync when we respond
323     // to a change in repr by regenerating nodepath     --bb
324     sp_object_read_attr(SP_OBJECT(np->item), "transform");
326     np->i2d  = sp_item_i2d_affine(np->item);
327     np->d2i  = np->i2d.inverse();
329     np->repr = repr;
330     if (repr_key_in) { // apparently the object is an LPEObject (this is a dirty check, hopefully nobody tries feeding non-lpeobjects into this method with non-null repr_key_in)
331         np->repr_key = g_strdup(repr_key_in);
332         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
333         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(object)->get_lpe();
334         if (!lpe) {
335             g_error("sp_nodepath_new: lpeobject without real lpe passed as argument!");
336             delete np;
337         }
338         Inkscape::LivePathEffect::Parameter *lpeparam = lpe->getParameter(repr_key_in);
339         if (lpeparam) {
340             lpeparam->param_setup_nodepath(np);
341         }
342     } else {
343         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
344         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
345             np->repr_key = g_strdup("inkscape:original-d");
347             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
348             if (lpe) {
349                 lpe->setup_nodepath(np);
350             }
351         } else {
352             np->repr_key = g_strdup("d");
353         }
354     }
356     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
357      * So for example a closed rectangle has a nodetypestring of length 5.
358      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
359     Geom::PathVector pathv_sanitized = pathv_to_linear_and_cubic_beziers(np->curve->get_pathvector());
360     np->curve->set_pathvector(pathv_sanitized);
361     guint length = np->curve->get_segment_count();
362     for (Geom::PathVector::const_iterator pit = pathv_sanitized.begin(); pit != pathv_sanitized.end(); ++pit) {
363         length += pit->empty() ? 0 : 1;
364     }
366     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
367     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
369     // create the subpath(s) from the bpath
370     subpaths_from_pathvector(np, pathv_sanitized, typestr);
372     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
373     np->subpaths = g_list_reverse(np->subpaths);
375     delete[] typestr;
376     curve->unref();
378     // Draw helper curve
379     if (np->show_helperpath) {
380         np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true);
381     }
383     sp_nodepath_create_helperpaths(np);
385     return np;
388 /**
389  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
390  */
391 Inkscape::NodePath::Path::~Path() {
392     while (this->subpaths) {
393         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) this->subpaths->data);
394     }
396     //Inform the ShapeEditor that made me, if any, that I am gone.
397     if (this->shape_editor)
398         this->shape_editor->nodepath_destroyed();
400     g_assert(!this->selected);
402     if (this->helper_path) {
403         GtkObject *temp = this->helper_path;
404         this->helper_path = NULL;
405         gtk_object_destroy(temp);
406     }
407     if (this->curve) {
408         this->curve->unref();
409         this->curve = NULL;
410     }
412     if (this->repr_key) {
413         g_free(this->repr_key);
414         this->repr_key = NULL;
415     }
416     if (this->repr_nodetypes_key) {
417         g_free(this->repr_nodetypes_key);
418         this->repr_nodetypes_key = NULL;
419     }
421     sp_nodepath_destroy_helperpaths(this);
423     this->desktop = NULL;
426 /**
427  *  Return the node count of a given NodeSubPath.
428  */
429 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
431     int nodeCount = 0;
433     if (subpath) {
434         nodeCount = g_list_length(subpath->nodes);
435     }
437     return nodeCount;
440 /**
441  *  Return the node count of a given NodePath.
442  */
443 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
445     gint nodeCount = 0;
446     if (np) {
447         for (GList *item = np->subpaths ; item ; item=item->next) {
448             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
449             nodeCount += g_list_length(subpath->nodes);
450         }
451     }
452     return nodeCount;
455 /**
456  *  Return the subpath count of a given NodePath.
457  */
458 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
460     gint nodeCount = 0;
461     if (np) {
462         nodeCount = g_list_length(np->subpaths);
463     }
464     return nodeCount;
467 /**
468  *  Return the selected node count of a given NodePath.
469  */
470 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
472     gint nodeCount = 0;
473     if (np) {
474         nodeCount = g_list_length(np->selected);
475     }
476     return nodeCount;
479 /**
480  *  Return the number of subpaths where nodes are selected in a given NodePath.
481  */
482 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
484     gint nodeCount = 0;
485     if (np && np->selected) {
486         if (!np->selected->next) {
487             nodeCount = 1;
488         } else {
489             for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
490                 Inkscape::NodePath::SubPath *subpath = static_cast<Inkscape::NodePath::SubPath *>(spl->data);
491                 for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
492                     Inkscape::NodePath::Node *node = static_cast<Inkscape::NodePath::Node *>(nl->data);
493                     if (node->selected) {
494                         nodeCount++;
495                         break;
496                     }
497                 }
498             }
499         }
500     }
501     return nodeCount;
504 /**
505  * Clean up a nodepath after editing.
506  *
507  * Currently we are deleting trivial subpaths.
508  */
509 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
511     GList *badSubPaths = NULL;
513     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
514     for (GList *l = nodepath->subpaths; l ; l=l->next) {
515        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
516        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
517             badSubPaths = g_list_append(badSubPaths, sp);
518     }
520     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
521     //also removes the subpath from nodepath->subpaths
522     for (GList *l = badSubPaths; l ; l=l->next) {
523        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
524         sp_nodepath_subpath_destroy(sp);
525     }
527     g_list_free(badSubPaths);
530 /**
531  * Create new nodepaths from pathvector, make it subpaths of np.
532  * \param t The node type array.
533  */
534 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
536     guint i = 0;  // index into node type array
537     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
538         if (pit->empty())
539             continue;  // don't add single knot paths
541         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
543         Geom::Point ppos = pit->initialPoint() * np->i2d;
544         NRPathcode pcode = NR_MOVETO;
546         /* Johan: Note that this is pretty arcane code. I am pretty sure it is working correctly, be very certain to change it! (better to just rewrite this whole method)*/
547         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
548             if( dynamic_cast<Geom::LineSegment const*>(&*cit) ||
549                 dynamic_cast<Geom::HLineSegment const*>(&*cit) ||
550                 dynamic_cast<Geom::VLineSegment const*>(&*cit) )
551             {
552                 Geom::Point pos = cit->initialPoint() * (Geom::Matrix)np->i2d;
553                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
555                 ppos = cit->finalPoint() * (Geom::Matrix)np->i2d;
556                 pcode = NR_LINETO;
557             }
558             else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
559                 std::vector<Geom::Point> points = cubic_bezier->points();
560                 Geom::Point pos = points[0] * (Geom::Matrix)np->i2d;
561                 Geom::Point npos = points[1] * (Geom::Matrix)np->i2d;
562                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
564                 ppos = points[2] * (Geom::Matrix)np->i2d;
565                 pcode = NR_CURVETO;
566             }
567         }
569         if (pit->closed()) {
570             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
571             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
572              * If the length is zero, don't add it to the nodepath. */
573             Geom::Curve const &closing_seg = pit->back_closed();
574             // Don't use !closing_seg.isDegenerate() as it is too precise, and does not account for floating point rounding probs (LP bug #257289)
575             if ( ! are_near(closing_seg.initialPoint(), closing_seg.finalPoint()) ) {
576                 Geom::Point pos = closing_seg.finalPoint() * (Geom::Matrix)np->i2d;
577                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
578             }
580             sp_nodepath_subpath_close(sp);
581         }
582     }
585 /**
586  * Convert from sodipodi:nodetypes to new style type array.
587  */
588 static
589 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
591     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
593     guint pos = 0;
595     if (types) {
596         for (guint i = 0; types[i] && ( i < length ); i++) {
597             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
598             if (types[i] != '\0') {
599                 switch (types[i]) {
600                     case 's':
601                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
602                         break;
603                     case 'a':
604                         typestr[pos++] =Inkscape::NodePath::NODE_AUTO;
605                         break;
606                     case 'z':
607                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
608                         break;
609                     case 'c':
610                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
611                         break;
612                     default:
613                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
614                         break;
615                 }
616             }
617         }
618     }
620     while (pos < length) {
621         typestr[pos++] = Inkscape::NodePath::NODE_NONE;
622     }
624     return typestr;
627 /**
628  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
629  * updated but repr is not (for speed). Used during curve and node drag.
630  */
631 static void update_object(Inkscape::NodePath::Path *np)
633     g_assert(np);
635     np->curve->unref();
636     np->curve = create_curve(np);
638     sp_nodepath_set_curve(np, np->curve);
640     if (np->show_helperpath) {
641         SPCurve * helper_curve = np->curve->copy();
642         helper_curve->transform(np->i2d);
643         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
644         helper_curve->unref();
645     }
647     // updating helperpaths of LPEItems is now done in sp_lpe_item_update();
648     //sp_nodepath_update_helperpaths(np);
650     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
651     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
652     np->shape_editor->update_knotholder();
655 /**
656  * Update XML path node with data from path object.
657  */
658 static void update_repr_internal(Inkscape::NodePath::Path *np)
660     g_assert(np);
662     Inkscape::XML::Node *repr = np->object->repr;
664     np->curve->unref();
665     np->curve = create_curve(np);
667     gchar *typestr = create_typestr(np);
668     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
670     // determine if path has an effect applied and write to correct "d" attribute.
671     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
672         np->local_change++;
673         repr->setAttribute(np->repr_key, svgpath);
674     }
676     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
677         np->local_change++;
678         repr->setAttribute(np->repr_nodetypes_key, typestr);
679     }
681     g_free(svgpath);
682     g_free(typestr);
684     if (np->show_helperpath) {
685         SPCurve * helper_curve = np->curve->copy();
686         helper_curve->transform(np->i2d);
687         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
688         helper_curve->unref();
689     }
691     // TODO: do we need this call here? after all, update_object() should have been called just before
692     //sp_nodepath_update_helperpaths(np);
695 /**
696  * Update XML path node with data from path object, commit changes forever.
697  */
698 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
700     //fixme: np can be NULL, so check before proceeding
701     g_return_if_fail(np != NULL);
703     update_repr_internal(np);
704     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
706     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
707                      annotation);
710 /**
711  * Update XML path node with data from path object, commit changes with undo.
712  */
713 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
715     update_repr_internal(np);
716     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
717                            annotation);
720 /**
721  * Make duplicate of path, replace corresponding XML node in tree, commit.
722  */
723 static void stamp_repr(Inkscape::NodePath::Path *np)
725     g_assert(np);
727     Inkscape::XML::Node *old_repr = np->object->repr;
728     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
730     // remember the position of the item
731     gint pos = old_repr->position();
732     // remember parent
733     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
735     SPCurve *curve = create_curve(np);
736     gchar *typestr = create_typestr(np);
738     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
740     new_repr->setAttribute(np->repr_key, svgpath);
741     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
743     // add the new repr to the parent
744     parent->appendChild(new_repr);
745     // move to the saved position
746     new_repr->setPosition(pos > 0 ? pos : 0);
748     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
749                      _("Stamp"));
751     Inkscape::GC::release(new_repr);
752     g_free(svgpath);
753     g_free(typestr);
754     curve->unref();
757 /**
758  * Create curve from path.
759  */
760 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
762     SPCurve *curve = new SPCurve();
764     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
765        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
766        curve->moveto(sp->first->pos * np->d2i);
767        Inkscape::NodePath::Node *n = sp->first->n.other;
768         while (n) {
769             Geom::Point const end_pt = n->pos * np->d2i;
770             if (!IS_FINITE(n->pos[0]) || !IS_FINITE(n->pos[1])){
771                 g_message("niet finite");
772             }
773             switch (n->code) {
774                 case NR_LINETO:
775                     curve->lineto(end_pt);
776                     break;
777                 case NR_CURVETO:
778                     curve->curveto(n->p.other->n.pos * np->d2i,
779                                      n->p.pos * np->d2i,
780                                      end_pt);
781                     break;
782                 default:
783                     g_assert_not_reached();
784                     break;
785             }
786             if (n != sp->last) {
787                 n = n->n.other;
788             } else {
789                 n = NULL;
790             }
791         }
792         if (sp->closed) {
793             curve->closepath();
794         }
795     }
797     return curve;
800 /**
801  * Convert path type string to sodipodi:nodetypes style.
802  */
803 static gchar *create_typestr(Inkscape::NodePath::Path *np)
805     gchar *typestr = g_new(gchar, 32);
806     gint len = 32;
807     gint pos = 0;
809     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
810        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
812         if (pos >= len) {
813             typestr = g_renew(gchar, typestr, len + 32);
814             len += 32;
815         }
817         typestr[pos++] = 'c';
819        Inkscape::NodePath::Node *n;
820         n = sp->first->n.other;
821         while (n) {
822             gchar code;
824             switch (n->type) {
825                 case Inkscape::NodePath::NODE_CUSP:
826                     code = 'c';
827                     break;
828                 case Inkscape::NodePath::NODE_SMOOTH:
829                     code = 's';
830                     break;
831                 case Inkscape::NodePath::NODE_AUTO:
832                     code = 'a';
833                     break;
834                 case Inkscape::NodePath::NODE_SYMM:
835                     code = 'z';
836                     break;
837                 default:
838                     g_assert_not_reached();
839                     code = '\0';
840                     break;
841             }
843             if (pos >= len) {
844                 typestr = g_renew(gchar, typestr, len + 32);
845                 len += 32;
846             }
848             typestr[pos++] = code;
850             if (n != sp->last) {
851                 n = n->n.other;
852             } else {
853                 n = NULL;
854             }
855         }
856     }
858     if (pos >= len) {
859         typestr = g_renew(gchar, typestr, len + 1);
860         len += 1;
861     }
863     typestr[pos++] = '\0';
865     return typestr;
868 // Returns different message contexts depending on the current context. This function should only
869 // be called when ec is either a SPNodeContext or SPLPEToolContext, thus we return NULL in all
870 // other cases.
871 static Inkscape::MessageContext *
872 get_message_context(SPEventContext *ec)
874     Inkscape::MessageContext *mc = 0;
876     if (SP_IS_NODE_CONTEXT(ec)) {
877         mc = SP_NODE_CONTEXT(ec)->_node_message_context;
878     } else if (SP_IS_LPETOOL_CONTEXT(ec)) {
879         mc = SP_LPETOOL_CONTEXT(ec)->_lpetool_message_context;
880     } else {
881         g_warning ("Nodepath should only be present in Node tool or Geometric tool.");
882     }
884     return mc;
887 /**
888  \brief Fills node and handle positions for three nodes, splitting line
889   marked by end at distance t.
890  */
891 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
893     g_assert(new_path != NULL);
894     g_assert(end      != NULL);
896     g_assert(end->p.other == new_path);
897    Inkscape::NodePath::Node *start = new_path->p.other;
898     g_assert(start);
900     if (end->code == NR_LINETO) {
901         new_path->type =Inkscape::NodePath::NODE_CUSP;
902         new_path->code = NR_LINETO;
903         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
904     } else {
905         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
906         new_path->code = NR_CURVETO;
907         gdouble s      = 1 - t;
908         for (int dim = 0; dim < 2; dim++) {
909             Geom::Coord const f000 = start->pos[dim];
910             Geom::Coord const f001 = start->n.pos[dim];
911             Geom::Coord const f011 = end->p.pos[dim];
912             Geom::Coord const f111 = end->pos[dim];
913             Geom::Coord const f00t = s * f000 + t * f001;
914             Geom::Coord const f01t = s * f001 + t * f011;
915             Geom::Coord const f11t = s * f011 + t * f111;
916             Geom::Coord const f0tt = s * f00t + t * f01t;
917             Geom::Coord const f1tt = s * f01t + t * f11t;
918             Geom::Coord const fttt = s * f0tt + t * f1tt;
919             start->n.pos[dim]    = f00t;
920             new_path->p.pos[dim] = f0tt;
921             new_path->pos[dim]   = fttt;
922             new_path->n.pos[dim] = f1tt;
923             end->p.pos[dim]      = f11t;
924         }
925     }
928 /**
929  * Adds new node on direct line between two nodes, activates handles of all
930  * three nodes.
931  */
932 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
934     g_assert(end);
935     g_assert(end->subpath);
936     g_assert(g_list_find(end->subpath->nodes, end));
938    Inkscape::NodePath::Node *start = end->p.other;
939     g_assert( start->n.other == end );
940    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
941                                                end,
942                                                (NRPathcode)end->code == NR_LINETO?
943                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
944                                                (NRPathcode)end->code,
945                                                &start->pos, &start->pos, &start->n.pos);
946     sp_nodepath_line_midpoint(newnode, end, t);
948     sp_node_adjust_handles(start);
949     sp_node_update_handles(start);
950     sp_node_update_handles(newnode);
951     sp_node_adjust_handles(end);
952     sp_node_update_handles(end);
954     return newnode;
957 /**
958 \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
959 */
960 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
962     g_assert(node);
963     g_assert(node->subpath);
964     g_assert(g_list_find(node->subpath->nodes, node));
966     Inkscape::NodePath::Node* result = 0;
967     Inkscape::NodePath::SubPath *sp = node->subpath;
968     Inkscape::NodePath::Path *np    = sp->nodepath;
970     if (sp->closed) {
971         sp_nodepath_subpath_open(sp, node);
972         result = sp->first;
973     } else if ( (node == sp->first) || (node == sp->last ) ){
974         // no break for end nodes
975         result = 0;
976     } else {
977         // create a new subpath
978         Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
980         // duplicate the break node as start of the new subpath
981         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL,
982                                                                  static_cast<Inkscape::NodePath::NodeType>(node->type),
983                                                                  NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
985         // attach rest of curve to new node
986         g_assert(node->n.other);
987         newnode->n.other = node->n.other; node->n.other = NULL;
988         newnode->n.other->p.other = newnode;
989         newsubpath->last = sp->last;
990         sp->last = node;
991         node = newnode;
992         while (node->n.other) {
993             node = node->n.other;
994             node->subpath = newsubpath;
995             sp->nodes = g_list_remove(sp->nodes, node);
996             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
997         }
1000         result = newnode;
1001     }
1002     return result;
1005 /**
1006  * Duplicate node and connect to neighbours.
1007  */
1008 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
1010     g_assert(node);
1011     g_assert(node->subpath);
1012     g_assert(g_list_find(node->subpath->nodes, node));
1014    Inkscape::NodePath::SubPath *sp = node->subpath;
1016     NRPathcode code = (NRPathcode) node->code;
1017     if (code == NR_MOVETO) { // if node is the endnode,
1018         node->code = NR_LINETO; // new one is inserted before it, so change that to line
1019     }
1021     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
1023     if (!node->n.other || !node->p.other) { // if node is an endnode, select it
1024         return node;
1025     } else {
1026         return newnode; // otherwise select the newly created node
1027     }
1030 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
1032     node->p.pos = (node->pos + (node->pos - node->n.pos));
1035 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
1037     node->n.pos = (node->pos + (node->pos - node->p.pos));
1040 /**
1041  * Change line type at node, with side effects on neighbours.
1042  */
1043 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
1045     g_assert(end);
1046     g_assert(end->subpath);
1047     g_assert(end->p.other);
1049     if (end->code != static_cast<guint>(code) ) {
1050         Inkscape::NodePath::Node *start = end->p.other;
1052         end->code = code;
1054         if (code == NR_LINETO) {
1055             if (start->code == NR_LINETO) {
1056                 sp_nodepath_set_node_type(start, Inkscape::NodePath::NODE_CUSP);
1057             }
1058             if (end->n.other) {
1059                 if (end->n.other->code == NR_LINETO) {
1060                     sp_nodepath_set_node_type(end, Inkscape::NodePath::NODE_CUSP);
1061                 }
1062             }
1064             if (start->type == Inkscape::NodePath::NODE_AUTO)
1065                 start->type = Inkscape::NodePath::NODE_SMOOTH;
1066             if (end->type == Inkscape::NodePath::NODE_AUTO)
1067                 end->type = Inkscape::NodePath::NODE_SMOOTH;
1069             start->n.pos = start->pos;
1070             end->p.pos = end->pos;
1072             sp_node_adjust_handle(start, -1);
1073             sp_node_adjust_handle(end, 1);
1075         } else {
1076             Geom::Point delta = end->pos - start->pos;
1077             start->n.pos = start->pos + delta / 3;
1078             end->p.pos = end->pos - delta / 3;
1079             sp_node_adjust_handle(start, 1);
1080             sp_node_adjust_handle(end, -1);
1081         }
1083         sp_node_update_handles(start);
1084         sp_node_update_handles(end);
1085     }
1088 static void
1089 sp_nodepath_update_node_knot(Inkscape::NodePath::Node *node)
1091     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1092         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1093         node->knot->setSize (node->selected? 11 : 9);
1094         sp_knot_update_ctrl(node->knot);
1095     } else if (node->type == Inkscape::NodePath::NODE_AUTO) {
1096         node->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1097         node->knot->setSize (node->selected? 11 : 9);
1098         sp_knot_update_ctrl(node->knot);
1099     } else {
1100         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1101         node->knot->setSize (node->selected? 9 : 7);
1102         sp_knot_update_ctrl(node->knot);
1103     }
1107 /**
1108  * Change node type, and its handles accordingly.
1109  */
1110 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1112     g_assert(node);
1113     g_assert(node->subpath);
1115     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1116         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1117             type =Inkscape::NodePath::NODE_CUSP;
1118         }
1119     }
1121     node->type = type;
1123     sp_nodepath_update_node_knot(node);
1125     // if one of handles is mouseovered, preserve its position
1126     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1127         sp_node_adjust_handle(node, 1);
1128     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1129         sp_node_adjust_handle(node, -1);
1130     } else {
1131         sp_node_adjust_handles(node);
1132     }
1134     sp_node_update_handles(node);
1136     sp_nodepath_update_statusbar(node->subpath->nodepath);
1138     return node;
1141 bool
1142 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1144 // TODO clean up multiple returns
1145         Inkscape::NodePath::Node *othernode = side->other;
1146         if (!othernode)
1147             return false;
1148         NRPathcode const code = sp_node_path_code_from_side(node, side);
1149         if (code == NR_LINETO)
1150             return true;
1151         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1152         if (&node->p == side) {
1153             other_to_me = &othernode->n;
1154         } else if (&node->n == side) {
1155             other_to_me = &othernode->p;
1156         }
1157         if (!other_to_me)
1158             return false;
1159         bool is_line =
1160              (Geom::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1161               Geom::L2(node->pos - side->pos) < 1e-6);
1162         return is_line;
1165 /**
1166  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1167  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1168  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too.
1169  * If already cusp and set to cusp, retracts handles.
1170 */
1171 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1173     if (type == Inkscape::NodePath::NODE_AUTO) {
1174         if (node->p.other != NULL)
1175             node->code = NR_CURVETO;
1176         if (node->n.other != NULL)
1177             node->n.other->code = NR_CURVETO;
1178     }
1180     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1182 /*
1183   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1185         if (two_handles) {
1186             // do nothing, adjust_handles called via set_node_type will line them up
1187         } else if (one_handle) {
1188             if (opposite_to_handle_is_line) {
1189                 if (lined_up) {
1190                     // already half-smooth; pull opposite handle too making it fully smooth
1191                 } else {
1192                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1193                 }
1194             } else {
1195                 // pull opposite handle in line with the existing one
1196             }
1197         } else if (no_handles) {
1198             if (both_segments_are_lines OR both_segments_are_curves) {
1199                 //pull both handles
1200             } else {
1201                 // pull the handle opposite to line segment, making node half-smooth
1202             }
1203         }
1204 */
1205         bool p_has_handle = (Geom::L2(node->pos  - node->p.pos) > 1e-6);
1206         bool n_has_handle = (Geom::L2(node->pos  - node->n.pos) > 1e-6);
1207         bool p_is_line = sp_node_side_is_line(node, &node->p);
1208         bool n_is_line = sp_node_side_is_line(node, &node->n);
1210         if (p_has_handle && n_has_handle) {
1211             // do nothing, adjust_handles will line them up
1212         } else if (p_has_handle || n_has_handle) {
1213             if (p_has_handle && n_is_line) {
1214                 Radial line (node->n.other->pos - node->pos);
1215                 Radial handle (node->pos - node->p.pos);
1216                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1217                     // already half-smooth; pull opposite handle too making it fully smooth
1218                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1219                 } else {
1220                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1221                 }
1222             } else if (n_has_handle && p_is_line) {
1223                 Radial line (node->p.other->pos - node->pos);
1224                 Radial handle (node->pos - node->n.pos);
1225                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1226                     // already half-smooth; pull opposite handle too making it fully smooth
1227                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1228                 } else {
1229                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1230                 }
1231             } else if (p_has_handle && node->n.other) {
1232                 // pull n handle
1233                 node->n.other->code = NR_CURVETO;
1234                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1235                     Geom::L2(node->p.pos - node->pos) :
1236                     Geom::L2(node->n.other->pos - node->pos) / 3;
1237                 node->n.pos = node->pos - (len / Geom::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1238             } else if (n_has_handle && node->p.other) {
1239                 // pull p handle
1240                 node->code = NR_CURVETO;
1241                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1242                     Geom::L2(node->n.pos - node->pos) :
1243                     Geom::L2(node->p.other->pos - node->pos) / 3;
1244                 node->p.pos = node->pos - (len / Geom::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1245             }
1246         } else if (!p_has_handle && !n_has_handle) {
1247             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1248                 // no handles, but both segments are either lnes or curves:
1249                 //pull both handles
1251                 // convert both to curves:
1252                 node->code = NR_CURVETO;
1253                 node->n.other->code = NR_CURVETO;
1255                 sp_node_adjust_handles_auto(node);
1256             } else {
1257                 // pull the handle opposite to line segment, making it half-smooth
1258                 if (p_is_line && node->n.other) {
1259                     if (type != Inkscape::NodePath::NODE_SYMM) {
1260                         // pull n handle
1261                         node->n.other->code = NR_CURVETO;
1262                         double len =  Geom::L2(node->n.other->pos - node->pos) / 3;
1263                         node->n.pos = node->pos + (len / Geom::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1264                     }
1265                 } else if (n_is_line && node->p.other) {
1266                     if (type != Inkscape::NodePath::NODE_SYMM) {
1267                         // pull p handle
1268                         node->code = NR_CURVETO;
1269                         double len =  Geom::L2(node->p.other->pos - node->pos) / 3;
1270                         node->p.pos = node->pos + (len / Geom::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1271                     }
1272                 }
1273             }
1274         }
1275     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1276         // cusping a cusp: retract nodes
1277         node->p.pos = node->pos;
1278         node->n.pos = node->pos;
1279     }
1281     sp_nodepath_set_node_type (node, type);
1284 /**
1285  * Move node to point, and adjust its and neighbouring handles.
1286  */
1287 void sp_node_moveto(Inkscape::NodePath::Node *node, Geom::Point p)
1289     if (node->type == Inkscape::NodePath::NODE_AUTO) {
1290         node->pos = p;
1291         sp_node_adjust_handles_auto(node);
1292     } else {
1293         Geom::Point delta = p - node->pos;
1294         node->pos = p;
1296         node->p.pos += delta;
1297         node->n.pos += delta;
1298     }
1300     Inkscape::NodePath::Node *node_p = NULL;
1301     Inkscape::NodePath::Node *node_n = NULL;
1303     if (node->p.other) {
1304         if (node->code == NR_LINETO) {
1305             sp_node_adjust_handle(node, 1);
1306             sp_node_adjust_handle(node->p.other, -1);
1307             node_p = node->p.other;
1308         }
1309         if (!node->p.other->selected && node->p.other->type == Inkscape::NodePath::NODE_AUTO) {
1310             sp_node_adjust_handles_auto(node->p.other);
1311             node_p = node->p.other;
1312         }
1313     }
1314     if (node->n.other) {
1315         if (node->n.other->code == NR_LINETO) {
1316             sp_node_adjust_handle(node, -1);
1317             sp_node_adjust_handle(node->n.other, 1);
1318             node_n = node->n.other;
1319         }
1320         if (!node->n.other->selected && node->n.other->type == Inkscape::NodePath::NODE_AUTO) {
1321             sp_node_adjust_handles_auto(node->n.other);
1322             node_n = node->n.other;
1323         }
1324     }
1326     // this function is only called from batch movers that will update display at the end
1327     // themselves, so here we just move all the knots without emitting move signals, for speed
1328     sp_node_update_handles(node, false);
1329     if (node_n) {
1330         sp_node_update_handles(node_n, false);
1331     }
1332     if (node_p) {
1333         sp_node_update_handles(node_p, false);
1334     }
1337 /**
1338  * Call sp_node_moveto() for node selection and handle possible snapping.
1339  */
1340 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, Geom::Coord dx, Geom::Coord dy,
1341                                             bool const snap, bool constrained = false,
1342                                             Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point())
1344     Geom::Point delta(dx, dy);
1345     Geom::Point best_pt = delta;
1346     Inkscape::SnappedPoint best;
1348     if (snap) {
1349         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1350          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we
1351          * must provide that information. */
1353         // Build a list of the unselected nodes to which the snapper should snap
1354         std::vector<std::pair<Geom::Point, int> > unselected_nodes;
1355         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1356             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1357             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1358                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1359                 if (!node->selected) {
1360                     unselected_nodes.push_back(std::make_pair(to_2geom(node->pos), node->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPTARGET_NODE_SMOOTH : Inkscape::SNAPTARGET_NODE_CUSP));
1361                 }
1362             }
1363         }
1365         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1367         // When only the node closest to the mouse pointer is to be snapped
1368         // then we will not even try to snap to other points and discard those immediately
1369         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1370         bool closest_only = prefs->getBool("/options/snapclosestonly/value", false);
1372         Inkscape::NodePath::Node *closest_node = NULL;
1373         Geom::Coord closest_dist = NR_HUGE;
1375         if (closest_only) {
1376                 for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1377                         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1378                         Geom::Coord dist = Geom::L2(nodepath->drag_origin_mouse - n->origin);
1379                         if (dist < closest_dist) {
1380                                 closest_node = n;
1381                                 closest_dist = dist;
1382                         }
1383                 }
1384         }
1386         // Iterate through all selected nodes
1387         m.setup(nodepath->desktop, false, SP_PATH(nodepath->item), &unselected_nodes);
1388         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1389             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1390             if (!closest_only || n == closest_node) { //try to snap either all selected nodes or only the closest one
1391                     Inkscape::SnappedPoint s;
1392                     Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP);
1393                     if (constrained) {
1394                         Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1395                         dedicated_constraint.setPoint(n->pos);
1396                         s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type, dedicated_constraint);
1397                     } else {
1398                         s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type);
1399                     }
1401                     if (s.getSnapped()) {
1402                         s.setPointerDistance(Geom::L2(nodepath->drag_origin_mouse - n->origin));
1403                         if (!s.isOtherSnapBetter(best, true)) {
1404                                 best = s;
1405                                 best_pt = from_2geom(s.getPoint()) - n->pos;
1406                         }
1407                     }
1408             }
1409         }
1411         if (best.getSnapped()) {
1412             nodepath->desktop->snapindicator->set_new_snaptarget(best);
1413         } else {
1414             nodepath->desktop->snapindicator->remove_snaptarget();
1415         }
1416     }
1418     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1419         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1420         sp_node_moveto(n, n->pos + best_pt);
1421     }
1423     // do not update repr here so that node dragging is acceptably fast
1424     update_object(nodepath);
1427 /**
1428 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1429 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1430 near x = 0.
1431  */
1432 double
1433 sculpt_profile (double x, double alpha, guint profile)
1435     double result = 1;
1437     if (x >= 1) {
1438         result = 0;
1439     } else if (x <= 0) {
1440         result = 1;
1441     } else {
1442         switch (profile) {
1443             case SCULPT_PROFILE_LINEAR:
1444                 result = 1 - x;
1445                 break;
1446             case SCULPT_PROFILE_BELL:
1447                 result = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1448                 break;
1449             case SCULPT_PROFILE_ELLIPTIC:
1450                 result = sqrt(1 - x*x);
1451                 break;
1452             default:
1453                 g_assert_not_reached();
1454         }
1455     }
1457     return result;
1460 double
1461 bezier_length (Geom::Point a, Geom::Point ah, Geom::Point bh, Geom::Point b)
1463     // extremely primitive for now, don't have time to look for the real one
1464     double lower = Geom::L2(b - a);
1465     double upper = Geom::L2(ah - a) + Geom::L2(bh - ah) + Geom::L2(bh - b);
1466     return (lower + upper)/2;
1469 void
1470 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, Geom::Point delta, Geom::Point delta_n, Geom::Point delta_p)
1472     n->pos = n->origin + delta;
1473     n->n.pos = n->n.origin + delta_n;
1474     n->p.pos = n->p.origin + delta_p;
1475     sp_node_adjust_handles(n);
1476     sp_node_update_handles(n, false);
1479 /**
1480  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1481  * on how far they are from the dragged node n.
1482  */
1483 static void
1484 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, Geom::Point delta)
1486     g_assert (n);
1487     g_assert (nodepath);
1488     g_assert (n->subpath->nodepath == nodepath);
1490     double pressure = n->knot->pressure;
1491     if (pressure == 0)
1492         pressure = 0.5; // default
1493     pressure = CLAMP (pressure, 0.2, 0.8);
1495     // map pressure to alpha = 1/5 ... 5
1496     double alpha = 1 - 2 * fabs(pressure - 0.5);
1497     if (pressure > 0.5)
1498         alpha = 1/alpha;
1500     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1501     guint profile = prefs->getInt("/tools/nodes/sculpting_profile", SCULPT_PROFILE_BELL);
1503     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1504         // Only one subpath has selected nodes:
1505         // use linear mode, where the distance from n to node being dragged is calculated along the path
1507         double n_sel_range = 0, p_sel_range = 0;
1508         guint n_nodes = 0, p_nodes = 0;
1509         guint n_sel_nodes = 0, p_sel_nodes = 0;
1511         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1512         {
1513             double n_range = 0, p_range = 0;
1514             bool n_going = true, p_going = true;
1515             Inkscape::NodePath::Node *n_node = n;
1516             Inkscape::NodePath::Node *p_node = n;
1517             do {
1518                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1519                 if (n_node && n_going)
1520                     n_node = n_node->n.other;
1521                 if (n_node == NULL) {
1522                     n_going = false;
1523                 } else {
1524                     n_nodes ++;
1525                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1526                     if (n_node->selected) {
1527                         n_sel_nodes ++;
1528                         n_sel_range = n_range;
1529                     }
1530                     if (n_node == p_node) {
1531                         n_going = false;
1532                         p_going = false;
1533                     }
1534                 }
1535                 if (p_node && p_going)
1536                     p_node = p_node->p.other;
1537                 if (p_node == NULL) {
1538                     p_going = false;
1539                 } else {
1540                     p_nodes ++;
1541                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1542                     if (p_node->selected) {
1543                         p_sel_nodes ++;
1544                         p_sel_range = p_range;
1545                     }
1546                     if (p_node == n_node) {
1547                         n_going = false;
1548                         p_going = false;
1549                     }
1550                 }
1551             } while (n_going || p_going);
1552         }
1554         // Second pass: actually move nodes in this subpath
1555         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1556         {
1557             double n_range = 0, p_range = 0;
1558             bool n_going = true, p_going = true;
1559             Inkscape::NodePath::Node *n_node = n;
1560             Inkscape::NodePath::Node *p_node = n;
1561             do {
1562                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1563                 if (n_node && n_going)
1564                     n_node = n_node->n.other;
1565                 if (n_node == NULL) {
1566                     n_going = false;
1567                 } else {
1568                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1569                     if (n_node->selected) {
1570                         sp_nodepath_move_node_and_handles (n_node,
1571                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1572                                                            sculpt_profile ((n_range + Geom::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1573                                                            sculpt_profile ((n_range - Geom::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1574                     }
1575                     if (n_node == p_node) {
1576                         n_going = false;
1577                         p_going = false;
1578                     }
1579                 }
1580                 if (p_node && p_going)
1581                     p_node = p_node->p.other;
1582                 if (p_node == NULL) {
1583                     p_going = false;
1584                 } else {
1585                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1586                     if (p_node->selected) {
1587                         sp_nodepath_move_node_and_handles (p_node,
1588                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1589                                                            sculpt_profile ((p_range - Geom::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1590                                                            sculpt_profile ((p_range + Geom::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1591                     }
1592                     if (p_node == n_node) {
1593                         n_going = false;
1594                         p_going = false;
1595                     }
1596                 }
1597             } while (n_going || p_going);
1598         }
1600     } else {
1601         // Multiple subpaths have selected nodes:
1602         // use spatial mode, where the distance from n to node being dragged is measured directly as Geom::L2.
1603         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1604         // fix the pear-like shape when sculpting e.g. a ring
1606         // First pass: calculate range
1607         gdouble direct_range = 0;
1608         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1609             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1610             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1611                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1612                 if (node->selected) {
1613                     direct_range = MAX(direct_range, Geom::L2(node->origin - n->origin));
1614                 }
1615             }
1616         }
1618         // Second pass: actually move nodes
1619         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1620             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1621             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1622                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1623                 if (node->selected) {
1624                     if (direct_range > 1e-6) {
1625                         sp_nodepath_move_node_and_handles (node,
1626                                                        sculpt_profile (Geom::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1627                                                        sculpt_profile (Geom::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1628                                                        sculpt_profile (Geom::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1629                     } else {
1630                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1631                     }
1633                 }
1634             }
1635         }
1636     }
1638     // do not update repr here so that node dragging is acceptably fast
1639     update_object(nodepath);
1643 /**
1644  * Move node selection to point, adjust its and neighbouring handles,
1645  * handle possible snapping, and commit the change with possible undo.
1646  */
1647 void
1648 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1650     if (!nodepath) return;
1652     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1654     if (dx == 0) {
1655         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1656     } else if (dy == 0) {
1657         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1658     } else {
1659         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1660     }
1663 /**
1664  * Move node selection off screen and commit the change.
1665  */
1666 void
1667 sp_node_selected_move_screen(SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1669     // borrowed from sp_selection_move_screen in selection-chemistry.c
1670     // we find out the current zoom factor and divide deltas by it
1672     gdouble zoom = desktop->current_zoom();
1673     gdouble zdx = dx / zoom;
1674     gdouble zdy = dy / zoom;
1676     if (!nodepath) return;
1678     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1680     if (dx == 0) {
1681         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1682     } else if (dy == 0) {
1683         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1684     } else {
1685         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1686     }
1689 /**
1690  * Move selected nodes to the absolute position given
1691  */
1692 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis)
1694     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1695         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1696         Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]);
1697         sp_node_moveto(n, npos);
1698     }
1700     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1703 /**
1704  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return Geom::Nothing
1705  */
1706 boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1708     boost::optional<Geom::Coord> no_coord;
1709     g_return_val_if_fail(nodepath->selected, no_coord);
1711     // determine coordinate of first selected node
1712     GList *nsel = nodepath->selected;
1713     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1714     Geom::Coord coord = n->pos[axis];
1715     bool coincide = true;
1717     // compare it to the coordinates of all the other selected nodes
1718     for (GList *l = nsel->next; l != NULL; l = l->next) {
1719         n = (Inkscape::NodePath::Node *) l->data;
1720         if (n->pos[axis] != coord) {
1721             coincide = false;
1722         }
1723     }
1724     if (coincide) {
1725         return coord;
1726     } else {
1727         Geom::Rect bbox = sp_node_selected_bbox(nodepath);
1728         // currently we return the coordinate of the bounding box midpoint because I don't know how
1729         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1730         return bbox.midpoint()[axis];
1731     }
1734 /** If they don't yet exist, creates knot and line for the given side of the node */
1735 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1737     if (!side->knot) {
1738         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"));
1740         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1741         side->knot->setSize (7);
1742         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1743         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1744         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1745         sp_knot_update_ctrl(side->knot);
1747         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1748         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1749         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1750         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1751         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1752         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1753     }
1755     if (!side->line) {
1756         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1757                                         SP_TYPE_CTRLLINE, NULL);
1758     }
1761 /**
1762  * Ensure the given handle of the node is visible/invisible, update its screen position
1763  */
1764 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1766     g_assert(node != NULL);
1768    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1769     NRPathcode code = sp_node_path_code_from_side(node, side);
1771     show_handle = show_handle && (code == NR_CURVETO) && (Geom::L2(side->pos - node->pos) > 1e-6);
1773     if (show_handle) {
1774         if (!side->knot) { // No handle knot at all
1775             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1776             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1777             side->knot->pos = side->pos;
1778             if (side->knot->item)
1779                 SP_CTRL(side->knot->item)->moveto(side->pos);
1780             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1781             sp_knot_show(side->knot);
1782         } else {
1783             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1784                 if (fire_move_signals) {
1785                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1786                 } else {
1787                     sp_knot_moveto(side->knot, side->pos);
1788                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1789                 }
1790             }
1791             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1792                 sp_knot_show(side->knot);
1793             }
1794         }
1795         sp_canvas_item_show(side->line);
1796     } else {
1797         if (side->knot) {
1798             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1799                 sp_knot_hide(side->knot);
1800             }
1801         }
1802         if (side->line) {
1803             sp_canvas_item_hide(side->line);
1804         }
1805     }
1808 /**
1809  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1810  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1811  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1812  * updated; otherwise, just move the knots silently (used in batch moves).
1813  */
1814 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1816     g_assert(node != NULL);
1818     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1819         sp_knot_show(node->knot);
1820     }
1822     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1823         if (fire_move_signals)
1824             sp_knot_set_position(node->knot, node->pos, 0);
1825         else
1826             sp_knot_moveto(node->knot, node->pos);
1827     }
1829     gboolean show_handles = node->selected;
1830     if (node->p.other != NULL) {
1831         if (node->p.other->selected) show_handles = TRUE;
1832     }
1833     if (node->n.other != NULL) {
1834         if (node->n.other->selected) show_handles = TRUE;
1835     }
1837     if (node->subpath->nodepath->show_handles == false)
1838         show_handles = FALSE;
1840     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1841     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1844 /**
1845  * Call sp_node_update_handles() for all nodes on subpath.
1846  */
1847 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1849     g_assert(subpath != NULL);
1851     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1852         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1853     }
1856 /**
1857  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1858  */
1859 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1861     g_assert(nodepath != NULL);
1863     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1864         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1865     }
1868 void
1869 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1871     if (nodepath) {
1872         nodepath->show_handles = show;
1873         sp_nodepath_update_handles(nodepath);
1874     }
1877 /**
1878  * Adds all selected nodes in nodepath to list.
1879  */
1880 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1882     StlConv<Node *>::list(l, selected);
1883 /// \todo this adds a copying, rework when the selection becomes a stl list
1886 /**
1887  * Align selected nodes on the specified axis.
1888  */
1889 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1891     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1892         return;
1893     }
1895     if ( !nodepath->selected->next ) { // only one node selected
1896         return;
1897     }
1898    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1899     Geom::Point dest(pNode->pos);
1900     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1901         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1902         if (pNode) {
1903             dest[axis] = pNode->pos[axis];
1904             sp_node_moveto(pNode, dest);
1905         }
1906     }
1908     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1911 /// Helper struct.
1912 struct NodeSort
1914    Inkscape::NodePath::Node *_node;
1915     Geom::Coord _coord;
1916     /// \todo use vectorof pointers instead of calling copy ctor
1917     NodeSort(Inkscape::NodePath::Node *node, Geom::Dim2 axis) :
1918         _node(node), _coord(node->pos[axis])
1919     {}
1921 };
1923 static bool operator<(NodeSort const &a, NodeSort const &b)
1925     return (a._coord < b._coord);
1928 /**
1929  * Distribute selected nodes on the specified axis.
1930  */
1931 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1933     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1934         return;
1935     }
1937     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1938         return;
1939     }
1941    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1942     std::vector<NodeSort> sorted;
1943     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1944         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1945         if (pNode) {
1946             NodeSort n(pNode, axis);
1947             sorted.push_back(n);
1948             //dest[axis] = pNode->pos[axis];
1949             //sp_node_moveto(pNode, dest);
1950         }
1951     }
1952     std::sort(sorted.begin(), sorted.end());
1953     unsigned int len = sorted.size();
1954     //overall bboxes span
1955     float dist = (sorted.back()._coord -
1956                   sorted.front()._coord);
1957     //new distance between each bbox
1958     float step = (dist) / (len - 1);
1959     float pos = sorted.front()._coord;
1960     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1961           it < sorted.end();
1962           it ++ )
1963     {
1964         Geom::Point dest((*it)._node->pos);
1965         dest[axis] = pos;
1966         sp_node_moveto((*it)._node, dest);
1967         pos += step;
1968     }
1970     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1974 /**
1975  * Call sp_nodepath_line_add_node() for all selected segments.
1976  */
1977 void
1978 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1980     if (!nodepath) {
1981         return;
1982     }
1984     GList *nl = NULL;
1986     int n_added = 0;
1988     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1989        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1990         g_assert(t->selected);
1991         if (t->p.other && t->p.other->selected) {
1992             nl = g_list_prepend(nl, t);
1993         }
1994     }
1996     while (nl) {
1997        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1998        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1999        sp_nodepath_node_select(n, TRUE, FALSE);
2000        n_added ++;
2001        nl = g_list_remove(nl, t);
2002     }
2004     /** \todo fixme: adjust ? */
2005     sp_nodepath_update_handles(nodepath);
2007     if (n_added > 1) {
2008         sp_nodepath_update_repr(nodepath, _("Add nodes"));
2009     } else if (n_added > 0) {
2010         sp_nodepath_update_repr(nodepath, _("Add node"));
2011     }
2013     sp_nodepath_update_statusbar(nodepath);
2016 /**
2017  * Select segment nearest to point
2018  */
2019 void
2020 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p, bool toggle)
2022     if (!nodepath) {
2023         return;
2024     }
2026     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2027     Geom::PathVector const &pathv = curve->get_pathvector();
2028     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2029     if (!pvpos) {
2030         g_print ("Possible error?\n");
2031         return;
2032     }
2034     // calculate index for nodepath's representation.
2035     unsigned int segment_index = floor(pvpos->t) + 1;
2036     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2037         segment_index += pathv[i].size() + 1;
2038         if (pathv[i].closed()) {
2039             segment_index += 1;
2040         }
2041     }
2043     curve->unref();
2045     //find segment to segment
2046     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2048     //fixme: this can return NULL, so check before proceeding.
2049     g_return_if_fail(e != NULL);
2051     gboolean force = FALSE;
2052     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
2053         force = TRUE;
2054     }
2055     sp_nodepath_node_select(e, (gboolean) toggle, force);
2056     if (e->p.other)
2057         sp_nodepath_node_select(e->p.other, TRUE, force);
2059     sp_nodepath_update_handles(nodepath);
2061     sp_nodepath_update_statusbar(nodepath);
2064 /**
2065  * Add a node nearest to point
2066  */
2067 void
2068 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p)
2070     if (!nodepath) {
2071         return;
2072     }
2074     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2075     Geom::PathVector const &pathv = curve->get_pathvector();
2076     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2077     if (!pvpos) {
2078         g_print ("Possible error?\n");
2079         return;
2080     }
2082     // calculate index for nodepath's representation.
2083     double int_part;
2084     double t = std::modf(pvpos->t, &int_part);
2085     unsigned int segment_index = (unsigned int)int_part + 1;
2086     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2087         segment_index += pathv[i].size() + 1;
2088         if (pathv[i].closed()) {
2089             segment_index += 1;
2090         }
2091     }
2093     curve->unref();
2095     //find segment to split
2096     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2097     if (!e) {
2098         return;
2099     }
2101     //don't know why but t seems to flip for lines
2102     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2103         t = 1.0 - t;
2104     }
2106     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2107     sp_nodepath_node_select(n, FALSE, TRUE);
2109     /* fixme: adjust ? */
2110     sp_nodepath_update_handles(nodepath);
2112     sp_nodepath_update_repr(nodepath, _("Add node"));
2114     sp_nodepath_update_statusbar(nodepath);
2117 /*
2118  * Adjusts a segment so that t moves by a certain delta for dragging
2119  * converts lines to curves
2120  *
2121  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2122  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2123  */
2124 void
2125 sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, Geom::Point delta)
2127     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node);
2129     //fixme: e and e->p can be NULL, so check for those before proceeding
2130     g_return_if_fail(e != NULL);
2131     g_return_if_fail(&e->p != NULL);
2133     if (e->type == Inkscape::NodePath::NODE_AUTO) {
2134         e->type = Inkscape::NodePath::NODE_SMOOTH;
2135         sp_nodepath_update_node_knot (e);
2136     }
2137     if (e->p.other->type == Inkscape::NodePath::NODE_AUTO) {
2138         e->p.other->type = Inkscape::NodePath::NODE_SMOOTH;
2139         sp_nodepath_update_node_knot (e->p.other);
2140     }
2142     /* feel good is an arbitrary parameter that distributes the delta between handles
2143      * if t of the drag point is less than 1/6 distance form the endpoint only
2144      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2145      */
2146     double feel_good;
2147     if (t <= 1.0 / 6.0)
2148         feel_good = 0;
2149     else if (t <= 0.5)
2150         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2151     else if (t <= 5.0 / 6.0)
2152         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2153     else
2154         feel_good = 1;
2156     //if we're dragging a line convert it to a curve
2157     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2158         sp_nodepath_set_line_type(e, NR_CURVETO);
2159     }
2161     Geom::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2162     Geom::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2163     e->p.other->n.pos += offsetcoord0;
2164     e->p.pos += offsetcoord1;
2166     // adjust handles of adjacent nodes where necessary
2167     sp_node_adjust_handle(e,1);
2168     sp_node_adjust_handle(e->p.other,-1);
2170     sp_nodepath_update_handles(e->subpath->nodepath);
2172     update_object(e->subpath->nodepath);
2174     sp_nodepath_update_statusbar(e->subpath->nodepath);
2178 /**
2179  * Call sp_nodepath_break() for all selected segments.
2180  */
2181 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2183     if (!nodepath) return;
2185     GList *tempin = g_list_copy(nodepath->selected);
2186     GList *temp = NULL;
2187     for (GList *l = tempin; l != NULL; l = l->next) {
2188        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2189        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2190         if (nn == NULL) continue; // no break, no new node
2191         temp = g_list_prepend(temp, nn);
2192     }
2193     g_list_free(tempin);
2195     if (temp) {
2196         sp_nodepath_deselect(nodepath);
2197     }
2198     for (GList *l = temp; l != NULL; l = l->next) {
2199         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2200     }
2202     sp_nodepath_update_handles(nodepath);
2204     sp_nodepath_update_repr(nodepath, _("Break path"));
2207 /**
2208  * Duplicate the selected node(s).
2209  */
2210 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2212     if (!nodepath) {
2213         return;
2214     }
2216     GList *temp = NULL;
2217     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2218        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2219        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2220         if (nn == NULL) continue; // could not duplicate
2221         temp = g_list_prepend(temp, nn);
2222     }
2224     if (temp) {
2225         sp_nodepath_deselect(nodepath);
2226     }
2227     for (GList *l = temp; l != NULL; l = l->next) {
2228         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2229     }
2231     sp_nodepath_update_handles(nodepath);
2233     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2236 /**
2237  *  Internal function to join two nodes by merging them into one.
2238  */
2239 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2241     /* a and b are endpoints */
2243     // if one of the two nodes is mouseovered, fix its position
2244     Geom::Point c;
2245     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2246         c = a->pos;
2247     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2248         c = b->pos;
2249     } else {
2250         // otherwise, move joined node to the midpoint
2251         c = (a->pos + b->pos) / 2;
2252     }
2254     if (a->subpath == b->subpath) {
2255        Inkscape::NodePath::SubPath *sp = a->subpath;
2256         sp_nodepath_subpath_close(sp);
2257         sp_node_moveto (sp->first, c);
2259         sp_nodepath_update_handles(sp->nodepath);
2260         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2261         return;
2262     }
2264     /* a and b are separate subpaths */
2265     Inkscape::NodePath::SubPath *sa = a->subpath;
2266     Inkscape::NodePath::SubPath *sb = b->subpath;
2267     Geom::Point p;
2268     Inkscape::NodePath::Node *n;
2269     NRPathcode code;
2270     if (a == sa->first) {
2271         // we will now reverse sa, so that a is its last node, not first, and drop that node
2272         p = sa->first->n.pos;
2273         code = (NRPathcode)sa->first->n.other->code;
2274         // create new subpath
2275        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2276        // create a first moveto node on it
2277         n = sa->last;
2278         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2279         n = n->p.other;
2280         if (n == sa->first) n = NULL;
2281         while (n) {
2282             // copy the rest of the nodes from sa to t, going backwards
2283             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2284             n = n->p.other;
2285             if (n == sa->first) n = NULL;
2286         }
2287         // replace sa with t
2288         sp_nodepath_subpath_destroy(sa);
2289         sa = t;
2290     } else if (a == sa->last) {
2291         // a is already last, just drop it
2292         p = sa->last->p.pos;
2293         code = (NRPathcode)sa->last->code;
2294         sp_nodepath_node_destroy(sa->last);
2295     } else {
2296         code = NR_END;
2297         g_assert_not_reached();
2298     }
2300     if (b == sb->first) {
2301         // copy all nodes from b to a, forward
2302         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2303         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2304             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2305         }
2306     } else if (b == sb->last) {
2307         // copy all nodes from b to a, backward
2308         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2309         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2310             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2311         }
2312     } else {
2313         g_assert_not_reached();
2314     }
2315     /* and now destroy sb */
2317     sp_nodepath_subpath_destroy(sb);
2319     sp_nodepath_update_handles(sa->nodepath);
2321     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2323     sp_nodepath_update_statusbar(nodepath);
2326 /**
2327  *  Internal function to join two nodes by adding a segment between them.
2328  */
2329 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2331     if (a->subpath == b->subpath) {
2332        Inkscape::NodePath::SubPath *sp = a->subpath;
2334         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2335         sp->closed = TRUE;
2337         sp->first->p.other = sp->last;
2338         sp->last->n.other  = sp->first;
2340         sp_node_handle_mirror_p_to_n(sp->last);
2341         sp_node_handle_mirror_n_to_p(sp->first);
2343         sp->first->code = sp->last->code;
2344         sp->first       = sp->last;
2346         sp_nodepath_update_handles(sp->nodepath);
2348         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2350         return;
2351     }
2353     /* a and b are separate subpaths */
2354     Inkscape::NodePath::SubPath *sa = a->subpath;
2355     Inkscape::NodePath::SubPath *sb = b->subpath;
2357     Inkscape::NodePath::Node *n;
2358     Geom::Point p;
2359     NRPathcode code;
2360     if (a == sa->first) {
2361         code = (NRPathcode) sa->first->n.other->code;
2362        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2363         n = sa->last;
2364         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2365         for (n = n->p.other; n != NULL; n = n->p.other) {
2366             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2367         }
2368         sp_nodepath_subpath_destroy(sa);
2369         sa = t;
2370     } else if (a == sa->last) {
2371         code = (NRPathcode)sa->last->code;
2372     } else {
2373         code = NR_END;
2374         g_assert_not_reached();
2375     }
2377     if (b == sb->first) {
2378         n = sb->first;
2379         sp_node_handle_mirror_p_to_n(sa->last);
2380         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2381         sp_node_handle_mirror_n_to_p(sa->last);
2382         for (n = n->n.other; n != NULL; n = n->n.other) {
2383             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2384         }
2385     } else if (b == sb->last) {
2386         n = sb->last;
2387         sp_node_handle_mirror_p_to_n(sa->last);
2388         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2389         sp_node_handle_mirror_n_to_p(sa->last);
2390         for (n = n->p.other; n != NULL; n = n->p.other) {
2391             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2392         }
2393     } else {
2394         g_assert_not_reached();
2395     }
2396     /* and now destroy sb */
2398     sp_nodepath_subpath_destroy(sb);
2400     sp_nodepath_update_handles(sa->nodepath);
2402     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2405 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2407 /**
2408  * Internal function to handle joining two nodes.
2409  */
2410 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2412     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2414     if (g_list_length(nodepath->selected) != 2) {
2415         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2416         return;
2417     }
2419     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2420     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2422     g_assert(a != b);
2423     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2424         // someone tried to join an orphan node (i.e. a single-node subpath).
2425         // this is not worth an error message, just fail silently.
2426         return;
2427     }
2429     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2430         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2431         return;
2432     }
2434     switch(mode) {
2435         case NODE_JOIN_ENDPOINTS:
2436             do_node_selected_join(nodepath, a, b);
2437             break;
2438         case NODE_JOIN_SEGMENT:
2439             do_node_selected_join_segment(nodepath, a, b);
2440             break;
2441     }
2444 /**
2445  *  Join two nodes by merging them into one.
2446  */
2447 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2449     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2452 /**
2453  *  Join two nodes by adding a segment between them.
2454  */
2455 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2457     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2460 /**
2461  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2462  */
2463 void sp_node_delete_preserve(GList *nodes_to_delete)
2465     GSList *nodepaths = NULL;
2467     while (nodes_to_delete) {
2468         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2469         Inkscape::NodePath::SubPath *sp = node->subpath;
2470         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2471         Inkscape::NodePath::Node *sample_cursor = NULL;
2472         Inkscape::NodePath::Node *sample_end = NULL;
2473         Inkscape::NodePath::Node *delete_cursor = node;
2474         bool just_delete = false;
2476         //find the start of this contiguous selection
2477         //move left to the first node that is not selected
2478         //or the start of the non-closed path
2479         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2480             delete_cursor = curr;
2481         }
2483         //just delete at the beginning of an open path
2484         if (!delete_cursor->p.other) {
2485             sample_cursor = delete_cursor;
2486             just_delete = true;
2487         } else {
2488             sample_cursor = delete_cursor->p.other;
2489         }
2491         //calculate points for each segment
2492         int rate = 5;
2493         float period = 1.0 / rate;
2494         std::vector<Geom::Point> data;
2495         if (!just_delete) {
2496             data.push_back(sample_cursor->pos);
2497             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2498                 //just delete at the end of an open path
2499                 if (!sp->closed && curr == sp->last) {
2500                     just_delete = true;
2501                     break;
2502                 }
2504                 //sample points on the contiguous selected segment
2505                 Geom::Point *bez;
2506                 bez = new Geom::Point [4];
2507                 bez[0] = curr->pos;
2508                 bez[1] = curr->n.pos;
2509                 bez[2] = curr->n.other->p.pos;
2510                 bez[3] = curr->n.other->pos;
2511                 for (int i=1; i<rate; i++) {
2512                     gdouble t = i * period;
2513                     Geom::Point p = bezier_pt(3, bez, t);
2514                     data.push_back(p);
2515                 }
2516                 data.push_back(curr->n.other->pos);
2518                 sample_end = curr->n.other;
2519                 //break if we've come full circle or hit the end of the selection
2520                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2521                     break;
2522                 }
2523             }
2524         }
2526         if (!just_delete) {
2527             //calculate the best fitting single segment and adjust the endpoints
2528             Geom::Point *adata;
2529             adata = new Geom::Point [data.size()];
2530             copy(data.begin(), data.end(), adata);
2532             Geom::Point *bez;
2533             bez = new Geom::Point [4];
2534             //would decreasing error create a better fitting approximation?
2535             gdouble error = 1.0;
2536             gint ret;
2537             ret = Geom::bezier_fit_cubic (bez, adata, data.size(), error);
2539             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2540             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2541             //the resulting nodes behave as expected.
2542             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2543                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2544             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2545                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2547             //adjust endpoints
2548             sample_cursor->n.pos = bez[1];
2549             sample_end->p.pos = bez[2];
2550         }
2552         //destroy this contiguous selection
2553         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2554             Inkscape::NodePath::Node *temp = delete_cursor;
2555             if (delete_cursor->n.other == delete_cursor) {
2556                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2557                 delete_cursor = NULL;
2558             } else {
2559                 delete_cursor = delete_cursor->n.other;
2560             }
2561             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2562             sp_nodepath_node_destroy(temp);
2563         }
2565         sp_nodepath_update_handles(nodepath);
2567         if (!g_slist_find(nodepaths, nodepath))
2568             nodepaths = g_slist_prepend (nodepaths, nodepath);
2569     }
2571     for (GSList *i = nodepaths; i; i = i->next) {
2572         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2573         // different nodepaths will give us one undo event per nodepath
2574         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2576         // if the entire nodepath is removed, delete the selected object.
2577         if (nodepath->subpaths == NULL ||
2578             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2579             //at least 2
2580             sp_nodepath_get_node_count(nodepath) < 2) {
2581             SPDocument *document = sp_desktop_document (nodepath->desktop);
2582             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2583             //delete this nodepath's object, not the entire selection! (though at this time, this
2584             //does not matter)
2585             sp_selection_delete(nodepath->desktop);
2586             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2587                               _("Delete nodes"));
2588         } else {
2589             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2590             sp_nodepath_update_statusbar(nodepath);
2591         }
2592     }
2594     g_slist_free (nodepaths);
2597 /**
2598  * Delete one or more selected nodes.
2599  */
2600 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2602     if (!nodepath) return;
2603     if (!nodepath->selected) return;
2605     /** \todo fixme: do it the right way */
2606     while (nodepath->selected) {
2607        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2608         sp_nodepath_node_destroy(node);
2609     }
2612     //clean up the nodepath (such as for trivial subpaths)
2613     sp_nodepath_cleanup(nodepath);
2615     sp_nodepath_update_handles(nodepath);
2617     // if the entire nodepath is removed, delete the selected object.
2618     if (nodepath->subpaths == NULL ||
2619         sp_nodepath_get_node_count(nodepath) < 2) {
2620         SPDocument *document = sp_desktop_document (nodepath->desktop);
2621         sp_selection_delete(nodepath->desktop);
2622         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2623                           _("Delete nodes"));
2624         return;
2625     }
2627     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2629     sp_nodepath_update_statusbar(nodepath);
2632 /**
2633  * Delete one or more segments between two selected nodes.
2634  * This is the code for 'split'.
2635  */
2636 void
2637 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2639    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2640    Inkscape::NodePath::Node *curr, *next;     //Iterators
2642     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2644     if (g_list_length(nodepath->selected) != 2) {
2645         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2646                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2647         return;
2648     }
2650     //Selected nodes, not inclusive
2651    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2652    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2654     if ( ( a==b)                       ||  //same node
2655          (a->subpath  != b->subpath )  ||  //not the same path
2656          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2657          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2658     {
2659         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2660                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2661         return;
2662     }
2664     //###########################################
2665     //# BEGIN EDITS
2666     //###########################################
2667     //##################################
2668     //# CLOSED PATH
2669     //##################################
2670     if (a->subpath->closed) {
2673         gboolean reversed = FALSE;
2675         //Since we can go in a circle, we need to find the shorter distance.
2676         //  a->b or b->a
2677         start = end = NULL;
2678         int distance    = 0;
2679         int minDistance = 0;
2680         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2681             if (curr==b) {
2682                 //printf("a to b:%d\n", distance);
2683                 start = a;//go from a to b
2684                 end   = b;
2685                 minDistance = distance;
2686                 //printf("A to B :\n");
2687                 break;
2688             }
2689             distance++;
2690         }
2692         //try again, the other direction
2693         distance = 0;
2694         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2695             if (curr==a) {
2696                 //printf("b to a:%d\n", distance);
2697                 if (distance < minDistance) {
2698                     start    = b;  //we go from b to a
2699                     end      = a;
2700                     reversed = TRUE;
2701                     //printf("B to A\n");
2702                 }
2703                 break;
2704             }
2705             distance++;
2706         }
2709         //Copy everything from 'end' to 'start' to a new subpath
2710        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2711         for (curr=end ; curr ; curr=curr->n.other) {
2712             NRPathcode code = (NRPathcode) curr->code;
2713             if (curr == end)
2714                 code = NR_MOVETO;
2715             sp_nodepath_node_new(t, NULL,
2716                                  (Inkscape::NodePath::NodeType)curr->type, code,
2717                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2718             if (curr == start)
2719                 break;
2720         }
2721         sp_nodepath_subpath_destroy(a->subpath);
2724     }
2728     //##################################
2729     //# OPEN PATH
2730     //##################################
2731     else {
2733         //We need to get the direction of the list between A and B
2734         //Can we walk from a to b?
2735         start = end = NULL;
2736         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2737             if (curr==b) {
2738                 start = a;  //did it!  we go from a to b
2739                 end   = b;
2740                 //printf("A to B\n");
2741                 break;
2742             }
2743         }
2744         if (!start) {//didn't work?  let's try the other direction
2745             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2746                 if (curr==a) {
2747                     start = b;  //did it!  we go from b to a
2748                     end   = a;
2749                     //printf("B to A\n");
2750                     break;
2751                 }
2752             }
2753         }
2754         if (!start) {
2755             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2756                                                      _("Cannot find path between nodes."));
2757             return;
2758         }
2762         //Copy everything after 'end' to a new subpath
2763        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2764         for (curr=end ; curr ; curr=curr->n.other) {
2765             NRPathcode code = (NRPathcode) curr->code;
2766             if (curr == end)
2767                 code = NR_MOVETO;
2768             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2769                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2770         }
2772         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2773         for (curr = start->n.other ; curr  ; curr=next) {
2774             next = curr->n.other;
2775             sp_nodepath_node_destroy(curr);
2776         }
2778     }
2779     //###########################################
2780     //# END EDITS
2781     //###########################################
2783     //clean up the nodepath (such as for trivial subpaths)
2784     sp_nodepath_cleanup(nodepath);
2786     sp_nodepath_update_handles(nodepath);
2788     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2790     sp_nodepath_update_statusbar(nodepath);
2793 /**
2794  * Call sp_nodepath_set_line() for all selected segments.
2795  */
2796 void
2797 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2799     if (nodepath == NULL) return;
2801     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2802        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2803         g_assert(n->selected);
2804         if (n->p.other && n->p.other->selected) {
2805             sp_nodepath_set_line_type(n, code);
2806         }
2807     }
2809     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2812 /**
2813  * Call sp_nodepath_convert_node_type() for all selected nodes.
2814  */
2815 void
2816 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2818     if (nodepath == NULL) return;
2820     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2822     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2823         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2824     }
2826     sp_nodepath_update_repr(nodepath, _("Change node type"));
2829 /**
2830  * Change select status of node, update its own and neighbour handles.
2831  */
2832 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2834     node->selected = selected;
2836     if (selected) {
2837         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 11 : 9);
2838         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2839         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2840         sp_knot_update_ctrl(node->knot);
2841     } else {
2842         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 9 : 7);
2843         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2844         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2845         sp_knot_update_ctrl(node->knot);
2846     }
2848     sp_node_update_handles(node);
2849     if (node->n.other) sp_node_update_handles(node->n.other);
2850     if (node->p.other) sp_node_update_handles(node->p.other);
2853 /**
2854 \brief Select a node
2855 \param node     The node to select
2856 \param incremental   If true, add to selection, otherwise deselect others
2857 \param override   If true, always select this node, otherwise toggle selected status
2858 */
2859 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2861     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2863     if (incremental) {
2864         if (override) {
2865             if (!g_list_find(nodepath->selected, node)) {
2866                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2867             }
2868             sp_node_set_selected(node, TRUE);
2869         } else { // toggle
2870             if (node->selected) {
2871                 g_assert(g_list_find(nodepath->selected, node));
2872                 nodepath->selected = g_list_remove(nodepath->selected, node);
2873             } else {
2874                 g_assert(!g_list_find(nodepath->selected, node));
2875                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2876             }
2877             sp_node_set_selected(node, !node->selected);
2878         }
2879     } else {
2880         sp_nodepath_deselect(nodepath);
2881         nodepath->selected = g_list_prepend(nodepath->selected, node);
2882         sp_node_set_selected(node, TRUE);
2883     }
2885     sp_nodepath_update_statusbar(nodepath);
2889 /**
2890 \brief Deselect all nodes in the nodepath
2891 */
2892 void
2893 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2895     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2897     while (nodepath->selected) {
2898         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2899         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2900     }
2901     sp_nodepath_update_statusbar(nodepath);
2904 /**
2905 \brief Select or invert selection of all nodes in the nodepath
2906 */
2907 void
2908 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2910     if (!nodepath) return;
2912     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2913        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2914         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2915            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2916            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2917         }
2918     }
2921 /**
2922  * If nothing selected, does the same as sp_nodepath_select_all();
2923  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2924  * (i.e., similar to "select all in layer", with the "selected" subpaths
2925  * being treated as "layers" in the path).
2926  */
2927 void
2928 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2930     if (!nodepath) return;
2932     if (g_list_length (nodepath->selected) == 0) {
2933         sp_nodepath_select_all (nodepath, invert);
2934         return;
2935     }
2937     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2938     GSList *subpaths = NULL;
2940     for (GList *l = copy; l != NULL; l = l->next) {
2941         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2942         Inkscape::NodePath::SubPath *subpath = n->subpath;
2943         if (!g_slist_find (subpaths, subpath))
2944             subpaths = g_slist_prepend (subpaths, subpath);
2945     }
2947     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2948         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2949         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2950             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2951             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2952         }
2953     }
2955     g_slist_free (subpaths);
2956     g_list_free (copy);
2959 /**
2960  * \brief Select the node after the last selected; if none is selected,
2961  * select the first within path.
2962  */
2963 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2965     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2967    Inkscape::NodePath::Node *last = NULL;
2968     if (nodepath->selected) {
2969         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2970            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2971             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2972             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2973                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2974                 if (node->selected) {
2975                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2976                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2977                             if (spl->next) { // there's a next subpath
2978                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2979                                 last = subpath_next->first;
2980                             } else if (spl->prev) { // there's a previous subpath
2981                                 last = NULL; // to be set later to the first node of first subpath
2982                             } else {
2983                                 last = node->n.other;
2984                             }
2985                         } else {
2986                             last = node->n.other;
2987                         }
2988                     } else {
2989                         if (node->n.other) {
2990                             last = node->n.other;
2991                         } else {
2992                             if (spl->next) { // there's a next subpath
2993                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2994                                 last = subpath_next->first;
2995                             } else if (spl->prev) { // there's a previous subpath
2996                                 last = NULL; // to be set later to the first node of first subpath
2997                             } else {
2998                                 last = (Inkscape::NodePath::Node *) subpath->first;
2999                             }
3000                         }
3001                     }
3002                 }
3003             }
3004         }
3005         sp_nodepath_deselect(nodepath);
3006     }
3008     if (last) { // there's at least one more node after selected
3009         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3010     } else { // no more nodes, select the first one in first subpath
3011        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
3012         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
3013     }
3016 /**
3017  * \brief Select the node before the first selected; if none is selected,
3018  * select the last within path
3019  */
3020 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
3022     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
3024    Inkscape::NodePath::Node *last = NULL;
3025     if (nodepath->selected) {
3026         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
3027            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3028             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
3029                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3030                 if (node->selected) {
3031                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
3032                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
3033                             if (spl->prev) { // there's a prev subpath
3034                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3035                                 last = subpath_prev->last;
3036                             } else if (spl->next) { // there's a next subpath
3037                                 last = NULL; // to be set later to the last node of last subpath
3038                             } else {
3039                                 last = node->p.other;
3040                             }
3041                         } else {
3042                             last = node->p.other;
3043                         }
3044                     } else {
3045                         if (node->p.other) {
3046                             last = node->p.other;
3047                         } else {
3048                             if (spl->prev) { // there's a prev subpath
3049                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3050                                 last = subpath_prev->last;
3051                             } else if (spl->next) { // there's a next subpath
3052                                 last = NULL; // to be set later to the last node of last subpath
3053                             } else {
3054                                 last = (Inkscape::NodePath::Node *) subpath->last;
3055                             }
3056                         }
3057                     }
3058                 }
3059             }
3060         }
3061         sp_nodepath_deselect(nodepath);
3062     }
3064     if (last) { // there's at least one more node before selected
3065         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3066     } else { // no more nodes, select the last one in last subpath
3067         GList *spl = g_list_last(nodepath->subpaths);
3068        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3069         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
3070     }
3073 /**
3074  * \brief Select all nodes that are within the rectangle.
3075  */
3076 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, Geom::Rect const &b, gboolean incremental)
3078     if (!incremental) {
3079         sp_nodepath_deselect(nodepath);
3080     }
3082     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3083        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3084         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3085            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3087             if (b.contains(node->pos)) {
3088                 sp_nodepath_node_select(node, TRUE, TRUE);
3089             }
3090         }
3091     }
3095 void
3096 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3098     g_assert (n);
3099     g_assert (nodepath);
3100     g_assert (n->subpath->nodepath == nodepath);
3102     if (g_list_length (nodepath->selected) == 0) {
3103         if (grow > 0) {
3104             sp_nodepath_node_select(n, TRUE, TRUE);
3105         }
3106         return;
3107     }
3109     if (g_list_length (nodepath->selected) == 1) {
3110         if (grow < 0) {
3111             sp_nodepath_deselect (nodepath);
3112             return;
3113         }
3114     }
3116         double n_sel_range = 0, p_sel_range = 0;
3117             Inkscape::NodePath::Node *farthest_n_node = n;
3118             Inkscape::NodePath::Node *farthest_p_node = n;
3120         // Calculate ranges
3121         {
3122             double n_range = 0, p_range = 0;
3123             bool n_going = true, p_going = true;
3124             Inkscape::NodePath::Node *n_node = n;
3125             Inkscape::NodePath::Node *p_node = n;
3126             do {
3127                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3128                 if (n_node && n_going)
3129                     n_node = n_node->n.other;
3130                 if (n_node == NULL) {
3131                     n_going = false;
3132                 } else {
3133                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3134                     if (n_node->selected) {
3135                         n_sel_range = n_range;
3136                         farthest_n_node = n_node;
3137                     }
3138                     if (n_node == p_node) {
3139                         n_going = false;
3140                         p_going = false;
3141                     }
3142                 }
3143                 if (p_node && p_going)
3144                     p_node = p_node->p.other;
3145                 if (p_node == NULL) {
3146                     p_going = false;
3147                 } else {
3148                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3149                     if (p_node->selected) {
3150                         p_sel_range = p_range;
3151                         farthest_p_node = p_node;
3152                     }
3153                     if (p_node == n_node) {
3154                         n_going = false;
3155                         p_going = false;
3156                     }
3157                 }
3158             } while (n_going || p_going);
3159         }
3161     if (grow > 0) {
3162         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3163                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3164         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3165                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3166         }
3167     } else {
3168         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3169                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3170         } else if (farthest_p_node && farthest_p_node->selected) {
3171                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3172         }
3173     }
3176 void
3177 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3179     g_assert (n);
3180     g_assert (nodepath);
3181     g_assert (n->subpath->nodepath == nodepath);
3183     if (g_list_length (nodepath->selected) == 0) {
3184         if (grow > 0) {
3185             sp_nodepath_node_select(n, TRUE, TRUE);
3186         }
3187         return;
3188     }
3190     if (g_list_length (nodepath->selected) == 1) {
3191         if (grow < 0) {
3192             sp_nodepath_deselect (nodepath);
3193             return;
3194         }
3195     }
3197     Inkscape::NodePath::Node *farthest_selected = NULL;
3198     double farthest_dist = 0;
3200     Inkscape::NodePath::Node *closest_unselected = NULL;
3201     double closest_dist = NR_HUGE;
3203     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3204        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3205         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3206            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3207            if (node == n)
3208                continue;
3209            if (node->selected) {
3210                if (Geom::L2(node->pos - n->pos) > farthest_dist) {
3211                    farthest_dist = Geom::L2(node->pos - n->pos);
3212                    farthest_selected = node;
3213                }
3214            } else {
3215                if (Geom::L2(node->pos - n->pos) < closest_dist) {
3216                    closest_dist = Geom::L2(node->pos - n->pos);
3217                    closest_unselected = node;
3218                }
3219            }
3220         }
3221     }
3223     if (grow > 0) {
3224         if (closest_unselected) {
3225             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3226         }
3227     } else {
3228         if (farthest_selected) {
3229             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3230         }
3231     }
3235 /**
3236 \brief  Saves all nodes' and handles' current positions in their origin members
3237 */
3238 void
3239 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3241     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3242        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3243         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3244            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3245            n->origin = n->pos;
3246            n->p.origin = n->p.pos;
3247            n->n.origin = n->n.pos;
3248         }
3249     }
3252 /**
3253 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3254 */
3255 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3257     GList *r = NULL;
3258     if (nodepath->selected) {
3259         guint i = 0;
3260         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3261             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3262             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3263                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3264                 i++;
3265                 if (node->selected) {
3266                     r = g_list_append(r, GINT_TO_POINTER(i));
3267                 }
3268             }
3269         }
3270     }
3271     return r;
3274 /**
3275 \brief  Restores selection by selecting nodes whose positions are in the list
3276 */
3277 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3279     sp_nodepath_deselect(nodepath);
3281     guint i = 0;
3282     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3283        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3284         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3285            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3286             i++;
3287             if (g_list_find(r, GINT_TO_POINTER(i))) {
3288                 sp_nodepath_node_select(node, TRUE, TRUE);
3289             }
3290         }
3291     }
3295 /**
3296 \brief Adjusts handle according to node type and line code.
3297 */
3298 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3300     g_assert(node);
3302     // nothing to do for auto nodes (sp_node_adjust_handles() does the job)
3303     if (node->type == Inkscape::NodePath::NODE_AUTO)
3304         return;
3306    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3307    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3309    // nothing to do if we are an end node
3310     if (me->other == NULL) return;
3311     if (other->other == NULL) return;
3313     // nothing to do if we are a cusp node
3314     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3316     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3317     NRPathcode mecode;
3318     if (which_adjust == 1) {
3319         mecode = (NRPathcode)me->other->code;
3320     } else {
3321         mecode = (NRPathcode)node->code;
3322     }
3323     if (mecode == NR_LINETO) return;
3325     if (sp_node_side_is_line(node, other)) {
3326         // other is a line, and we are either smooth or symm
3327        Inkscape::NodePath::Node *othernode = other->other;
3328         double len = Geom::L2(me->pos - node->pos);
3329         Geom::Point delta = node->pos - othernode->pos;
3330         double linelen = Geom::L2(delta);
3331         if (linelen < 1e-18)
3332             return;
3333         me->pos = node->pos + (len / linelen)*delta;
3334         return;
3335     }
3337     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3338         // symmetrize
3339         me->pos = 2 * node->pos - other->pos;
3340         return;
3341     } else {
3342         // smoothify
3343         double len = Geom::L2(me->pos - node->pos);
3344         Geom::Point delta = other->pos - node->pos;
3345         double otherlen = Geom::L2(delta);
3346         if (otherlen < 1e-18) return;
3347         me->pos = node->pos - (len / otherlen) * delta;
3348     }
3351 /**
3352  \brief Adjusts both handles according to node type and line code
3353  */
3354 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3356     g_assert(node);
3358     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3360     /* we are either smooth or symm */
3362     if (node->p.other == NULL) return;
3363     if (node->n.other == NULL) return;
3365     if (node->type == Inkscape::NodePath::NODE_AUTO) {
3366         sp_node_adjust_handles_auto(node);
3367         return;
3368     }
3370     if (sp_node_side_is_line(node, &node->p)) {
3371         sp_node_adjust_handle(node, 1);
3372         return;
3373     }
3375     if (sp_node_side_is_line(node, &node->n)) {
3376         sp_node_adjust_handle(node, -1);
3377         return;
3378     }
3380     /* both are curves */
3381     Geom::Point const delta( node->n.pos - node->p.pos );
3383     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3384         node->p.pos = node->pos - delta / 2;
3385         node->n.pos = node->pos + delta / 2;
3386         return;
3387     }
3389     /* We are smooth */
3390     double plen = Geom::L2(node->p.pos - node->pos);
3391     if (plen < 1e-18) return;
3392     double nlen = Geom::L2(node->n.pos - node->pos);
3393     if (nlen < 1e-18) return;
3394     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3395     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3398 static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node)
3400     if (node->p.other == NULL || node->n.other == NULL) {
3401         node->p.pos = node->pos;
3402         node->n.pos = node->pos;
3403         return;
3404     }
3406     Geom::Point leg_prev = to_2geom(node->p.other->pos - node->pos);
3407     Geom::Point leg_next = to_2geom(node->n.other->pos - node->pos);
3409     double norm_leg_prev = Geom::L2(leg_prev);
3410     double norm_leg_next = Geom::L2(leg_next);
3412     Geom::Point delta;
3413     if (norm_leg_next > 0.0) {
3414         delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
3415         delta.normalize();
3416     }
3418     node->p.pos = node->pos - norm_leg_prev / 3 * delta;
3419     node->n.pos = node->pos + norm_leg_next / 3 * delta;
3422 /**
3423  * Node event callback.
3424  */
3425 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3427     gboolean ret = FALSE;
3428     switch (event->type) {
3429         case GDK_ENTER_NOTIFY:
3430             Inkscape::NodePath::Path::active_node = n;
3431             break;
3432         case GDK_LEAVE_NOTIFY:
3433             Inkscape::NodePath::Path::active_node = NULL;
3434             break;
3435         case GDK_SCROLL:
3436             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3437                 switch (event->scroll.direction) {
3438                     case GDK_SCROLL_UP:
3439                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3440                         break;
3441                     case GDK_SCROLL_DOWN:
3442                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3443                         break;
3444                     default:
3445                         break;
3446                 }
3447                 ret = TRUE;
3448             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3449                 switch (event->scroll.direction) {
3450                     case GDK_SCROLL_UP:
3451                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3452                         break;
3453                     case GDK_SCROLL_DOWN:
3454                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3455                         break;
3456                     default:
3457                         break;
3458                 }
3459                 ret = TRUE;
3460             }
3461             break;
3462         case GDK_KEY_PRESS:
3463             switch (get_group0_keyval (&event->key)) {
3464                 case GDK_space:
3465                     if (event->key.state & GDK_BUTTON1_MASK) {
3466                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3467                         stamp_repr(nodepath);
3468                         ret = TRUE;
3469                     }
3470                     break;
3471                 case GDK_Page_Up:
3472                     if (event->key.state & GDK_CONTROL_MASK) {
3473                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3474                     } else {
3475                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3476                     }
3477                     break;
3478                 case GDK_Page_Down:
3479                     if (event->key.state & GDK_CONTROL_MASK) {
3480                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3481                     } else {
3482                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3483                     }
3484                     break;
3485                 default:
3486                     break;
3487             }
3488             break;
3489         default:
3490             break;
3491     }
3493     return ret;
3496 /**
3497  * Handle keypress on node; directly called.
3498  */
3499 gboolean node_key(GdkEvent *event)
3501     Inkscape::NodePath::Path *np;
3503     // there is no way to verify nodes so set active_node to nil when deleting!!
3504     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3506     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3507         gint ret = FALSE;
3508         switch (get_group0_keyval (&event->key)) {
3509             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3510             case GDK_BackSpace:
3511                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3512                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3513                 sp_nodepath_update_repr(np, _("Delete node"));
3514                 Inkscape::NodePath::Path::active_node = NULL;
3515                 ret = TRUE;
3516                 break;
3517             case GDK_c:
3518                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3519                 ret = TRUE;
3520                 break;
3521             case GDK_s:
3522                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3523                 ret = TRUE;
3524                 break;
3525             case GDK_a:
3526                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_AUTO);
3527                 ret = TRUE;
3528                 break;
3529             case GDK_y:
3530                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3531                 ret = TRUE;
3532                 break;
3533             case GDK_b:
3534                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3535                 ret = TRUE;
3536                 break;
3537         }
3538         return ret;
3539     }
3540     return FALSE;
3543 /**
3544  * Mouseclick on node callback.
3545  */
3546 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3548    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3550     if (state & GDK_CONTROL_MASK) {
3551         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3553         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3554             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3555                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3556             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3557                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3558             } else if (n->type == Inkscape::NodePath::NODE_SYMM) {
3559                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_AUTO);
3560             } else {
3561                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3562             }
3563             sp_nodepath_update_repr(nodepath, _("Change node type"));
3564             sp_nodepath_update_statusbar(nodepath);
3566         } else { //ctrl+alt+click: delete node
3567             GList *node_to_delete = NULL;
3568             node_to_delete = g_list_append(node_to_delete, n);
3569             sp_node_delete_preserve(node_to_delete);
3570         }
3572     } else {
3573         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3574     }
3577 /**
3578  * Mouse grabbed node callback.
3579  */
3580 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3582    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3584     if (!n->selected) {
3585         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3586     }
3588     n->is_dragging = true;
3589     sp_canvas_set_snap_delay_active(n->subpath->nodepath->desktop->canvas, true);
3590     // Reconstruct and store the location of the mouse pointer at the time when we started dragging (needed for snapping)
3591     n->subpath->nodepath->drag_origin_mouse = knot->grabbed_rel_pos + knot->drag_origin;
3593     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3595     sp_nodepath_remember_origins (n->subpath->nodepath);
3598 /**
3599  * Mouse ungrabbed node callback.
3600  */
3601 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3603    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3605    n->dragging_out = NULL;
3606    n->is_dragging = false;
3607    sp_canvas_set_snap_delay_active(n->subpath->nodepath->desktop->canvas, false);
3608    n->subpath->nodepath->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
3609    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3611    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3614 /**
3615  * The point on a line, given by its angle, closest to the given point.
3616  * \param p  A point.
3617  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3618  * \param closest  Pointer to the point struct where the result is stored.
3619  * \todo FIXME: use dot product perhaps?
3620  */
3621 static void point_line_closest(Geom::Point *p, double a, Geom::Point *closest)
3623     if (a == HUGE_VAL) { // vertical
3624         *closest = Geom::Point(0, (*p)[Geom::Y]);
3625     } else {
3626         (*closest)[Geom::X] = ( a * (*p)[Geom::Y] + (*p)[Geom::X]) / (a*a + 1);
3627         (*closest)[Geom::Y] = a * (*closest)[Geom::X];
3628     }
3631 /**
3632  * Distance from the point to a line given by its angle.
3633  * \param p  A point.
3634  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3635  */
3636 static double point_line_distance(Geom::Point *p, double a)
3638     Geom::Point c;
3639     point_line_closest(p, a, &c);
3640     return sqrt(((*p)[Geom::X] - c[Geom::X])*((*p)[Geom::X] - c[Geom::X]) + ((*p)[Geom::Y] - c[Geom::Y])*((*p)[Geom::Y] - c[Geom::Y]));
3643 /**
3644  * Callback for node "request" signal.
3645  * \todo fixme: This goes to "moved" event? (lauris)
3646  */
3647 static gboolean
3648 node_request(SPKnot */*knot*/, Geom::Point const &p, guint state, gpointer data)
3650     double yn, xn, yp, xp;
3651     double an, ap, na, pa;
3652     double d_an, d_ap, d_na, d_pa;
3653     gboolean collinear = FALSE;
3654     Geom::Point c;
3655     Geom::Point pr;
3657     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3659     n->subpath->nodepath->desktop->snapindicator->remove_snaptarget();
3661     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3662     if ( (!n->subpath->nodepath->straight_path) &&
3663          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3664            || n->dragging_out ) )
3665     {
3666        Geom::Point mouse = p;
3668        if (!n->dragging_out) {
3669            // This is the first drag-out event; find out which handle to drag out
3670            double appr_n = (n->n.other ? Geom::L2(n->n.other->pos - n->pos) - Geom::L2(n->n.other->pos - p) : -HUGE_VAL);
3671            double appr_p = (n->p.other ? Geom::L2(n->p.other->pos - n->pos) - Geom::L2(n->p.other->pos - p) : -HUGE_VAL);
3673            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3674                return FALSE;
3676            Inkscape::NodePath::NodeSide *opposite;
3677            if (appr_p > appr_n) { // closer to p
3678                n->dragging_out = &n->p;
3679                opposite = &n->n;
3680                n->code = NR_CURVETO;
3681            } else if (appr_p < appr_n) { // closer to n
3682                n->dragging_out = &n->n;
3683                opposite = &n->p;
3684                n->n.other->code = NR_CURVETO;
3685            } else { // p and n nodes are the same
3686                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3687                    n->dragging_out = &n->p;
3688                    opposite = &n->n;
3689                    n->code = NR_CURVETO;
3690                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3691                    n->dragging_out = &n->n;
3692                    opposite = &n->p;
3693                    n->n.other->code = NR_CURVETO;
3694                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3695                    double appr_other_n = (n->n.other ? Geom::L2(n->n.other->n.pos - n->pos) - Geom::L2(n->n.other->n.pos - p) : -HUGE_VAL);
3696                    double appr_other_p = (n->n.other ? Geom::L2(n->n.other->p.pos - n->pos) - Geom::L2(n->n.other->p.pos - p) : -HUGE_VAL);
3697                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3698                        n->dragging_out = &n->n;
3699                        opposite = &n->p;
3700                        n->n.other->code = NR_CURVETO;
3701                    } else { // closer to other's n handle
3702                        n->dragging_out = &n->p;
3703                        opposite = &n->n;
3704                        n->code = NR_CURVETO;
3705                    }
3706                }
3707            }
3709            // if there's another handle, make sure the one we drag out starts parallel to it
3710            if (opposite->pos != n->pos) {
3711                mouse = n->pos - Geom::L2(mouse - n->pos) * Geom::unit_vector(opposite->pos - n->pos);
3712            }
3714            // knots might not be created yet!
3715            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3716            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3717        }
3719        // pass this on to the handle-moved callback
3720        node_handle_moved(n->dragging_out->knot, mouse, state, (gpointer) n);
3721        sp_node_update_handles(n);
3722        return TRUE;
3723    }
3725     if (state & GDK_CONTROL_MASK) { // constrained motion
3727         // calculate relative distances of handles
3728         // n handle:
3729         yn = n->n.pos[Geom::Y] - n->pos[Geom::Y];
3730         xn = n->n.pos[Geom::X] - n->pos[Geom::X];
3731         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3732         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3733             if (n->n.other) { // if there is the next point
3734                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3735                     yn = n->n.other->origin[Geom::Y] - n->origin[Geom::Y]; // use origin because otherwise the direction will change as you drag
3736                     xn = n->n.other->origin[Geom::X] - n->origin[Geom::X];
3737             }
3738         }
3739         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3740         if (yn < 0) { xn = -xn; yn = -yn; }
3742         // p handle:
3743         yp = n->p.pos[Geom::Y] - n->pos[Geom::Y];
3744         xp = n->p.pos[Geom::X] - n->pos[Geom::X];
3745         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3746         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3747             if (n->p.other) {
3748                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3749                     yp = n->p.other->origin[Geom::Y] - n->origin[Geom::Y];
3750                     xp = n->p.other->origin[Geom::X] - n->origin[Geom::X];
3751             }
3752         }
3753         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3754         if (yp < 0) { xp = -xp; yp = -yp; }
3756         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3757             // sliding on handles, only if at least one of the handles is non-vertical
3758             // (otherwise it's the same as ctrl+drag anyway)
3760             // calculate angles of the handles
3761             if (xn == 0) {
3762                 if (yn == 0) { // no handle, consider it the continuation of the other one
3763                     an = 0;
3764                     collinear = TRUE;
3765                 }
3766                 else an = 0; // vertical; set the angle to horizontal
3767             } else an = yn/xn;
3769             if (xp == 0) {
3770                 if (yp == 0) { // no handle, consider it the continuation of the other one
3771                     ap = an;
3772                 }
3773                 else ap = 0; // vertical; set the angle to horizontal
3774             } else  ap = yp/xp;
3776             if (collinear) an = ap;
3778             // angles of the perpendiculars; HUGE_VAL means vertical
3779             if (an == 0) na = HUGE_VAL; else na = -1/an;
3780             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3782             // mouse point relative to the node's original pos
3783             pr = p - n->origin;
3785             // distances to the four lines (two handles and two perpendiculars)
3786             d_an = point_line_distance(&pr, an);
3787             d_na = point_line_distance(&pr, na);
3788             d_ap = point_line_distance(&pr, ap);
3789             d_pa = point_line_distance(&pr, pa);
3791             // find out which line is the closest, save its closest point in c
3792             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3793                 point_line_closest(&pr, an, &c);
3794             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3795                 point_line_closest(&pr, ap, &c);
3796             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3797                 point_line_closest(&pr, na, &c);
3798             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3799                 point_line_closest(&pr, pa, &c);
3800             }
3802             // move the node to the closest point
3803             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3804                                             n->origin[Geom::X] + c[Geom::X] - n->pos[Geom::X],
3805                                             n->origin[Geom::Y] + c[Geom::Y] - n->pos[Geom::Y],
3806                                             true);
3808         } else {  // constraining to hor/vert
3810             if (fabs(p[Geom::X] - n->origin[Geom::X]) > fabs(p[Geom::Y] - n->origin[Geom::Y])) { // snap to hor
3811                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3812                                                 p[Geom::X] - n->pos[Geom::X],
3813                                                 n->origin[Geom::Y] - n->pos[Geom::Y],
3814                                                 true,
3815                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::X]));
3816             } else { // snap to vert
3817                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3818                                                 n->origin[Geom::X] - n->pos[Geom::X],
3819                                                 p[Geom::Y] - n->pos[Geom::Y],
3820                                                 true,
3821                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::Y]));
3822             }
3823         }
3824     } else { // move freely
3825         if (n->is_dragging) {
3826             if (state & GDK_MOD1_MASK) { // sculpt
3827                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, p - n->origin);
3828             } else {
3829                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3830                                             p[Geom::X] - n->pos[Geom::X],
3831                                             p[Geom::Y] - n->pos[Geom::Y],
3832                                             (state & GDK_SHIFT_MASK) == 0);
3833             }
3834         }
3835     }
3837     n->subpath->nodepath->desktop->scroll_to_point(p);
3839     return TRUE;
3842 /**
3843  * Node handle clicked callback.
3844  */
3845 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3847    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3849     if (state & GDK_CONTROL_MASK) { // "delete" handle
3850         if (n->p.knot == knot) {
3851             n->p.pos = n->pos;
3852         } else if (n->n.knot == knot) {
3853             n->n.pos = n->pos;
3854         }
3855         sp_node_update_handles(n);
3856         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3857         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3858         sp_nodepath_update_statusbar(nodepath);
3860     } else { // just select or add to selection, depending in Shift
3861         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3862     }
3865 /**
3866  * Node handle grabbed callback.
3867  */
3868 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3870    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3872     // convert auto -> smooth when dragging handle
3873    if (n->type == Inkscape::NodePath::NODE_AUTO) {
3874         n->type = Inkscape::NodePath::NODE_SMOOTH;
3875         sp_nodepath_update_node_knot (n);
3876    }
3878     if (!n->selected) {
3879         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3880     }
3882     // remember the origin point of the handle
3883     if (n->p.knot == knot) {
3884         n->p.origin_radial = n->p.pos - n->pos;
3885     } else if (n->n.knot == knot) {
3886         n->n.origin_radial = n->n.pos - n->pos;
3887     } else {
3888         g_assert_not_reached();
3889     }
3891     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3894 /**
3895  * Node handle ungrabbed callback.
3896  */
3897 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3899    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3901     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3902     if (n->p.knot == knot) {
3903         n->p.origin_radial.a = 0;
3904         sp_knot_set_position(knot, n->p.pos, state);
3905     } else if (n->n.knot == knot) {
3906         n->n.origin_radial.a = 0;
3907         sp_knot_set_position(knot, n->n.pos, state);
3908     } else {
3909         g_assert_not_reached();
3910     }
3912     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3915 /**
3916  * Node handle "request" signal callback.
3917  */
3918 static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, gpointer data)
3920     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3922     Inkscape::NodePath::NodeSide *me, *opposite;
3923     gint which;
3924     if (n->p.knot == knot) {
3925         me = &n->p;
3926         opposite = &n->n;
3927         which = -1;
3928     } else if (n->n.knot == knot) {
3929         me = &n->n;
3930         opposite = &n->p;
3931         which = 1;
3932     } else {
3933         me = opposite = NULL;
3934         which = 0;
3935         g_assert_not_reached();
3936     }
3938     SPDesktop *desktop = n->subpath->nodepath->desktop;
3939     SnapManager &m = desktop->namedview->snap_manager;
3940     m.setup(desktop, true, n->subpath->nodepath->item);
3941     Inkscape::SnappedPoint s;
3943     if ((state & GDK_SHIFT_MASK) != 0) {
3944         // We will not try to snap when the shift-key is pressed
3945         // so remove the old snap indicator and don't wait for it to time-out
3946         desktop->snapindicator->remove_snaptarget();
3947     }
3949     Inkscape::NodePath::Node *othernode = opposite->other;
3950     Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP);
3951     if (othernode) {
3952         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3953             /* We are smooth node adjacent with line */
3954             Geom::Point const delta = p - n->pos;
3955             Geom::Coord const len = Geom::L2(delta);
3956             Inkscape::NodePath::Node *othernode = opposite->other;
3957             Geom::Point const ndelta = n->pos - othernode->pos;
3958             Geom::Coord const linelen = Geom::L2(ndelta);
3959             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3960                 Geom::Coord const scal = dot(delta, ndelta) / linelen;
3961                 p = n->pos + (scal / linelen) * ndelta;
3962             }
3963             if ((state & GDK_SHIFT_MASK) == 0) {
3964                 s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type, Inkscape::Snapper::ConstraintLine(p, ndelta));
3965             }
3966         } else {
3967             if ((state & GDK_SHIFT_MASK) == 0) {
3968                 s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type);
3969             }
3970         }
3971     } else {
3972         if ((state & GDK_SHIFT_MASK) == 0) {
3973             s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type);
3974         }
3975     }
3977     s.getPoint(p);
3979     sp_node_adjust_handle(n, -which);
3981     return FALSE;
3984 /**
3985  * Node handle moved callback.
3986  */
3987 static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data)
3989    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3990    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3992    Inkscape::NodePath::NodeSide *me;
3993    Inkscape::NodePath::NodeSide *other;
3994     if (n->p.knot == knot) {
3995         me = &n->p;
3996         other = &n->n;
3997     } else if (n->n.knot == knot) {
3998         me = &n->n;
3999         other = &n->p;
4000     } else {
4001         me = NULL;
4002         other = NULL;
4003         g_assert_not_reached();
4004     }
4006     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
4007     Radial rme(me->pos - n->pos);
4008     Radial rother(other->pos - n->pos);
4009     Radial rnew(p - n->pos);
4011     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
4012         int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
4013         /* 0 interpreted as "no snapping". */
4015         // 1. Snap to the closest PI/snaps angle, starting from zero.
4016         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
4018         // 2. Snap to the original angle, its opposite and perpendiculars
4019         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
4020             /* The closest PI/2 angle, starting from original angle */
4021             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
4023             // Snap to the closest.
4024             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
4025                        ? a_snapped
4026                        : a_ortho );
4027         }
4029         // 3. Snap to the angle of the opposite line, if any
4030         Inkscape::NodePath::Node *othernode = other->other;
4031         if (othernode) {
4032             Geom::Point other_to_snap(0,0);
4033             if (sp_node_side_is_line(n, other)) {
4034                 other_to_snap = othernode->pos - n->pos;
4035             } else {
4036                 other_to_snap = other->pos - n->pos;
4037             }
4038             if (Geom::L2(other_to_snap) > 1e-3) {
4039                 Radial rother_to_snap(other_to_snap);
4040                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
4041                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
4043                 // Snap to the closest.
4044                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
4045                        ? a_snapped
4046                        : a_oppo );
4047             }
4048         }
4050         rnew.a = a_snapped;
4051     }
4053     if (state & GDK_MOD1_MASK) {
4054         // lock handle length
4055         rnew.r = me->origin_radial.r;
4056     }
4058     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
4059         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
4060         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
4061         rother.a += rnew.a - rme.a;
4062         other->pos = Geom::Point(rother) + n->pos;
4063         if (other->knot) {
4064             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
4065             sp_knot_moveto(other->knot, other->pos);
4066         }
4067     }
4069     me->pos = Geom::Point(rnew) + n->pos;
4070     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
4072     // move knot, but without emitting the signal:
4073     // we cannot emit a "moved" signal because we're now processing it
4074     sp_knot_moveto(me->knot, me->pos);
4076     update_object(n->subpath->nodepath);
4078     /* status text */
4079     SPDesktop *desktop = n->subpath->nodepath->desktop;
4080     if (!desktop) return;
4081     SPEventContext *ec = desktop->event_context;
4082     if (!ec) return;
4084     Inkscape::MessageContext *mc = get_message_context(ec);
4086     if (!mc) return;
4088     double degrees = 180 / M_PI * rnew.a;
4089     if (degrees > 180) degrees -= 360;
4090     if (degrees < -180) degrees += 360;
4091     if (prefs->getBool("/options/compassangledisplay/value"))
4092         degrees = angle_to_compass (degrees);
4094     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
4096     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
4097          _("<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);
4099     g_string_free(length, TRUE);
4102 /**
4103  * Node handle event callback.
4104  */
4105 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
4107     gboolean ret = FALSE;
4108     switch (event->type) {
4109         case GDK_KEY_PRESS:
4110             switch (get_group0_keyval (&event->key)) {
4111                 case GDK_space:
4112                     if (event->key.state & GDK_BUTTON1_MASK) {
4113                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
4114                         stamp_repr(nodepath);
4115                         ret = TRUE;
4116                     }
4117                     break;
4118                 default:
4119                     break;
4120             }
4121             break;
4122         case GDK_ENTER_NOTIFY:
4123             // we use an experimentally determined threshold that seems to work fine
4124             if (Geom::L2(n->pos - knot->pos) < 0.75)
4125                 Inkscape::NodePath::Path::active_node = n;
4126             break;
4127         case GDK_LEAVE_NOTIFY:
4128             // we use an experimentally determined threshold that seems to work fine
4129             if (Geom::L2(n->pos - knot->pos) < 0.75)
4130                 Inkscape::NodePath::Path::active_node = NULL;
4131             break;
4132         default:
4133             break;
4134     }
4136     return ret;
4139 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
4140                                  Radial &rme, Radial &rother, gboolean const both)
4142     rme.a += angle;
4143     if ( both
4144          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4145          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4146     {
4147         rother.a += angle;
4148     }
4151 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
4152                                         Radial &rme, Radial &rother, gboolean const both)
4154     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
4156     gdouble r;
4157     if ( both
4158          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4159          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4160     {
4161         r = MAX(rme.r, rother.r);
4162     } else {
4163         r = rme.r;
4164     }
4166     gdouble const weird_angle = atan2(norm_angle, r);
4167 /* Bulia says norm_angle is just the visible distance that the
4168  * object's end must travel on the screen.  Left as 'angle' for want of
4169  * a better name.*/
4171     rme.a += weird_angle;
4172     if ( both
4173          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4174          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4175     {
4176         rother.a += weird_angle;
4177     }
4180 /**
4181  * Rotate one node.
4182  */
4183 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4185     Inkscape::NodePath::NodeSide *me, *other;
4186     bool both = false;
4188     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4189     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4191     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4192         me = &(n->p);
4193         other = &(n->n);
4194     } else if (!n->p.other) {
4195         me = &(n->n);
4196         other = &(n->p);
4197     } else {
4198         if (which > 0) { // right handle
4199             if (xn > xp) {
4200                 me = &(n->n);
4201                 other = &(n->p);
4202             } else {
4203                 me = &(n->p);
4204                 other = &(n->n);
4205             }
4206         } else if (which < 0){ // left handle
4207             if (xn <= xp) {
4208                 me = &(n->n);
4209                 other = &(n->p);
4210             } else {
4211                 me = &(n->p);
4212                 other = &(n->n);
4213             }
4214         } else { // both handles
4215             me = &(n->n);
4216             other = &(n->p);
4217             both = true;
4218         }
4219     }
4221     Radial rme(me->pos - n->pos);
4222     Radial rother(other->pos - n->pos);
4224     if (screen) {
4225         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4226     } else {
4227         node_rotate_one_internal (*n, angle, rme, rother, both);
4228     }
4230     me->pos = n->pos + Geom::Point(rme);
4232     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4233         other->pos =  n->pos + Geom::Point(rother);
4234     }
4236     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4237     // so here we just move all the knots without emitting move signals, for speed
4238     sp_node_update_handles(n, false);
4241 /**
4242  * Rotate selected nodes.
4243  */
4244 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4246     if (!nodepath || !nodepath->selected) return;
4248     if (g_list_length(nodepath->selected) == 1) {
4249        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4250         node_rotate_one (n, angle, which, screen);
4251     } else {
4252        // rotate as an object:
4254         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4255         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4256         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4257             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4258             box.expandTo (n->pos); // contain all selected nodes
4259         }
4261         gdouble rot;
4262         if (screen) {
4263             gdouble const zoom = nodepath->desktop->current_zoom();
4264             gdouble const zmove = angle / zoom;
4265             gdouble const r = Geom::L2(box.max() - box.midpoint());
4266             rot = atan2(zmove, r);
4267         } else {
4268             rot = angle;
4269         }
4271         Geom::Point rot_center;
4272         if (Inkscape::NodePath::Path::active_node == NULL)
4273             rot_center = box.midpoint();
4274         else
4275             rot_center = Inkscape::NodePath::Path::active_node->pos;
4277         Geom::Matrix t =
4278             Geom::Matrix (Geom::Translate(-rot_center)) *
4279             Geom::Matrix (Geom::Rotate(rot)) *
4280             Geom::Matrix (Geom::Translate(rot_center));
4282         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4283             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4284             n->pos *= t;
4285             n->n.pos *= t;
4286             n->p.pos *= t;
4287             sp_node_update_handles(n, false);
4288         }
4289     }
4291     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4294 /**
4295  * Scale one node.
4296  */
4297 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4299     bool both = false;
4300     Inkscape::NodePath::NodeSide *me, *other;
4302     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4303     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4305     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4306         me = &(n->p);
4307         other = &(n->n);
4308         n->code = NR_CURVETO;
4309     } else if (!n->p.other) {
4310         me = &(n->n);
4311         other = &(n->p);
4312         if (n->n.other)
4313             n->n.other->code = NR_CURVETO;
4314     } else {
4315         if (which > 0) { // right handle
4316             if (xn > xp) {
4317                 me = &(n->n);
4318                 other = &(n->p);
4319                 if (n->n.other)
4320                     n->n.other->code = NR_CURVETO;
4321             } else {
4322                 me = &(n->p);
4323                 other = &(n->n);
4324                 n->code = NR_CURVETO;
4325             }
4326         } else if (which < 0){ // left handle
4327             if (xn <= xp) {
4328                 me = &(n->n);
4329                 other = &(n->p);
4330                 if (n->n.other)
4331                     n->n.other->code = NR_CURVETO;
4332             } else {
4333                 me = &(n->p);
4334                 other = &(n->n);
4335                 n->code = NR_CURVETO;
4336             }
4337         } else { // both handles
4338             me = &(n->n);
4339             other = &(n->p);
4340             both = true;
4341             n->code = NR_CURVETO;
4342             if (n->n.other)
4343                 n->n.other->code = NR_CURVETO;
4344         }
4345     }
4347     Radial rme(me->pos - n->pos);
4348     Radial rother(other->pos - n->pos);
4350     rme.r += grow;
4351     if (rme.r < 0) rme.r = 0;
4352     if (rme.a == HUGE_VAL) {
4353         if (me->other) { // if direction is unknown, initialize it towards the next node
4354             Radial rme_next(me->other->pos - n->pos);
4355             rme.a = rme_next.a;
4356         } else { // if there's no next, initialize to 0
4357             rme.a = 0;
4358         }
4359     }
4360     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4361         rother.r += grow;
4362         if (rother.r < 0) rother.r = 0;
4363         if (rother.a == HUGE_VAL) {
4364             rother.a = rme.a + M_PI;
4365         }
4366     }
4368     me->pos = n->pos + Geom::Point(rme);
4370     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4371         other->pos = n->pos + Geom::Point(rother);
4372     }
4374     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4375     // so here we just move all the knots without emitting move signals, for speed
4376     sp_node_update_handles(n, false);
4379 /**
4380  * Scale selected nodes.
4381  */
4382 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4384     if (!nodepath || !nodepath->selected) return;
4386     if (g_list_length(nodepath->selected) == 1) {
4387         // scale handles of the single selected node
4388         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4389         node_scale_one (n, grow, which);
4390     } else {
4391         // scale nodes as an "object":
4393         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4394         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4395         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4396             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4397             box.expandTo (n->pos); // contain all selected nodes
4398         }
4400         if ( Geom::are_near(box.maxExtent(), 0) ) {
4401             SPEventContext *ec = nodepath->desktop->event_context;
4402             if (!ec) return;
4403             Inkscape::MessageContext *mc = get_message_context(ec);
4404             if (!mc) return;
4405             mc->setF(Inkscape::WARNING_MESSAGE,
4406                              _("Cannot scale nodes when all are at the same location."));
4407             return;
4408         }
4409         double scale = (box.maxExtent() + grow)/box.maxExtent();
4412         Geom::Point scale_center;
4413         if (Inkscape::NodePath::Path::active_node == NULL)
4414             scale_center = box.midpoint();
4415         else
4416             scale_center = Inkscape::NodePath::Path::active_node->pos;
4418         Geom::Matrix t =
4419             Geom::Translate(-scale_center) *
4420             Geom::Scale(scale, scale) *
4421             Geom::Translate(scale_center);
4423         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4424             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4425             n->pos *= t;
4426             n->n.pos *= t;
4427             n->p.pos *= t;
4428             sp_node_update_handles(n, false);
4429         }
4430     }
4432     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4435 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4437     if (!nodepath) return;
4438     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4441 /**
4442  * Flip selected nodes horizontally/vertically.
4443  */
4444 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis, boost::optional<Geom::Point> center)
4446     if (!nodepath || !nodepath->selected) return;
4448     if (g_list_length(nodepath->selected) == 1 && !center) {
4449         // flip handles of the single selected node
4450         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4451         double temp = n->p.pos[axis];
4452         n->p.pos[axis] = n->n.pos[axis];
4453         n->n.pos[axis] = temp;
4454         sp_node_update_handles(n, false);
4455     } else {
4456         // scale nodes as an "object":
4458         Geom::Rect box = sp_node_selected_bbox (nodepath);
4459         if (!center) {
4460             center = box.midpoint();
4461         }
4462         Geom::Matrix t =
4463             Geom::Matrix (Geom::Translate(- *center)) *
4464             Geom::Matrix ((axis == Geom::X)? Geom::Scale(-1, 1) : Geom::Scale(1, -1)) *
4465             Geom::Matrix (Geom::Translate(*center));
4467         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4468             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4469             n->pos *= t;
4470             n->n.pos *= t;
4471             n->p.pos *= t;
4472             sp_node_update_handles(n, false);
4473         }
4474     }
4476     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4479 Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4481     g_assert (nodepath->selected);
4483     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4484     Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4485     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4486         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4487         box.expandTo (n->pos); // contain all selected nodes
4488     }
4489     return box;
4492 //-----------------------------------------------
4493 /**
4494  * Return new subpath under given nodepath.
4495  */
4496 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4498     g_assert(nodepath);
4499     g_assert(nodepath->desktop);
4501    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4503     s->nodepath = nodepath;
4504     s->closed = FALSE;
4505     s->nodes = NULL;
4506     s->first = NULL;
4507     s->last = NULL;
4509     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4510     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4511     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4513     return s;
4516 /**
4517  * Destroy nodes in subpath, then subpath itself.
4518  */
4519 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4521     g_assert(subpath);
4522     g_assert(subpath->nodepath);
4523     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4525     while (subpath->nodes) {
4526         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4527     }
4529     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4531     g_free(subpath);
4534 /**
4535  * Link head to tail in subpath.
4536  */
4537 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4539     g_assert(!sp->closed);
4540     g_assert(sp->last != sp->first);
4541     g_assert(sp->first->code == NR_MOVETO);
4543     sp->closed = TRUE;
4545     //Link the head to the tail
4546     sp->first->p.other = sp->last;
4547     sp->last->n.other  = sp->first;
4548     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4549     sp->first          = sp->last;
4551     //Remove the extra end node
4552     sp_nodepath_node_destroy(sp->last->n.other);
4555 /**
4556  * Open closed (loopy) subpath at node.
4557  */
4558 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4560     g_assert(sp->closed);
4561     g_assert(n->subpath == sp);
4562     g_assert(sp->first == sp->last);
4564     /* We create new startpoint, current node will become last one */
4566    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4567                                                 &n->pos, &n->pos, &n->n.pos);
4570     sp->closed        = FALSE;
4572     //Unlink to make a head and tail
4573     sp->first         = new_path;
4574     sp->last          = n;
4575     n->n.other        = NULL;
4576     new_path->p.other = NULL;
4579 /**
4580  * Return new node in subpath with given properties.
4581  * \param pos Position of node.
4582  * \param ppos Handle position in previous direction
4583  * \param npos Handle position in previous direction
4584  */
4585 Inkscape::NodePath::Node *
4586 sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp, Inkscape::NodePath::Node *next, Inkscape::NodePath::NodeType type, NRPathcode code, Geom::Point *ppos, Geom::Point *pos, Geom::Point *npos)
4588     g_assert(sp);
4589     g_assert(sp->nodepath);
4590     g_assert(sp->nodepath->desktop);
4592     if (nodechunk == NULL)
4593         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4595     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4597     n->subpath  = sp;
4599     if (type != Inkscape::NodePath::NODE_NONE) {
4600         // use the type from sodipodi:nodetypes
4601         n->type = type;
4602     } else {
4603         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4604             // points are (almost) collinear
4605             if (Geom::L2(*pos - *ppos) < 1e-6 || Geom::L2(*pos - *npos) < 1e-6) {
4606                 // endnode, or a node with a retracted handle
4607                 n->type = Inkscape::NodePath::NODE_CUSP;
4608             } else {
4609                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4610             }
4611         } else {
4612             n->type = Inkscape::NodePath::NODE_CUSP;
4613         }
4614     }
4616     n->code     = code;
4617     n->selected = FALSE;
4618     n->pos      = *pos;
4619     n->p.pos    = *ppos;
4620     n->n.pos    = *npos;
4622     n->dragging_out = NULL;
4624     Inkscape::NodePath::Node *prev;
4625     if (next) {
4626         //g_assert(g_list_find(sp->nodes, next));
4627         prev = next->p.other;
4628     } else {
4629         prev = sp->last;
4630     }
4632     if (prev)
4633         prev->n.other = n;
4634     else
4635         sp->first = n;
4637     if (next)
4638         next->p.other = n;
4639     else
4640         sp->last = n;
4642     n->p.other = prev;
4643     n->n.other = next;
4645     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"));
4646     sp_knot_set_position(n->knot, *pos, 0);
4648     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4649     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4650     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4652     sp_nodepath_update_node_knot(n);
4654     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4655     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4656     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4657     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4658     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4659     sp_knot_show(n->knot);
4661     // We only create handle knots and lines on demand
4662     n->p.knot = NULL;
4663     n->p.line = NULL;
4664     n->n.knot = NULL;
4665     n->n.line = NULL;
4667     sp->nodes = g_list_prepend(sp->nodes, n);
4669     return n;
4672 /**
4673  * Destroy node and its knots, link neighbors in subpath.
4674  */
4675 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4677     g_assert(node);
4678     g_assert(node->subpath);
4679     g_assert(SP_IS_KNOT(node->knot));
4681    Inkscape::NodePath::SubPath *sp = node->subpath;
4683     if (node->selected) { // first, deselect
4684         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4685         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4686     }
4688     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4690     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4691     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4692     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4693     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4694     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4695     g_object_unref(G_OBJECT(node->knot));
4697     if (node->p.knot) {
4698         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4699         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4700         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4701         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4702         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4703         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4704         g_object_unref(G_OBJECT(node->p.knot));
4705         node->p.knot = NULL;
4706     }
4708     if (node->n.knot) {
4709         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4710         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4711         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4712         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4713         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4714         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4715         g_object_unref(G_OBJECT(node->n.knot));
4716         node->n.knot = NULL;
4717     }
4719     if (node->p.line)
4720         gtk_object_destroy(GTK_OBJECT(node->p.line));
4721     if (node->n.line)
4722         gtk_object_destroy(GTK_OBJECT(node->n.line));
4724     if (sp->nodes) { // there are others nodes on the subpath
4725         if (sp->closed) {
4726             if (sp->first == node) {
4727                 g_assert(sp->last == node);
4728                 sp->first = node->n.other;
4729                 sp->last = sp->first;
4730             }
4731             node->p.other->n.other = node->n.other;
4732             node->n.other->p.other = node->p.other;
4733         } else {
4734             if (sp->first == node) {
4735                 sp->first = node->n.other;
4736                 sp->first->code = NR_MOVETO;
4737             }
4738             if (sp->last == node) sp->last = node->p.other;
4739             if (node->p.other) node->p.other->n.other = node->n.other;
4740             if (node->n.other) node->n.other->p.other = node->p.other;
4741         }
4742     } else { // this was the last node on subpath
4743         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4744     }
4746     g_mem_chunk_free(nodechunk, node);
4749 /**
4750  * Returns one of the node's two sides.
4751  * \param which Indicates which side.
4752  * \return Pointer to previous node side if which==-1, next if which==1.
4753  */
4754 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4756     g_assert(node);
4757     Inkscape::NodePath::NodeSide * result = 0;
4758     switch (which) {
4759         case -1:
4760             result = &node->p;
4761             break;
4762         case 1:
4763             result = &node->n;
4764             break;
4765         default:
4766             g_assert_not_reached();
4767     }
4769     return result;
4772 /**
4773  * Return the other side of the node, given one of its sides.
4774  */
4775 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4777     g_assert(node);
4778     Inkscape::NodePath::NodeSide *result = 0;
4780     if (me == &node->p) {
4781         result = &node->n;
4782     } else if (me == &node->n) {
4783         result = &node->p;
4784     } else {
4785         g_assert_not_reached();
4786     }
4788     return result;
4791 /**
4792  * Return NRPathcode on the given side of the node.
4793  */
4794 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4796     g_assert(node);
4798     NRPathcode result = NR_END;
4799     if (me == &node->p) {
4800         if (node->p.other) {
4801             result = (NRPathcode)node->code;
4802         } else {
4803             result = NR_MOVETO;
4804         }
4805     } else if (me == &node->n) {
4806         if (node->n.other) {
4807             result = (NRPathcode)node->n.other->code;
4808         } else {
4809             result = NR_MOVETO;
4810         }
4811     } else {
4812         g_assert_not_reached();
4813     }
4815     return result;
4818 /**
4819  * Return node with the given index
4820  */
4821 Inkscape::NodePath::Node *
4822 sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index)
4824     Inkscape::NodePath::Node *e = NULL;
4826     if (!nodepath) {
4827         return e;
4828     }
4830     //find segment
4831     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4833         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4834         int n = g_list_length(sp->nodes);
4835         if (sp->closed) {
4836             n++;
4837         }
4839         //if the piece belongs to this subpath grab it
4840         //otherwise move onto the next subpath
4841         if (index < n) {
4842             e = sp->first;
4843             for (int i = 0; i < index; ++i) {
4844                 e = e->n.other;
4845             }
4846             break;
4847         } else {
4848             if (sp->closed) {
4849                 index -= (n+1);
4850             } else {
4851                 index -= n;
4852             }
4853         }
4854     }
4856     return e;
4859 /**
4860  * Returns plain text meaning of node type.
4861  */
4862 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4864     unsigned retracted = 0;
4865     bool endnode = false;
4867     for (int which = -1; which <= 1; which += 2) {
4868         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4869         if (side->other && Geom::L2(side->pos - node->pos) < 1e-6)
4870             retracted ++;
4871         if (!side->other)
4872             endnode = true;
4873     }
4875     if (retracted == 0) {
4876         if (endnode) {
4877                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4878                 return _("end node");
4879         } else {
4880             switch (node->type) {
4881                 case Inkscape::NodePath::NODE_CUSP:
4882                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4883                     return _("cusp");
4884                 case Inkscape::NodePath::NODE_SMOOTH:
4885                     // TRANSLATORS: "smooth" is an adjective here
4886                     return _("smooth");
4887                 case Inkscape::NodePath::NODE_AUTO:
4888                     return _("auto");
4889                 case Inkscape::NodePath::NODE_SYMM:
4890                     return _("symmetric");
4891             }
4892         }
4893     } else if (retracted == 1) {
4894         if (endnode) {
4895             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4896             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4897         } else {
4898             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4899         }
4900     } else {
4901         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4902     }
4904     return NULL;
4907 /**
4908  * Handles content of statusbar as long as node tool is active.
4909  */
4910 void
4911 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4913     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");
4914     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4916     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4917     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4918     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4919     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4921     SPDesktop *desktop = NULL;
4922     if (nodepath) {
4923         desktop = nodepath->desktop;
4924     } else {
4925         desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above
4926     }
4928     SPEventContext *ec = desktop->event_context;
4929     if (!ec) return;
4931     Inkscape::MessageContext *mc = get_message_context(ec);
4932     if (!mc) return;
4934     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4936     if (selected_nodes == 0) {
4937         Inkscape::Selection *sel = desktop->selection;
4938         if (!sel || sel->isEmpty()) {
4939             mc->setF(Inkscape::NORMAL_MESSAGE,
4940                      _("Select a single object to edit its nodes or handles."));
4941         } else {
4942             if (nodepath) {
4943             mc->setF(Inkscape::NORMAL_MESSAGE,
4944                      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.",
4945                               "<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.",
4946                               total_nodes),
4947                      total_nodes);
4948             } else {
4949                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4950                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4951                 } else {
4952                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4953                 }
4954             }
4955         }
4956     } else if (nodepath && selected_nodes == 1) {
4957         mc->setF(Inkscape::NORMAL_MESSAGE,
4958                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4959                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4960                           total_nodes),
4961                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4962     } else {
4963         if (selected_subpaths > 1) {
4964             mc->setF(Inkscape::NORMAL_MESSAGE,
4965                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4966                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4967                               total_nodes),
4968                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4969         } else {
4970             mc->setF(Inkscape::NORMAL_MESSAGE,
4971                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4972                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4973                               total_nodes),
4974                      selected_nodes, total_nodes, when_selected);
4975         }
4976     }
4979 /*
4980  * returns a *copy* of the curve of that object.
4981  */
4982 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4983     if (!object)
4984         return NULL;
4986     SPCurve *curve = NULL;
4987     if (SP_IS_PATH(object)) {
4988         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4989         curve = curve_new->copy();
4990     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4991         const gchar *svgd = object->repr->attribute(key);
4992         if (svgd) {
4993             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4994             SPCurve *curve_new = new SPCurve(pv);
4995             if (curve_new) {
4996                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4997             }
4998         }
4999     }
5001     return curve;
5004 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
5005     if (!np || !np->object || !curve)
5006         return;
5008     if (SP_IS_PATH(np->object)) {
5009         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
5010             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
5011         } else {
5012             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
5013         }
5014     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
5015         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(np->object)->get_lpe();
5016         if (lpe) {
5017             Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( lpe->getParameter(np->repr_key) );
5018             if (pathparam) {
5019                 pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
5020                 np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
5021             }
5022         }
5023     }
5026 /*
5027 SPCanvasItem *
5028 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
5029     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
5031 */
5034 /// \todo this code to generate a helper canvasitem from an spcurve should be moved to different file
5035 SPCanvasItem *
5036 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const Geom::Matrix & i2d, guint32 color = 0xff0000ff) {
5037     SPCurve *flash_curve = curve->copy();
5038     flash_curve->transform(i2d);
5039     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
5040     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
5041     // unless we also flash the nodes...
5042     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
5043     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
5044     sp_canvas_item_show(canvasitem);
5045     flash_curve->unref();
5046     return canvasitem;
5049 SPCanvasItem *
5050 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item) {
5051     if (!item || !desktop) {
5052         return NULL;
5053     }
5055     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5056     guint32 color = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
5058     Geom::Matrix i2d = sp_item_i2d_affine(item);
5060     SPCurve *curve = NULL;
5061     if (SP_IS_PATH(item)) {
5062         curve = sp_path_get_curve_for_edit(SP_PATH(item));
5063     } else if (SP_IS_RECT(item)) {
5064         Geom::Rect rect = sp_rect_get_rect(SP_RECT(item));
5065         curve = SPCurve::new_from_rect(rect);
5066     } else {
5067         g_warning ("-----> sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item): TODO: generate the helper path for this item type!\n");
5068         return NULL;
5069     }
5071     SPCanvasItem * helperpath = sp_nodepath_generate_helperpath(desktop, curve, i2d, color);
5073     curve->unref();
5075     return helperpath;
5079 // TODO: Merge this with sp_nodepath_make_helper_item()!
5080 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
5081     np->show_helperpath = show;
5083     if (show) {
5084         SPCurve *helper_curve = np->curve->copy();
5085         helper_curve->transform(np->i2d);
5086         if (!np->helper_path) {
5087             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
5089             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
5090             sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(np->helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
5091             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
5092             sp_canvas_item_move_to_z(np->helper_path, 0);
5093             sp_canvas_item_show(np->helper_path);
5094         } else {
5095             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
5096         }
5097         helper_curve->unref();
5098     } else {
5099         if (np->helper_path) {
5100             GtkObject *temp = np->helper_path;
5101             np->helper_path = NULL;
5102             gtk_object_destroy(temp);
5103         }
5104     }
5107 /* sp_nodepath_make_straight_path:
5108  *   Prevents user from curving the path by dragging a segment or activating handles etc.
5109  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
5110  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
5111  */
5112 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
5113     np->straight_path = true;
5114     np->show_handles = false;
5115     g_message("add code to make the path straight.");
5116     // do sp_nodepath_convert_node_type on all nodes?
5117     // coding tip: search for this text : "Make selected segments lines"
5120 /*
5121   Local Variables:
5122   mode:c++
5123   c-file-style:"stroustrup"
5124   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5125   indent-tabs-mode:nil
5126   fill-column:99
5127   End:
5128 */
5129 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :