Code

Add an option to the preferences to _only_ snap the node closest to the mouse pointer
[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 "libnr/nr-matrix-ops.h"
49 #include "svg/svg.h"
50 #include "verbs.h"
51 #include "display/bezier-utils.h"
52 #include <vector>
53 #include <algorithm>
54 #include <cstring>
55 #include <cmath>
56 #include "live_effects/lpeobject.h"
57 #include "live_effects/lpeobject-reference.h"
58 #include "live_effects/effect.h"
59 #include "live_effects/parameter/parameter.h"
60 #include "live_effects/parameter/path.h"
61 #include "util/mathfns.h"
62 #include "display/snap-indicator.h"
63 #include "snapped-point.h"
65 namespace Geom { class Matrix; }
67 /// \todo
68 /// evil evil evil. FIXME: conflict of two different Path classes!
69 /// There is a conflict in the namespace between two classes named Path.
70 /// #include "sp-flowtext.h"
71 /// #include "sp-flowregion.h"
73 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
74 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
75 GType sp_flowregion_get_type (void);
76 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
77 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
78 GType sp_flowtext_get_type (void);
79 // end evil workaround
81 #include "helper/stlport.h"
84 /// \todo fixme: Implement these via preferences */
86 #define NODE_FILL          0xbfbfbf00
87 #define NODE_STROKE        0x000000ff
88 #define NODE_FILL_HI       0xff000000
89 #define NODE_STROKE_HI     0x000000ff
90 #define NODE_FILL_SEL      0x0000ffff
91 #define NODE_STROKE_SEL    0x000000ff
92 #define NODE_FILL_SEL_HI   0xff000000
93 #define NODE_STROKE_SEL_HI 0x000000ff
94 #define KNOT_FILL          0xffffffff
95 #define KNOT_STROKE        0x000000ff
96 #define KNOT_FILL_HI       0xff000000
97 #define KNOT_STROKE_HI     0x000000ff
99 static GMemChunk *nodechunk = NULL;
101 /* Creation from object */
103 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t);
104 static Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length);
106 /* Object updating */
108 static void stamp_repr(Inkscape::NodePath::Path *np);
109 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
110 static gchar *create_typestr(Inkscape::NodePath::Path *np);
112 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
114 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
116 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
118 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
120 /* Adjust handle placement, if the node or the other handle is moved */
121 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
122 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
123 static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node);
125 /* Node event callbacks */
126 static void node_clicked(SPKnot *knot, guint state, gpointer data);
127 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
128 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
129 static gboolean node_request(SPKnot *knot, Geom::Point const &p, guint state, gpointer data);
131 /* Handle event callbacks */
132 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
133 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
134 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
135 static gboolean node_handle_request(SPKnot *knot, Geom::Point const &p, guint state, gpointer data);
136 static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data);
137 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
139 /* Constructors and destructors */
141 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
142 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
143 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
144 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
145 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
146                                          Geom::Point *ppos, Geom::Point *pos, Geom::Point *npos);
147 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
149 /* Helpers */
151 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
152 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
153 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
155 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
156 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
158 // active_node indicates mouseover node
159 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
161 static SPCanvasItem *
162 sp_nodepath_make_helper_item(Inkscape::NodePath::Path *np, /*SPDesktop *desktop, */const SPCurve *curve, bool show = false) {
163     SPCurve *helper_curve = curve->copy();
164     helper_curve->transform(np->i2d);
165     SPCanvasItem *helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
166     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
167     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helper_path), 0, SP_WIND_RULE_NONZERO);
168     sp_canvas_item_move_to_z(helper_path, 0);
169     if (show) {
170         sp_canvas_item_show(helper_path);
171     }
172     helper_curve->unref();
173     return helper_path;
176 static SPCanvasItem *
177 canvasitem_from_pathvec(Inkscape::NodePath::Path *np, Geom::PathVector const &pathv, bool show) {
178     SPCurve *helper_curve = new SPCurve(pathv);
179     return sp_nodepath_make_helper_item(np, helper_curve, show);
182 static void
183 sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) {
184     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> > helper_path_vec;
185     if (!SP_IS_LPE_ITEM(np->item)) {
186         g_print ("Only LPEItems can have helperpaths!\n");
187         return;
188     }
190     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
191     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
192     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
193         Inkscape::LivePathEffect::LPEObjectReference *lperef = (*i);
194         Inkscape::LivePathEffect::Effect *lpe = lperef->lpeobject->get_lpe();
195         if (lpe) {
196             // create new canvas items from the effect's helper paths
197             std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
198             for (std::vector<Geom::PathVector>::iterator j = hpaths.begin(); j != hpaths.end(); ++j) {
199                 np->helper_path_vec[lpe].push_back(canvasitem_from_pathvec(np, *j, true));
200             }
201         }
202     }
205 void
206 sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np) {
207     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> > helper_path_vec;
208     if (!SP_IS_LPE_ITEM(np->item)) {
209         g_print ("Only LPEItems can have helperpaths!\n");
210         return;
211     }
213     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
214     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
215     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
216         Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->get_lpe();
217         if (lpe) {
218             /* update canvas items from the effect's helper paths; note that this code relies on the
219              * fact that getHelperPaths() will always return the same number of helperpaths in the same
220              * order as during their creation in sp_nodepath_create_helperpaths
221              */
222             std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
223             for (unsigned int j = 0; j < hpaths.size(); ++j) {
224                 SPCurve *curve = new SPCurve(hpaths[j]);
225                 curve->transform(np->i2d);
226                 sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH((np->helper_path_vec[lpe])[j]), curve);
227                 curve = curve->unref();
228             }
229         }
230     }
233 static void
234 sp_nodepath_destroy_helperpaths(Inkscape::NodePath::Path *np) {
235     for (HelperPathList::iterator i = np->helper_path_vec.begin(); i != np->helper_path_vec.end(); ++i) {
236         for (std::vector<SPCanvasItem *>::iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) {
237             GtkObject *temp = *j;
238             *j = NULL;
239             gtk_object_destroy(temp);
240         }
241     }
242     np->helper_path_vec.clear();
246 /**
247  * \brief Creates new nodepath from item
248  *
249  * \todo create proper constructor for nodepath::path, this method returns null a constructor cannot so this cannot be simply converted to constructor.
250  */
251 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
253     Inkscape::XML::Node *repr = object->repr;
255     /** \todo
256      * FIXME: remove this. We don't want to edit paths inside flowtext.
257      * Instead we will build our flowtext with cloned paths, so that the
258      * real paths are outside the flowtext and thus editable as usual.
259      */
260     if (SP_IS_FLOWTEXT(object)) {
261         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
262             if SP_IS_FLOWREGION(child) {
263                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
264                 if (grandchild && SP_IS_PATH(grandchild)) {
265                     object = SP_ITEM(grandchild);
266                     break;
267                 }
268             }
269         }
270     }
272     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
274     if (curve == NULL) {
275         return NULL;
276     }
278     if (curve->get_segment_count() < 1) {
279         curve->unref();
280         return NULL; // prevent crash for one-node paths
281     }
283     //Create new nodepath
284     Inkscape::NodePath::Path *np = new Inkscape::NodePath::Path();
285     if (!np) {
286         curve->unref();
287         return NULL;
288     }
290     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
292     // Set defaults
293     np->desktop     = desktop;
294     np->object      = object;
295     np->subpaths    = NULL;
296     np->selected    = NULL;
297     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
298     np->local_change = 0;
299     np->show_handles = show_handles;
300     np->helper_path = NULL;
301     np->helperpath_rgba = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
302     np->helperpath_width = 1.0;
303     np->curve = curve->copy();
304     np->show_helperpath = prefs->getBool("/tools/nodes/show_helperpath");
305     if (SP_IS_LPE_ITEM(object)) {
306         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
307         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
308             np->show_helperpath = true;
309         }            
310     }
311     np->straight_path = false;
312     if (IS_LIVEPATHEFFECT(object) && item) {
313         np->item = item;
314     } else {
315         np->item = SP_ITEM(object);
316     }
317     
318     np->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
320     // we need to update item's transform from the repr here,
321     // because they may be out of sync when we respond
322     // to a change in repr by regenerating nodepath     --bb
323     sp_object_read_attr(SP_OBJECT(np->item), "transform");
325     np->i2d  = sp_item_i2d_affine(np->item);
326     np->d2i  = np->i2d.inverse();
328     np->repr = repr;
329     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)
330         np->repr_key = g_strdup(repr_key_in);
331         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
332         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(object)->get_lpe();
333         if (!lpe) {
334             g_error("sp_nodepath_new: lpeobject without real lpe passed as argument!");
335             delete np;
336         }
337         Inkscape::LivePathEffect::Parameter *lpeparam = lpe->getParameter(repr_key_in);
338         if (lpeparam) {
339             lpeparam->param_setup_nodepath(np);
340         }
341     } else {
342         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
343         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
344             np->repr_key = g_strdup("inkscape:original-d");
346             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
347             if (lpe) {
348                 lpe->setup_nodepath(np);
349             }
350         } else {
351             np->repr_key = g_strdup("d");
352         }
353     }
355     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
356      * So for example a closed rectangle has a nodetypestring of length 5.
357      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
358     Geom::PathVector pathv_sanitized = pathv_to_linear_and_cubic_beziers(np->curve->get_pathvector());
359     np->curve->set_pathvector(pathv_sanitized);
360     guint length = np->curve->get_segment_count();
361     for (Geom::PathVector::const_iterator pit = pathv_sanitized.begin(); pit != pathv_sanitized.end(); ++pit) {
362         length += pit->empty() ? 0 : 1;
363     }
365     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
366     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
368     // create the subpath(s) from the bpath
369     subpaths_from_pathvector(np, pathv_sanitized, typestr);
371     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
372     np->subpaths = g_list_reverse(np->subpaths);
374     delete[] typestr;
375     curve->unref();
377     // Draw helper curve
378     if (np->show_helperpath) {
379         np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true);
380     }
382     sp_nodepath_create_helperpaths(np);
384     return np;
387 /**
388  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
389  */
390 Inkscape::NodePath::Path::~Path() {
391     while (this->subpaths) {
392         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) this->subpaths->data);
393     }
395     //Inform the ShapeEditor that made me, if any, that I am gone.
396     if (this->shape_editor)
397         this->shape_editor->nodepath_destroyed();
399     g_assert(!this->selected);
401     if (this->helper_path) {
402         GtkObject *temp = this->helper_path;
403         this->helper_path = NULL;
404         gtk_object_destroy(temp);
405     }
406     if (this->curve) {
407         this->curve->unref();
408         this->curve = NULL;
409     }
411     if (this->repr_key) {
412         g_free(this->repr_key);
413         this->repr_key = NULL;
414     }
415     if (this->repr_nodetypes_key) {
416         g_free(this->repr_nodetypes_key);
417         this->repr_nodetypes_key = NULL;
418     }
420     sp_nodepath_destroy_helperpaths(this);
422     this->desktop = NULL;
425 /**
426  *  Return the node count of a given NodeSubPath.
427  */
428 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
430     int nodeCount = 0;
432     if (subpath) {
433         nodeCount = g_list_length(subpath->nodes);
434     }
436     return nodeCount;
439 /**
440  *  Return the node count of a given NodePath.
441  */
442 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
444     gint nodeCount = 0;
445     if (np) {
446         for (GList *item = np->subpaths ; item ; item=item->next) {
447             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
448             nodeCount += g_list_length(subpath->nodes);
449         }
450     }
451     return nodeCount;
454 /**
455  *  Return the subpath count of a given NodePath.
456  */
457 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
459     gint nodeCount = 0;
460     if (np) {
461         nodeCount = g_list_length(np->subpaths);
462     }
463     return nodeCount;
466 /**
467  *  Return the selected node count of a given NodePath.
468  */
469 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
471     gint nodeCount = 0;
472     if (np) {
473         nodeCount = g_list_length(np->selected);
474     }
475     return nodeCount;
478 /**
479  *  Return the number of subpaths where nodes are selected in a given NodePath.
480  */
481 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
483     gint nodeCount = 0;
484     if (np && np->selected) {
485         if (!np->selected->next) {
486             nodeCount = 1;
487         } else {
488             for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
489                 Inkscape::NodePath::SubPath *subpath = static_cast<Inkscape::NodePath::SubPath *>(spl->data);
490                 for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
491                     Inkscape::NodePath::Node *node = static_cast<Inkscape::NodePath::Node *>(nl->data);
492                     if (node->selected) {
493                         nodeCount++;
494                         break;
495                     }
496                 }
497             }
498         }
499     }
500     return nodeCount;
503 /**
504  * Clean up a nodepath after editing.
505  *
506  * Currently we are deleting trivial subpaths.
507  */
508 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
510     GList *badSubPaths = NULL;
512     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
513     for (GList *l = nodepath->subpaths; l ; l=l->next) {
514        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
515        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
516             badSubPaths = g_list_append(badSubPaths, sp);
517     }
519     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
520     //also removes the subpath from nodepath->subpaths
521     for (GList *l = badSubPaths; l ; l=l->next) {
522        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
523         sp_nodepath_subpath_destroy(sp);
524     }
526     g_list_free(badSubPaths);
529 /**
530  * Create new nodepaths from pathvector, make it subpaths of np.
531  * \param t The node type array.
532  */
533 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
535     guint i = 0;  // index into node type array
536     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
537         if (pit->empty())
538             continue;  // don't add single knot paths
540         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
542         Geom::Point ppos = pit->initialPoint() * np->i2d;
543         NRPathcode pcode = NR_MOVETO;
545         /* 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)*/
546         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
547             if( dynamic_cast<Geom::LineSegment const*>(&*cit) ||
548                 dynamic_cast<Geom::HLineSegment const*>(&*cit) ||
549                 dynamic_cast<Geom::VLineSegment const*>(&*cit) )
550             {
551                 Geom::Point pos = cit->initialPoint() * (Geom::Matrix)np->i2d;
552                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
554                 ppos = cit->finalPoint() * (Geom::Matrix)np->i2d;
555                 pcode = NR_LINETO;
556             }
557             else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
558                 std::vector<Geom::Point> points = cubic_bezier->points();
559                 Geom::Point pos = points[0] * (Geom::Matrix)np->i2d;
560                 Geom::Point npos = points[1] * (Geom::Matrix)np->i2d;
561                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
563                 ppos = points[2] * (Geom::Matrix)np->i2d;
564                 pcode = NR_CURVETO;
565             }
566         }
568         if (pit->closed()) {
569             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
570             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
571              * If the length is zero, don't add it to the nodepath. */
572             Geom::Curve const &closing_seg = pit->back_closed();
573             // Don't use !closing_seg.isDegenerate() as it is too precise, and does not account for floating point rounding probs (LP bug #257289)
574             if ( ! are_near(closing_seg.initialPoint(), closing_seg.finalPoint()) ) {
575                 Geom::Point pos = closing_seg.finalPoint() * (Geom::Matrix)np->i2d;
576                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
577             }
579             sp_nodepath_subpath_close(sp);
580         }
581     }
584 /**
585  * Convert from sodipodi:nodetypes to new style type array.
586  */
587 static
588 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
590     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
592     guint pos = 0;
594     if (types) {
595         for (guint i = 0; types[i] && ( i < length ); i++) {
596             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
597             if (types[i] != '\0') {
598                 switch (types[i]) {
599                     case 's':
600                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
601                         break;
602                     case 'a':
603                         typestr[pos++] =Inkscape::NodePath::NODE_AUTO;
604                         break;
605                     case 'z':
606                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
607                         break;
608                     case 'c':
609                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
610                         break;
611                     default:
612                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
613                         break;
614                 }
615             }
616         }
617     }
619     while (pos < length) {
620         typestr[pos++] = Inkscape::NodePath::NODE_NONE;
621     }
623     return typestr;
626 /**
627  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
628  * updated but repr is not (for speed). Used during curve and node drag.
629  */
630 static void update_object(Inkscape::NodePath::Path *np)
632     g_assert(np);
634     np->curve->unref();
635     np->curve = create_curve(np);
637     sp_nodepath_set_curve(np, np->curve);
639     if (np->show_helperpath) {
640         SPCurve * helper_curve = np->curve->copy();
641         helper_curve->transform(np->i2d);
642         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
643         helper_curve->unref();
644     }
646     // updating helperpaths of LPEItems is now done in sp_lpe_item_update();
647     //sp_nodepath_update_helperpaths(np);
649     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
650     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
651     np->shape_editor->update_knotholder();
654 /**
655  * Update XML path node with data from path object.
656  */
657 static void update_repr_internal(Inkscape::NodePath::Path *np)
659     g_assert(np);
661     Inkscape::XML::Node *repr = np->object->repr;
663     np->curve->unref();
664     np->curve = create_curve(np);
666     gchar *typestr = create_typestr(np);
667     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
669     // determine if path has an effect applied and write to correct "d" attribute.
670     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
671         np->local_change++;
672         repr->setAttribute(np->repr_key, svgpath);
673     }
675     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
676         np->local_change++;
677         repr->setAttribute(np->repr_nodetypes_key, typestr);
678     }
680     g_free(svgpath);
681     g_free(typestr);
683     if (np->show_helperpath) {
684         SPCurve * helper_curve = np->curve->copy();
685         helper_curve->transform(np->i2d);
686         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
687         helper_curve->unref();
688     }
690     // TODO: do we need this call here? after all, update_object() should have been called just before
691     //sp_nodepath_update_helperpaths(np);
694 /**
695  * Update XML path node with data from path object, commit changes forever.
696  */
697 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
699     //fixme: np can be NULL, so check before proceeding
700     g_return_if_fail(np != NULL);
702     update_repr_internal(np);
703     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
705     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
706                      annotation);
709 /**
710  * Update XML path node with data from path object, commit changes with undo.
711  */
712 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
714     update_repr_internal(np);
715     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
716                            annotation);
719 /**
720  * Make duplicate of path, replace corresponding XML node in tree, commit.
721  */
722 static void stamp_repr(Inkscape::NodePath::Path *np)
724     g_assert(np);
726     Inkscape::XML::Node *old_repr = np->object->repr;
727     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
729     // remember the position of the item
730     gint pos = old_repr->position();
731     // remember parent
732     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
734     SPCurve *curve = create_curve(np);
735     gchar *typestr = create_typestr(np);
737     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
739     new_repr->setAttribute(np->repr_key, svgpath);
740     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
742     // add the new repr to the parent
743     parent->appendChild(new_repr);
744     // move to the saved position
745     new_repr->setPosition(pos > 0 ? pos : 0);
747     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
748                      _("Stamp"));
750     Inkscape::GC::release(new_repr);
751     g_free(svgpath);
752     g_free(typestr);
753     curve->unref();
756 /**
757  * Create curve from path.
758  */
759 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
761     SPCurve *curve = new SPCurve();
763     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
764        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
765        curve->moveto(sp->first->pos * np->d2i);
766        Inkscape::NodePath::Node *n = sp->first->n.other;
767         while (n) {
768             Geom::Point const end_pt = n->pos * np->d2i;
769             if (!IS_FINITE(n->pos[0]) || !IS_FINITE(n->pos[1])){
770                 g_message("niet finite");
771             }
772             switch (n->code) {
773                 case NR_LINETO:
774                     curve->lineto(end_pt);
775                     break;
776                 case NR_CURVETO:
777                     curve->curveto(n->p.other->n.pos * np->d2i,
778                                      n->p.pos * np->d2i,
779                                      end_pt);
780                     break;
781                 default:
782                     g_assert_not_reached();
783                     break;
784             }
785             if (n != sp->last) {
786                 n = n->n.other;
787             } else {
788                 n = NULL;
789             }
790         }
791         if (sp->closed) {
792             curve->closepath();
793         }
794     }
796     return curve;
799 /**
800  * Convert path type string to sodipodi:nodetypes style.
801  */
802 static gchar *create_typestr(Inkscape::NodePath::Path *np)
804     gchar *typestr = g_new(gchar, 32);
805     gint len = 32;
806     gint pos = 0;
808     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
809        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
811         if (pos >= len) {
812             typestr = g_renew(gchar, typestr, len + 32);
813             len += 32;
814         }
816         typestr[pos++] = 'c';
818        Inkscape::NodePath::Node *n;
819         n = sp->first->n.other;
820         while (n) {
821             gchar code;
823             switch (n->type) {
824                 case Inkscape::NodePath::NODE_CUSP:
825                     code = 'c';
826                     break;
827                 case Inkscape::NodePath::NODE_SMOOTH:
828                     code = 's';
829                     break;
830                 case Inkscape::NodePath::NODE_AUTO:
831                     code = 'a';
832                     break;
833                 case Inkscape::NodePath::NODE_SYMM:
834                     code = 'z';
835                     break;
836                 default:
837                     g_assert_not_reached();
838                     code = '\0';
839                     break;
840             }
842             if (pos >= len) {
843                 typestr = g_renew(gchar, typestr, len + 32);
844                 len += 32;
845             }
847             typestr[pos++] = code;
849             if (n != sp->last) {
850                 n = n->n.other;
851             } else {
852                 n = NULL;
853             }
854         }
855     }
857     if (pos >= len) {
858         typestr = g_renew(gchar, typestr, len + 1);
859         len += 1;
860     }
862     typestr[pos++] = '\0';
864     return typestr;
867 // Returns different message contexts depending on the current context. This function should only
868 // be called when ec is either a SPNodeContext or SPLPEToolContext, thus we return NULL in all
869 // other cases.
870 static Inkscape::MessageContext *
871 get_message_context(SPEventContext *ec)
873     Inkscape::MessageContext *mc = 0;
875     if (SP_IS_NODE_CONTEXT(ec)) {
876         mc = SP_NODE_CONTEXT(ec)->_node_message_context;
877     } else if (SP_IS_LPETOOL_CONTEXT(ec)) {
878         mc = SP_LPETOOL_CONTEXT(ec)->_lpetool_message_context;
879     } else {
880         g_warning ("Nodepath should only be present in Node tool or Geometric tool.");
881     }
883     return mc;
886 /**
887  \brief Fills node and handle positions for three nodes, splitting line
888   marked by end at distance t.
889  */
890 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
892     g_assert(new_path != NULL);
893     g_assert(end      != NULL);
895     g_assert(end->p.other == new_path);
896    Inkscape::NodePath::Node *start = new_path->p.other;
897     g_assert(start);
899     if (end->code == NR_LINETO) {
900         new_path->type =Inkscape::NodePath::NODE_CUSP;
901         new_path->code = NR_LINETO;
902         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
903     } else {
904         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
905         new_path->code = NR_CURVETO;
906         gdouble s      = 1 - t;
907         for (int dim = 0; dim < 2; dim++) {
908             Geom::Coord const f000 = start->pos[dim];
909             Geom::Coord const f001 = start->n.pos[dim];
910             Geom::Coord const f011 = end->p.pos[dim];
911             Geom::Coord const f111 = end->pos[dim];
912             Geom::Coord const f00t = s * f000 + t * f001;
913             Geom::Coord const f01t = s * f001 + t * f011;
914             Geom::Coord const f11t = s * f011 + t * f111;
915             Geom::Coord const f0tt = s * f00t + t * f01t;
916             Geom::Coord const f1tt = s * f01t + t * f11t;
917             Geom::Coord const fttt = s * f0tt + t * f1tt;
918             start->n.pos[dim]    = f00t;
919             new_path->p.pos[dim] = f0tt;
920             new_path->pos[dim]   = fttt;
921             new_path->n.pos[dim] = f1tt;
922             end->p.pos[dim]      = f11t;
923         }
924     }
927 /**
928  * Adds new node on direct line between two nodes, activates handles of all
929  * three nodes.
930  */
931 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
933     g_assert(end);
934     g_assert(end->subpath);
935     g_assert(g_list_find(end->subpath->nodes, end));
937    Inkscape::NodePath::Node *start = end->p.other;
938     g_assert( start->n.other == end );
939    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
940                                                end,
941                                                (NRPathcode)end->code == NR_LINETO?
942                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
943                                                (NRPathcode)end->code,
944                                                &start->pos, &start->pos, &start->n.pos);
945     sp_nodepath_line_midpoint(newnode, end, t);
947     sp_node_adjust_handles(start);
948     sp_node_update_handles(start);
949     sp_node_update_handles(newnode);
950     sp_node_adjust_handles(end);
951     sp_node_update_handles(end);
953     return newnode;
956 /**
957 \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
958 */
959 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
961     g_assert(node);
962     g_assert(node->subpath);
963     g_assert(g_list_find(node->subpath->nodes, node));
965     Inkscape::NodePath::Node* result = 0;
966     Inkscape::NodePath::SubPath *sp = node->subpath;
967     Inkscape::NodePath::Path *np    = sp->nodepath;
969     if (sp->closed) {
970         sp_nodepath_subpath_open(sp, node);
971         result = sp->first;
972     } else if ( (node == sp->first) || (node == sp->last ) ){
973         // no break for end nodes
974         result = 0;
975     } else {
976         // create a new subpath
977         Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
979         // duplicate the break node as start of the new subpath
980         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL,
981                                                                  static_cast<Inkscape::NodePath::NodeType>(node->type),
982                                                                  NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
984         // attach rest of curve to new node
985         g_assert(node->n.other);
986         newnode->n.other = node->n.other; node->n.other = NULL;
987         newnode->n.other->p.other = newnode;
988         newsubpath->last = sp->last;
989         sp->last = node;
990         node = newnode;
991         while (node->n.other) {
992             node = node->n.other;
993             node->subpath = newsubpath;
994             sp->nodes = g_list_remove(sp->nodes, node);
995             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
996         }
999         result = newnode;
1000     }
1001     return result;
1004 /**
1005  * Duplicate node and connect to neighbours.
1006  */
1007 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
1009     g_assert(node);
1010     g_assert(node->subpath);
1011     g_assert(g_list_find(node->subpath->nodes, node));
1013    Inkscape::NodePath::SubPath *sp = node->subpath;
1015     NRPathcode code = (NRPathcode) node->code;
1016     if (code == NR_MOVETO) { // if node is the endnode,
1017         node->code = NR_LINETO; // new one is inserted before it, so change that to line
1018     }
1020     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
1022     if (!node->n.other || !node->p.other) { // if node is an endnode, select it
1023         return node;
1024     } else {
1025         return newnode; // otherwise select the newly created node
1026     }
1029 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
1031     node->p.pos = (node->pos + (node->pos - node->n.pos));
1034 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
1036     node->n.pos = (node->pos + (node->pos - node->p.pos));
1039 /**
1040  * Change line type at node, with side effects on neighbours.
1041  */
1042 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
1044     g_assert(end);
1045     g_assert(end->subpath);
1046     g_assert(end->p.other);
1048     if (end->code != static_cast<guint>(code) ) {
1049         Inkscape::NodePath::Node *start = end->p.other;
1051         end->code = code;
1053         if (code == NR_LINETO) {
1054             if (start->code == NR_LINETO) {
1055                 sp_nodepath_set_node_type(start, Inkscape::NodePath::NODE_CUSP);
1056             }
1057             if (end->n.other) {
1058                 if (end->n.other->code == NR_LINETO) {
1059                     sp_nodepath_set_node_type(end, Inkscape::NodePath::NODE_CUSP);
1060                 }
1061             }
1063             if (start->type == Inkscape::NodePath::NODE_AUTO)
1064                 start->type = Inkscape::NodePath::NODE_SMOOTH;
1065             if (end->type == Inkscape::NodePath::NODE_AUTO)
1066                 end->type = Inkscape::NodePath::NODE_SMOOTH;
1067     
1068             sp_node_adjust_handle(start, -1);
1069             sp_node_adjust_handle(end, 1);
1071         } else {
1072             Geom::Point delta = end->pos - start->pos;
1073             start->n.pos = start->pos + delta / 3;
1074             end->p.pos = end->pos - delta / 3;
1075             sp_node_adjust_handle(start, 1);
1076             sp_node_adjust_handle(end, -1);
1077         }
1079         sp_node_update_handles(start);
1080         sp_node_update_handles(end);
1081     }
1084 static void
1085 sp_nodepath_update_node_knot(Inkscape::NodePath::Node *node)
1087     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1088         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1089         node->knot->setSize (node->selected? 11 : 9);
1090         sp_knot_update_ctrl(node->knot);
1091     } else if (node->type == Inkscape::NodePath::NODE_AUTO) {
1092         node->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1093         node->knot->setSize (node->selected? 11 : 9);
1094         sp_knot_update_ctrl(node->knot);
1095     } else {
1096         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1097         node->knot->setSize (node->selected? 9 : 7);
1098         sp_knot_update_ctrl(node->knot);
1099     }
1103 /**
1104  * Change node type, and its handles accordingly.
1105  */
1106 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1108     g_assert(node);
1109     g_assert(node->subpath);
1111     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1112         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1113             type =Inkscape::NodePath::NODE_CUSP;
1114         }
1115     }
1117     node->type = type;
1119     sp_nodepath_update_node_knot(node);
1121     // if one of handles is mouseovered, preserve its position
1122     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1123         sp_node_adjust_handle(node, 1);
1124     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1125         sp_node_adjust_handle(node, -1);
1126     } else {
1127         sp_node_adjust_handles(node);
1128     }
1130     sp_node_update_handles(node);
1132     sp_nodepath_update_statusbar(node->subpath->nodepath);
1134     return node;
1137 bool
1138 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1140 // TODO clean up multiple returns
1141         Inkscape::NodePath::Node *othernode = side->other;
1142         if (!othernode)
1143             return false;
1144         NRPathcode const code = sp_node_path_code_from_side(node, side);
1145         if (code == NR_LINETO)
1146             return true;
1147         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1148         if (&node->p == side) {
1149             other_to_me = &othernode->n;
1150         } else if (&node->n == side) {
1151             other_to_me = &othernode->p;
1152         } 
1153         if (!other_to_me)
1154             return false;
1155         bool is_line = 
1156              (Geom::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1157               Geom::L2(node->pos - side->pos) < 1e-6);
1158         return is_line;
1161 /**
1162  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1163  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1164  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1165  * If already cusp and set to cusp, retracts handles.
1166 */
1167 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1169     if (type == Inkscape::NodePath::NODE_AUTO) {
1170         if (node->p.other != NULL)
1171             node->code = NR_CURVETO;
1172         if (node->n.other != NULL)
1173             node->n.other->code = NR_CURVETO;
1174     }
1176     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1178 /* 
1179   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1180  
1181         if (two_handles) {
1182             // do nothing, adjust_handles called via set_node_type will line them up
1183         } else if (one_handle) {
1184             if (opposite_to_handle_is_line) {
1185                 if (lined_up) {
1186                     // already half-smooth; pull opposite handle too making it fully smooth
1187                 } else {
1188                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1189                 }
1190             } else {
1191                 // pull opposite handle in line with the existing one
1192             }
1193         } else if (no_handles) {
1194             if (both_segments_are_lines OR both_segments_are_curves) {
1195                 //pull both handles
1196             } else {
1197                 // pull the handle opposite to line segment, making node half-smooth
1198             }
1199         }
1200 */
1201         bool p_has_handle = (Geom::L2(node->pos  - node->p.pos) > 1e-6);
1202         bool n_has_handle = (Geom::L2(node->pos  - node->n.pos) > 1e-6);
1203         bool p_is_line = sp_node_side_is_line(node, &node->p);
1204         bool n_is_line = sp_node_side_is_line(node, &node->n);
1206         if (p_has_handle && n_has_handle) {
1207             // do nothing, adjust_handles will line them up
1208         } else if (p_has_handle || n_has_handle) {
1209             if (p_has_handle && n_is_line) {
1210                 Radial line (node->n.other->pos - node->pos);
1211                 Radial handle (node->pos - node->p.pos);
1212                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1213                     // already half-smooth; pull opposite handle too making it fully smooth
1214                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1215                 } else {
1216                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1217                 }
1218             } else if (n_has_handle && p_is_line) {
1219                 Radial line (node->p.other->pos - node->pos);
1220                 Radial handle (node->pos - node->n.pos);
1221                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1222                     // already half-smooth; pull opposite handle too making it fully smooth
1223                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1224                 } else {
1225                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1226                 }
1227             } else if (p_has_handle && node->n.other) {
1228                 // pull n handle
1229                 node->n.other->code = NR_CURVETO;
1230                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1231                     Geom::L2(node->p.pos - node->pos) :
1232                     Geom::L2(node->n.other->pos - node->pos) / 3;
1233                 node->n.pos = node->pos - (len / Geom::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1234             } else if (n_has_handle && node->p.other) {
1235                 // pull p handle
1236                 node->code = NR_CURVETO;
1237                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1238                     Geom::L2(node->n.pos - node->pos) :
1239                     Geom::L2(node->p.other->pos - node->pos) / 3;
1240                 node->p.pos = node->pos - (len / Geom::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1241             }
1242         } else if (!p_has_handle && !n_has_handle) {
1243             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1244                 // no handles, but both segments are either lnes or curves:
1245                 //pull both handles
1247                 // convert both to curves:
1248                 node->code = NR_CURVETO;
1249                 node->n.other->code = NR_CURVETO;
1251                 sp_node_adjust_handles_auto(node);
1252             } else {
1253                 // pull the handle opposite to line segment, making it half-smooth
1254                 if (p_is_line && node->n.other) {
1255                     if (type != Inkscape::NodePath::NODE_SYMM) {
1256                         // pull n handle
1257                         node->n.other->code = NR_CURVETO;
1258                         double len =  Geom::L2(node->n.other->pos - node->pos) / 3;
1259                         node->n.pos = node->pos + (len / Geom::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1260                     }
1261                 } else if (n_is_line && node->p.other) {
1262                     if (type != Inkscape::NodePath::NODE_SYMM) {
1263                         // pull p handle
1264                         node->code = NR_CURVETO;
1265                         double len =  Geom::L2(node->p.other->pos - node->pos) / 3;
1266                         node->p.pos = node->pos + (len / Geom::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1267                     }
1268                 }
1269             }
1270         }
1271     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1272         // cusping a cusp: retract nodes
1273         node->p.pos = node->pos;
1274         node->n.pos = node->pos;
1275     }
1277     sp_nodepath_set_node_type (node, type);
1280 /**
1281  * Move node to point, and adjust its and neighbouring handles.
1282  */
1283 void sp_node_moveto(Inkscape::NodePath::Node *node, Geom::Point p)
1285     if (node->type == Inkscape::NodePath::NODE_AUTO) {
1286         node->pos = p;
1287         sp_node_adjust_handles_auto(node);
1288     } else {
1289         Geom::Point delta = p - node->pos;
1290         node->pos = p;
1292         node->p.pos += delta;
1293         node->n.pos += delta;
1294     }
1296     Inkscape::NodePath::Node *node_p = NULL;
1297     Inkscape::NodePath::Node *node_n = NULL;
1299     if (node->p.other) {
1300         if (node->code == NR_LINETO) {
1301             sp_node_adjust_handle(node, 1);
1302             sp_node_adjust_handle(node->p.other, -1);
1303             node_p = node->p.other;
1304         }
1305         if (!node->p.other->selected && node->p.other->type == Inkscape::NodePath::NODE_AUTO) {
1306             sp_node_adjust_handles_auto(node->p.other);
1307             node_p = node->p.other;
1308         }
1309     }
1310     if (node->n.other) {
1311         if (node->n.other->code == NR_LINETO) {
1312             sp_node_adjust_handle(node, -1);
1313             sp_node_adjust_handle(node->n.other, 1);
1314             node_n = node->n.other;
1315         }
1316         if (!node->n.other->selected && node->n.other->type == Inkscape::NodePath::NODE_AUTO) {
1317             sp_node_adjust_handles_auto(node->n.other);
1318             node_n = node->n.other;
1319         }
1320     }
1322     // this function is only called from batch movers that will update display at the end
1323     // themselves, so here we just move all the knots without emitting move signals, for speed
1324     sp_node_update_handles(node, false);
1325     if (node_n) {
1326         sp_node_update_handles(node_n, false);
1327     }
1328     if (node_p) {
1329         sp_node_update_handles(node_p, false);
1330     }
1333 /**
1334  * Call sp_node_moveto() for node selection and handle possible snapping.
1335  */
1336 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, Geom::Coord dx, Geom::Coord dy,
1337                                             bool const snap, bool constrained = false, 
1338                                             Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point())
1340     Geom::Point delta(dx, dy);
1341     Geom::Point best_pt = delta;
1342     Inkscape::SnappedPoint best;
1343     
1344     if (snap) {    
1345         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1346          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1347          * must provide that information. */
1348           
1349         // Build a list of the unselected nodes to which the snapper should snap 
1350         std::vector<Geom::Point> unselected_nodes;
1351         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1352             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1353             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1354                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1355                 if (!node->selected) {
1356                     unselected_nodes.push_back(to_2geom(node->pos));
1357                 }    
1358             }
1359         }        
1360         
1361         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1362         
1363         // When only the node closest to the mouse pointer is to be snapped 
1364         // then we will not even try to snap to other points and discard those immediately
1365         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1366         bool closest_only = prefs->getBool("/options/snapclosestonly/value", false); 
1367         
1368         Inkscape::NodePath::Node *closest_node = NULL;
1369         Geom::Coord closest_dist = NR_HUGE;
1370         
1371         if (closest_only) {
1372                 for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1373                         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1374                         Geom::Coord dist = Geom::L2(nodepath->drag_origin_mouse - n->origin);
1375                         if (dist < closest_dist) {
1376                                 closest_node = n;
1377                                 closest_dist = dist;
1378                         }
1379                 }                       
1380         }
1381         
1382         // Iterate through all selected nodes
1383         m.setup(nodepath->desktop, false, SP_PATH(nodepath->item), &unselected_nodes);
1384         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1385             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1386             if (!closest_only || n == closest_node) { //try to snap either all selected nodes or only the closest one
1387                     Inkscape::SnappedPoint s;            
1388                     if (constrained) {
1389                         Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1390                         dedicated_constraint.setPoint(n->pos);
1391                         s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), dedicated_constraint);
1392                     } else {
1393                         s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta));
1394                     }            
1395                     
1396                     if (s.getSnapped()) {
1397                         s.setPointerDistance(Geom::L2(nodepath->drag_origin_mouse - n->origin));                            
1398                         if (!s.isOtherSnapBetter(best, true)) {
1399                                 best = s;
1400                                 best_pt = from_2geom(s.getPoint()) - n->pos;
1401                         }
1402                     }
1403             }
1404         }
1405                         
1406         if (best.getSnapped()) {
1407             nodepath->desktop->snapindicator->set_new_snappoint(best);
1408         } else {
1409             nodepath->desktop->snapindicator->remove_snappoint();    
1410         }
1411     }
1413     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1414         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1415         sp_node_moveto(n, n->pos + best_pt);
1416     }
1418     // do not update repr here so that node dragging is acceptably fast
1419     update_object(nodepath);
1422 /**
1423 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1424 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1425 near x = 0.
1426  */
1427 double
1428 sculpt_profile (double x, double alpha, guint profile)
1430     double result = 1;
1432     if (x >= 1) {
1433         result = 0;
1434     } else if (x <= 0) {
1435         result = 1;
1436     } else {
1437         switch (profile) {
1438             case SCULPT_PROFILE_LINEAR:
1439                 result = 1 - x;
1440                 break;
1441             case SCULPT_PROFILE_BELL:
1442                 result = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1443                 break;
1444             case SCULPT_PROFILE_ELLIPTIC:
1445                 result = sqrt(1 - x*x);
1446                 break;
1447             default:
1448                 g_assert_not_reached();
1449         }
1450     }
1452     return result;
1455 double
1456 bezier_length (Geom::Point a, Geom::Point ah, Geom::Point bh, Geom::Point b)
1458     // extremely primitive for now, don't have time to look for the real one
1459     double lower = Geom::L2(b - a);
1460     double upper = Geom::L2(ah - a) + Geom::L2(bh - ah) + Geom::L2(bh - b);
1461     return (lower + upper)/2;
1464 void
1465 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, Geom::Point delta, Geom::Point delta_n, Geom::Point delta_p)
1467     n->pos = n->origin + delta;
1468     n->n.pos = n->n.origin + delta_n;
1469     n->p.pos = n->p.origin + delta_p;
1470     sp_node_adjust_handles(n);
1471     sp_node_update_handles(n, false);
1474 /**
1475  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1476  * on how far they are from the dragged node n.
1477  */
1478 static void
1479 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, Geom::Point delta)
1481     g_assert (n);
1482     g_assert (nodepath);
1483     g_assert (n->subpath->nodepath == nodepath);
1485     double pressure = n->knot->pressure;
1486     if (pressure == 0)
1487         pressure = 0.5; // default
1488     pressure = CLAMP (pressure, 0.2, 0.8);
1490     // map pressure to alpha = 1/5 ... 5
1491     double alpha = 1 - 2 * fabs(pressure - 0.5);
1492     if (pressure > 0.5)
1493         alpha = 1/alpha;
1495     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1496     guint profile = prefs->getInt("/tools/nodes/sculpting_profile", SCULPT_PROFILE_BELL);
1498     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1499         // Only one subpath has selected nodes:
1500         // use linear mode, where the distance from n to node being dragged is calculated along the path
1502         double n_sel_range = 0, p_sel_range = 0;
1503         guint n_nodes = 0, p_nodes = 0;
1504         guint n_sel_nodes = 0, p_sel_nodes = 0;
1506         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1507         {
1508             double n_range = 0, p_range = 0;
1509             bool n_going = true, p_going = true;
1510             Inkscape::NodePath::Node *n_node = n;
1511             Inkscape::NodePath::Node *p_node = n;
1512             do {
1513                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1514                 if (n_node && n_going)
1515                     n_node = n_node->n.other;
1516                 if (n_node == NULL) {
1517                     n_going = false;
1518                 } else {
1519                     n_nodes ++;
1520                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1521                     if (n_node->selected) {
1522                         n_sel_nodes ++;
1523                         n_sel_range = n_range;
1524                     }
1525                     if (n_node == p_node) {
1526                         n_going = false;
1527                         p_going = false;
1528                     }
1529                 }
1530                 if (p_node && p_going)
1531                     p_node = p_node->p.other;
1532                 if (p_node == NULL) {
1533                     p_going = false;
1534                 } else {
1535                     p_nodes ++;
1536                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1537                     if (p_node->selected) {
1538                         p_sel_nodes ++;
1539                         p_sel_range = p_range;
1540                     }
1541                     if (p_node == n_node) {
1542                         n_going = false;
1543                         p_going = false;
1544                     }
1545                 }
1546             } while (n_going || p_going);
1547         }
1549         // Second pass: actually move nodes in this subpath
1550         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1551         {
1552             double n_range = 0, p_range = 0;
1553             bool n_going = true, p_going = true;
1554             Inkscape::NodePath::Node *n_node = n;
1555             Inkscape::NodePath::Node *p_node = n;
1556             do {
1557                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1558                 if (n_node && n_going)
1559                     n_node = n_node->n.other;
1560                 if (n_node == NULL) {
1561                     n_going = false;
1562                 } else {
1563                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1564                     if (n_node->selected) {
1565                         sp_nodepath_move_node_and_handles (n_node,
1566                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1567                                                            sculpt_profile ((n_range + Geom::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1568                                                            sculpt_profile ((n_range - Geom::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1569                     }
1570                     if (n_node == p_node) {
1571                         n_going = false;
1572                         p_going = false;
1573                     }
1574                 }
1575                 if (p_node && p_going)
1576                     p_node = p_node->p.other;
1577                 if (p_node == NULL) {
1578                     p_going = false;
1579                 } else {
1580                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1581                     if (p_node->selected) {
1582                         sp_nodepath_move_node_and_handles (p_node,
1583                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1584                                                            sculpt_profile ((p_range - Geom::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1585                                                            sculpt_profile ((p_range + Geom::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1586                     }
1587                     if (p_node == n_node) {
1588                         n_going = false;
1589                         p_going = false;
1590                     }
1591                 }
1592             } while (n_going || p_going);
1593         }
1595     } else {
1596         // Multiple subpaths have selected nodes:
1597         // use spatial mode, where the distance from n to node being dragged is measured directly as Geom::L2.
1598         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1599         // fix the pear-like shape when sculpting e.g. a ring
1601         // First pass: calculate range
1602         gdouble direct_range = 0;
1603         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1604             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1605             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1606                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1607                 if (node->selected) {
1608                     direct_range = MAX(direct_range, Geom::L2(node->origin - n->origin));
1609                 }
1610             }
1611         }
1613         // Second pass: actually move nodes
1614         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1615             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1616             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1617                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1618                 if (node->selected) {
1619                     if (direct_range > 1e-6) {
1620                         sp_nodepath_move_node_and_handles (node,
1621                                                        sculpt_profile (Geom::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1622                                                        sculpt_profile (Geom::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1623                                                        sculpt_profile (Geom::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1624                     } else {
1625                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1626                     }
1628                 }
1629             }
1630         }
1631     }
1633     // do not update repr here so that node dragging is acceptably fast
1634     update_object(nodepath);
1638 /**
1639  * Move node selection to point, adjust its and neighbouring handles,
1640  * handle possible snapping, and commit the change with possible undo.
1641  */
1642 void
1643 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1645     if (!nodepath) return;
1647     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1649     if (dx == 0) {
1650         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1651     } else if (dy == 0) {
1652         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1653     } else {
1654         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1655     }
1658 /**
1659  * Move node selection off screen and commit the change.
1660  */
1661 void
1662 sp_node_selected_move_screen(SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1664     // borrowed from sp_selection_move_screen in selection-chemistry.c
1665     // we find out the current zoom factor and divide deltas by it
1667     gdouble zoom = desktop->current_zoom();
1668     gdouble zdx = dx / zoom;
1669     gdouble zdy = dy / zoom;
1671     if (!nodepath) return;
1673     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1675     if (dx == 0) {
1676         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1677     } else if (dy == 0) {
1678         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1679     } else {
1680         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1681     }
1684 /**
1685  * Move selected nodes to the absolute position given
1686  */
1687 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis)
1689     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1690         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1691         Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]);
1692         sp_node_moveto(n, npos);
1693     }
1695     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1698 /**
1699  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return Geom::Nothing
1700  */
1701 boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1703     boost::optional<Geom::Coord> no_coord;
1704     g_return_val_if_fail(nodepath->selected, no_coord);
1706     // determine coordinate of first selected node
1707     GList *nsel = nodepath->selected;
1708     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1709     Geom::Coord coord = n->pos[axis];
1710     bool coincide = true;
1712     // compare it to the coordinates of all the other selected nodes
1713     for (GList *l = nsel->next; l != NULL; l = l->next) {
1714         n = (Inkscape::NodePath::Node *) l->data;
1715         if (n->pos[axis] != coord) {
1716             coincide = false;
1717         }
1718     }
1719     if (coincide) {
1720         return coord;
1721     } else {
1722         Geom::Rect bbox = sp_node_selected_bbox(nodepath);
1723         // currently we return the coordinate of the bounding box midpoint because I don't know how
1724         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1725         return bbox.midpoint()[axis];
1726     }
1729 /** If they don't yet exist, creates knot and line for the given side of the node */
1730 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1732     if (!side->knot) {
1733         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"));
1735         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1736         side->knot->setSize (7);
1737         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1738         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1739         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1740         sp_knot_update_ctrl(side->knot);
1742         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1743         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1744         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1745         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1746         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1747         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1748     }
1750     if (!side->line) {
1751         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1752                                         SP_TYPE_CTRLLINE, NULL);
1753     }
1756 /**
1757  * Ensure the given handle of the node is visible/invisible, update its screen position
1758  */
1759 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1761     g_assert(node != NULL);
1763    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1764     NRPathcode code = sp_node_path_code_from_side(node, side);
1766     show_handle = show_handle && (code == NR_CURVETO) && (Geom::L2(side->pos - node->pos) > 1e-6);
1768     if (show_handle) {
1769         if (!side->knot) { // No handle knot at all
1770             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1771             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1772             side->knot->pos = side->pos;
1773             if (side->knot->item)
1774                 SP_CTRL(side->knot->item)->moveto(side->pos);
1775             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1776             sp_knot_show(side->knot);
1777         } else {
1778             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1779                 if (fire_move_signals) {
1780                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1781                 } else {
1782                     sp_knot_moveto(side->knot, side->pos);
1783                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1784                 }
1785             }
1786             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1787                 sp_knot_show(side->knot);
1788             }
1789         }
1790         sp_canvas_item_show(side->line);
1791     } else {
1792         if (side->knot) {
1793             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1794                 sp_knot_hide(side->knot);
1795             }
1796         }
1797         if (side->line) {
1798             sp_canvas_item_hide(side->line);
1799         }
1800     }
1803 /**
1804  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1805  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1806  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1807  * updated; otherwise, just move the knots silently (used in batch moves).
1808  */
1809 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1811     g_assert(node != NULL);
1813     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1814         sp_knot_show(node->knot);
1815     }
1817     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1818         if (fire_move_signals)
1819             sp_knot_set_position(node->knot, node->pos, 0);
1820         else
1821             sp_knot_moveto(node->knot, node->pos);
1822     }
1824     gboolean show_handles = node->selected;
1825     if (node->p.other != NULL) {
1826         if (node->p.other->selected) show_handles = TRUE;
1827     }
1828     if (node->n.other != NULL) {
1829         if (node->n.other->selected) show_handles = TRUE;
1830     }
1832     if (node->subpath->nodepath->show_handles == false)
1833         show_handles = FALSE;
1835     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1836     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1839 /**
1840  * Call sp_node_update_handles() for all nodes on subpath.
1841  */
1842 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1844     g_assert(subpath != NULL);
1846     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1847         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1848     }
1851 /**
1852  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1853  */
1854 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1856     g_assert(nodepath != NULL);
1858     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1859         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1860     }
1863 void
1864 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1866     if (nodepath) {
1867         nodepath->show_handles = show;
1868         sp_nodepath_update_handles(nodepath);
1869     }
1872 /**
1873  * Adds all selected nodes in nodepath to list.
1874  */
1875 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1877     StlConv<Node *>::list(l, selected);
1878 /// \todo this adds a copying, rework when the selection becomes a stl list
1881 /**
1882  * Align selected nodes on the specified axis.
1883  */
1884 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1886     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1887         return;
1888     }
1890     if ( !nodepath->selected->next ) { // only one node selected
1891         return;
1892     }
1893    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1894     Geom::Point dest(pNode->pos);
1895     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1896         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1897         if (pNode) {
1898             dest[axis] = pNode->pos[axis];
1899             sp_node_moveto(pNode, dest);
1900         }
1901     }
1903     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1906 /// Helper struct.
1907 struct NodeSort
1909    Inkscape::NodePath::Node *_node;
1910     Geom::Coord _coord;
1911     /// \todo use vectorof pointers instead of calling copy ctor
1912     NodeSort(Inkscape::NodePath::Node *node, Geom::Dim2 axis) :
1913         _node(node), _coord(node->pos[axis])
1914     {}
1916 };
1918 static bool operator<(NodeSort const &a, NodeSort const &b)
1920     return (a._coord < b._coord);
1923 /**
1924  * Distribute selected nodes on the specified axis.
1925  */
1926 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1928     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1929         return;
1930     }
1932     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1933         return;
1934     }
1936    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1937     std::vector<NodeSort> sorted;
1938     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1939         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1940         if (pNode) {
1941             NodeSort n(pNode, axis);
1942             sorted.push_back(n);
1943             //dest[axis] = pNode->pos[axis];
1944             //sp_node_moveto(pNode, dest);
1945         }
1946     }
1947     std::sort(sorted.begin(), sorted.end());
1948     unsigned int len = sorted.size();
1949     //overall bboxes span
1950     float dist = (sorted.back()._coord -
1951                   sorted.front()._coord);
1952     //new distance between each bbox
1953     float step = (dist) / (len - 1);
1954     float pos = sorted.front()._coord;
1955     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1956           it < sorted.end();
1957           it ++ )
1958     {
1959         Geom::Point dest((*it)._node->pos);
1960         dest[axis] = pos;
1961         sp_node_moveto((*it)._node, dest);
1962         pos += step;
1963     }
1965     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1969 /**
1970  * Call sp_nodepath_line_add_node() for all selected segments.
1971  */
1972 void
1973 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1975     if (!nodepath) {
1976         return;
1977     }
1979     GList *nl = NULL;
1981     int n_added = 0;
1983     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1984        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1985         g_assert(t->selected);
1986         if (t->p.other && t->p.other->selected) {
1987             nl = g_list_prepend(nl, t);
1988         }
1989     }
1991     while (nl) {
1992        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1993        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1994        sp_nodepath_node_select(n, TRUE, FALSE);
1995        n_added ++;
1996        nl = g_list_remove(nl, t);
1997     }
1999     /** \todo fixme: adjust ? */
2000     sp_nodepath_update_handles(nodepath);
2002     if (n_added > 1) {
2003         sp_nodepath_update_repr(nodepath, _("Add nodes"));
2004     } else if (n_added > 0) {
2005         sp_nodepath_update_repr(nodepath, _("Add node"));
2006     }
2008     sp_nodepath_update_statusbar(nodepath);
2011 /**
2012  * Select segment nearest to point
2013  */
2014 void
2015 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p, bool toggle)
2017     if (!nodepath) {
2018         return;
2019     }
2021     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2022     Geom::PathVector const &pathv = curve->get_pathvector();
2023     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2024     if (!pvpos) {
2025         g_print ("Possible error?\n");
2026         return;
2027     }
2029     // calculate index for nodepath's representation.
2030     unsigned int segment_index = floor(pvpos->t) + 1;
2031     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2032         segment_index += pathv[i].size() + 1;
2033         if (pathv[i].closed()) {
2034             segment_index += 1;
2035         }
2036     }
2038     curve->unref();
2040     //find segment to segment
2041     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2043     //fixme: this can return NULL, so check before proceeding.
2044     g_return_if_fail(e != NULL);
2046     gboolean force = FALSE;
2047     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
2048         force = TRUE;
2049     }
2050     sp_nodepath_node_select(e, (gboolean) toggle, force);
2051     if (e->p.other)
2052         sp_nodepath_node_select(e->p.other, TRUE, force);
2054     sp_nodepath_update_handles(nodepath);
2056     sp_nodepath_update_statusbar(nodepath);
2059 /**
2060  * Add a node nearest to point
2061  */
2062 void
2063 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p)
2065     if (!nodepath) {
2066         return;
2067     }
2069     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2070     Geom::PathVector const &pathv = curve->get_pathvector();
2071     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2072     if (!pvpos) {
2073         g_print ("Possible error?\n");
2074         return;
2075     }
2077     // calculate index for nodepath's representation.
2078     double int_part;
2079     double t = std::modf(pvpos->t, &int_part);
2080     unsigned int segment_index = (unsigned int)int_part + 1;
2081     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2082         segment_index += pathv[i].size() + 1;
2083         if (pathv[i].closed()) {
2084             segment_index += 1;
2085         }
2086     }
2088     curve->unref();
2090     //find segment to split
2091     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2092     if (!e) {
2093         return;
2094     }
2096     //don't know why but t seems to flip for lines
2097     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2098         t = 1.0 - t;
2099     }
2101     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2102     sp_nodepath_node_select(n, FALSE, TRUE);
2104     /* fixme: adjust ? */
2105     sp_nodepath_update_handles(nodepath);
2107     sp_nodepath_update_repr(nodepath, _("Add node"));
2109     sp_nodepath_update_statusbar(nodepath);
2112 /*
2113  * Adjusts a segment so that t moves by a certain delta for dragging
2114  * converts lines to curves
2115  *
2116  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2117  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2118  */
2119 void
2120 sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, Geom::Point delta)
2122     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node);
2124     //fixme: e and e->p can be NULL, so check for those before proceeding
2125     g_return_if_fail(e != NULL);
2126     g_return_if_fail(&e->p != NULL);
2128     if (e->type == Inkscape::NodePath::NODE_AUTO) {
2129         e->type = Inkscape::NodePath::NODE_SMOOTH;
2130         sp_nodepath_update_node_knot (e);
2131     }
2132     if (e->p.other->type == Inkscape::NodePath::NODE_AUTO) {
2133         e->p.other->type = Inkscape::NodePath::NODE_SMOOTH;
2134         sp_nodepath_update_node_knot (e->p.other);
2135     }
2137     /* feel good is an arbitrary parameter that distributes the delta between handles
2138      * if t of the drag point is less than 1/6 distance form the endpoint only
2139      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2140      */
2141     double feel_good;
2142     if (t <= 1.0 / 6.0)
2143         feel_good = 0;
2144     else if (t <= 0.5)
2145         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2146     else if (t <= 5.0 / 6.0)
2147         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2148     else
2149         feel_good = 1;
2151     //if we're dragging a line convert it to a curve
2152     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2153         sp_nodepath_set_line_type(e, NR_CURVETO);
2154     }
2156     Geom::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2157     Geom::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2158     e->p.other->n.pos += offsetcoord0;
2159     e->p.pos += offsetcoord1;
2161     // adjust handles of adjacent nodes where necessary
2162     sp_node_adjust_handle(e,1);
2163     sp_node_adjust_handle(e->p.other,-1);
2165     sp_nodepath_update_handles(e->subpath->nodepath);
2167     update_object(e->subpath->nodepath);
2169     sp_nodepath_update_statusbar(e->subpath->nodepath);
2173 /**
2174  * Call sp_nodepath_break() for all selected segments.
2175  */
2176 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2178     if (!nodepath) return;
2180     GList *tempin = g_list_copy(nodepath->selected);
2181     GList *temp = NULL;
2182     for (GList *l = tempin; l != NULL; l = l->next) {
2183        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2184        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2185         if (nn == NULL) continue; // no break, no new node
2186         temp = g_list_prepend(temp, nn);
2187     }
2188     g_list_free(tempin);
2190     if (temp) {
2191         sp_nodepath_deselect(nodepath);
2192     }
2193     for (GList *l = temp; l != NULL; l = l->next) {
2194         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2195     }
2197     sp_nodepath_update_handles(nodepath);
2199     sp_nodepath_update_repr(nodepath, _("Break path"));
2202 /**
2203  * Duplicate the selected node(s).
2204  */
2205 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2207     if (!nodepath) {
2208         return;
2209     }
2211     GList *temp = NULL;
2212     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2213        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2214        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2215         if (nn == NULL) continue; // could not duplicate
2216         temp = g_list_prepend(temp, nn);
2217     }
2219     if (temp) {
2220         sp_nodepath_deselect(nodepath);
2221     }
2222     for (GList *l = temp; l != NULL; l = l->next) {
2223         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2224     }
2226     sp_nodepath_update_handles(nodepath);
2228     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2231 /**
2232  *  Internal function to join two nodes by merging them into one.
2233  */
2234 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2236     /* a and b are endpoints */
2238     // if one of the two nodes is mouseovered, fix its position
2239     Geom::Point c;
2240     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2241         c = a->pos;
2242     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2243         c = b->pos;
2244     } else {
2245         // otherwise, move joined node to the midpoint
2246         c = (a->pos + b->pos) / 2;
2247     }
2249     if (a->subpath == b->subpath) {
2250        Inkscape::NodePath::SubPath *sp = a->subpath;
2251         sp_nodepath_subpath_close(sp);
2252         sp_node_moveto (sp->first, c);
2254         sp_nodepath_update_handles(sp->nodepath);
2255         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2256         return;
2257     }
2259     /* a and b are separate subpaths */
2260     Inkscape::NodePath::SubPath *sa = a->subpath;
2261     Inkscape::NodePath::SubPath *sb = b->subpath;
2262     Geom::Point p;
2263     Inkscape::NodePath::Node *n;
2264     NRPathcode code;
2265     if (a == sa->first) {
2266         // we will now reverse sa, so that a is its last node, not first, and drop that node
2267         p = sa->first->n.pos;
2268         code = (NRPathcode)sa->first->n.other->code;
2269         // create new subpath
2270        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2271        // create a first moveto node on it
2272         n = sa->last;
2273         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2274         n = n->p.other;
2275         if (n == sa->first) n = NULL;
2276         while (n) {
2277             // copy the rest of the nodes from sa to t, going backwards
2278             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2279             n = n->p.other;
2280             if (n == sa->first) n = NULL;
2281         }
2282         // replace sa with t
2283         sp_nodepath_subpath_destroy(sa);
2284         sa = t;
2285     } else if (a == sa->last) {
2286         // a is already last, just drop it
2287         p = sa->last->p.pos;
2288         code = (NRPathcode)sa->last->code;
2289         sp_nodepath_node_destroy(sa->last);
2290     } else {
2291         code = NR_END;
2292         g_assert_not_reached();
2293     }
2295     if (b == sb->first) {
2296         // copy all nodes from b to a, forward 
2297         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2298         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2299             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2300         }
2301     } else if (b == sb->last) {
2302         // copy all nodes from b to a, backward 
2303         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2304         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2305             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2306         }
2307     } else {
2308         g_assert_not_reached();
2309     }
2310     /* and now destroy sb */
2312     sp_nodepath_subpath_destroy(sb);
2314     sp_nodepath_update_handles(sa->nodepath);
2316     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2318     sp_nodepath_update_statusbar(nodepath);
2321 /**
2322  *  Internal function to join two nodes by adding a segment between them.
2323  */
2324 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2326     if (a->subpath == b->subpath) {
2327        Inkscape::NodePath::SubPath *sp = a->subpath;
2329         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2330         sp->closed = TRUE;
2332         sp->first->p.other = sp->last;
2333         sp->last->n.other  = sp->first;
2335         sp_node_handle_mirror_p_to_n(sp->last);
2336         sp_node_handle_mirror_n_to_p(sp->first);
2338         sp->first->code = sp->last->code;
2339         sp->first       = sp->last;
2341         sp_nodepath_update_handles(sp->nodepath);
2343         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2345         return;
2346     }
2348     /* a and b are separate subpaths */
2349     Inkscape::NodePath::SubPath *sa = a->subpath;
2350     Inkscape::NodePath::SubPath *sb = b->subpath;
2352     Inkscape::NodePath::Node *n;
2353     Geom::Point p;
2354     NRPathcode code;
2355     if (a == sa->first) {
2356         code = (NRPathcode) sa->first->n.other->code;
2357        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2358         n = sa->last;
2359         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2360         for (n = n->p.other; n != NULL; n = n->p.other) {
2361             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2362         }
2363         sp_nodepath_subpath_destroy(sa);
2364         sa = t;
2365     } else if (a == sa->last) {
2366         code = (NRPathcode)sa->last->code;
2367     } else {
2368         code = NR_END;
2369         g_assert_not_reached();
2370     }
2372     if (b == sb->first) {
2373         n = sb->first;
2374         sp_node_handle_mirror_p_to_n(sa->last);
2375         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2376         sp_node_handle_mirror_n_to_p(sa->last);
2377         for (n = n->n.other; n != NULL; n = n->n.other) {
2378             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2379         }
2380     } else if (b == sb->last) {
2381         n = sb->last;
2382         sp_node_handle_mirror_p_to_n(sa->last);
2383         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2384         sp_node_handle_mirror_n_to_p(sa->last);
2385         for (n = n->p.other; n != NULL; n = n->p.other) {
2386             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2387         }
2388     } else {
2389         g_assert_not_reached();
2390     }
2391     /* and now destroy sb */
2393     sp_nodepath_subpath_destroy(sb);
2395     sp_nodepath_update_handles(sa->nodepath);
2397     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2400 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2402 /**
2403  * Internal function to handle joining two nodes.
2404  */
2405 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2407     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2409     if (g_list_length(nodepath->selected) != 2) {
2410         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2411         return;
2412     }
2414     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2415     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2417     g_assert(a != b);
2418     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2419         // someone tried to join an orphan node (i.e. a single-node subpath).
2420         // this is not worth an error message, just fail silently.
2421         return;
2422     }
2424     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2425         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2426         return;
2427     }
2429     switch(mode) {
2430         case NODE_JOIN_ENDPOINTS:
2431             do_node_selected_join(nodepath, a, b);
2432             break;
2433         case NODE_JOIN_SEGMENT:
2434             do_node_selected_join_segment(nodepath, a, b);
2435             break;
2436     }
2439 /**
2440  *  Join two nodes by merging them into one.
2441  */
2442 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2444     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2447 /**
2448  *  Join two nodes by adding a segment between them.
2449  */
2450 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2452     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2455 /**
2456  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2457  */
2458 void sp_node_delete_preserve(GList *nodes_to_delete)
2460     GSList *nodepaths = NULL;
2462     while (nodes_to_delete) {
2463         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2464         Inkscape::NodePath::SubPath *sp = node->subpath;
2465         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2466         Inkscape::NodePath::Node *sample_cursor = NULL;
2467         Inkscape::NodePath::Node *sample_end = NULL;
2468         Inkscape::NodePath::Node *delete_cursor = node;
2469         bool just_delete = false;
2471         //find the start of this contiguous selection
2472         //move left to the first node that is not selected
2473         //or the start of the non-closed path
2474         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2475             delete_cursor = curr;
2476         }
2478         //just delete at the beginning of an open path
2479         if (!delete_cursor->p.other) {
2480             sample_cursor = delete_cursor;
2481             just_delete = true;
2482         } else {
2483             sample_cursor = delete_cursor->p.other;
2484         }
2486         //calculate points for each segment
2487         int rate = 5;
2488         float period = 1.0 / rate;
2489         std::vector<Geom::Point> data;
2490         if (!just_delete) {
2491             data.push_back(sample_cursor->pos);
2492             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2493                 //just delete at the end of an open path
2494                 if (!sp->closed && curr == sp->last) {
2495                     just_delete = true;
2496                     break;
2497                 }
2499                 //sample points on the contiguous selected segment
2500                 Geom::Point *bez;
2501                 bez = new Geom::Point [4];
2502                 bez[0] = curr->pos;
2503                 bez[1] = curr->n.pos;
2504                 bez[2] = curr->n.other->p.pos;
2505                 bez[3] = curr->n.other->pos;
2506                 for (int i=1; i<rate; i++) {
2507                     gdouble t = i * period;
2508                     Geom::Point p = bezier_pt(3, bez, t);
2509                     data.push_back(p);
2510                 }
2511                 data.push_back(curr->n.other->pos);
2513                 sample_end = curr->n.other;
2514                 //break if we've come full circle or hit the end of the selection
2515                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2516                     break;
2517                 }
2518             }
2519         }
2521         if (!just_delete) {
2522             //calculate the best fitting single segment and adjust the endpoints
2523             Geom::Point *adata;
2524             adata = new Geom::Point [data.size()];
2525             copy(data.begin(), data.end(), adata);
2527             Geom::Point *bez;
2528             bez = new Geom::Point [4];
2529             //would decreasing error create a better fitting approximation?
2530             gdouble error = 1.0;
2531             gint ret;
2532             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2534             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2535             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2536             //the resulting nodes behave as expected.
2537             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2538                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2539             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2540                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2542             //adjust endpoints
2543             sample_cursor->n.pos = bez[1];
2544             sample_end->p.pos = bez[2];
2545         }
2547         //destroy this contiguous selection
2548         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2549             Inkscape::NodePath::Node *temp = delete_cursor;
2550             if (delete_cursor->n.other == delete_cursor) {
2551                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2552                 delete_cursor = NULL;
2553             } else {
2554                 delete_cursor = delete_cursor->n.other;
2555             }
2556             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2557             sp_nodepath_node_destroy(temp);
2558         }
2560         sp_nodepath_update_handles(nodepath);
2562         if (!g_slist_find(nodepaths, nodepath))
2563             nodepaths = g_slist_prepend (nodepaths, nodepath);
2564     }
2566     for (GSList *i = nodepaths; i; i = i->next) {
2567         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2568         // different nodepaths will give us one undo event per nodepath
2569         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2571         // if the entire nodepath is removed, delete the selected object.
2572         if (nodepath->subpaths == NULL ||
2573             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2574             //at least 2
2575             sp_nodepath_get_node_count(nodepath) < 2) {
2576             SPDocument *document = sp_desktop_document (nodepath->desktop);
2577             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2578             //delete this nodepath's object, not the entire selection! (though at this time, this
2579             //does not matter)
2580             sp_selection_delete(nodepath->desktop);
2581             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2582                               _("Delete nodes"));
2583         } else {
2584             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2585             sp_nodepath_update_statusbar(nodepath);
2586         }
2587     }
2589     g_slist_free (nodepaths);
2592 /**
2593  * Delete one or more selected nodes.
2594  */
2595 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2597     if (!nodepath) return;
2598     if (!nodepath->selected) return;
2600     /** \todo fixme: do it the right way */
2601     while (nodepath->selected) {
2602        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2603         sp_nodepath_node_destroy(node);
2604     }
2607     //clean up the nodepath (such as for trivial subpaths)
2608     sp_nodepath_cleanup(nodepath);
2610     sp_nodepath_update_handles(nodepath);
2612     // if the entire nodepath is removed, delete the selected object.
2613     if (nodepath->subpaths == NULL ||
2614         sp_nodepath_get_node_count(nodepath) < 2) {
2615         SPDocument *document = sp_desktop_document (nodepath->desktop);
2616         sp_selection_delete(nodepath->desktop);
2617         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2618                           _("Delete nodes"));
2619         return;
2620     }
2622     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2624     sp_nodepath_update_statusbar(nodepath);
2627 /**
2628  * Delete one or more segments between two selected nodes.
2629  * This is the code for 'split'.
2630  */
2631 void
2632 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2634    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2635    Inkscape::NodePath::Node *curr, *next;     //Iterators
2637     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2639     if (g_list_length(nodepath->selected) != 2) {
2640         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2641                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2642         return;
2643     }
2645     //Selected nodes, not inclusive
2646    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2647    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2649     if ( ( a==b)                       ||  //same node
2650          (a->subpath  != b->subpath )  ||  //not the same path
2651          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2652          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2653     {
2654         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2655                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2656         return;
2657     }
2659     //###########################################
2660     //# BEGIN EDITS
2661     //###########################################
2662     //##################################
2663     //# CLOSED PATH
2664     //##################################
2665     if (a->subpath->closed) {
2668         gboolean reversed = FALSE;
2670         //Since we can go in a circle, we need to find the shorter distance.
2671         //  a->b or b->a
2672         start = end = NULL;
2673         int distance    = 0;
2674         int minDistance = 0;
2675         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2676             if (curr==b) {
2677                 //printf("a to b:%d\n", distance);
2678                 start = a;//go from a to b
2679                 end   = b;
2680                 minDistance = distance;
2681                 //printf("A to B :\n");
2682                 break;
2683             }
2684             distance++;
2685         }
2687         //try again, the other direction
2688         distance = 0;
2689         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2690             if (curr==a) {
2691                 //printf("b to a:%d\n", distance);
2692                 if (distance < minDistance) {
2693                     start    = b;  //we go from b to a
2694                     end      = a;
2695                     reversed = TRUE;
2696                     //printf("B to A\n");
2697                 }
2698                 break;
2699             }
2700             distance++;
2701         }
2704         //Copy everything from 'end' to 'start' to a new subpath
2705        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2706         for (curr=end ; curr ; curr=curr->n.other) {
2707             NRPathcode code = (NRPathcode) curr->code;
2708             if (curr == end)
2709                 code = NR_MOVETO;
2710             sp_nodepath_node_new(t, NULL,
2711                                  (Inkscape::NodePath::NodeType)curr->type, code,
2712                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2713             if (curr == start)
2714                 break;
2715         }
2716         sp_nodepath_subpath_destroy(a->subpath);
2719     }
2723     //##################################
2724     //# OPEN PATH
2725     //##################################
2726     else {
2728         //We need to get the direction of the list between A and B
2729         //Can we walk from a to b?
2730         start = end = NULL;
2731         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2732             if (curr==b) {
2733                 start = a;  //did it!  we go from a to b
2734                 end   = b;
2735                 //printf("A to B\n");
2736                 break;
2737             }
2738         }
2739         if (!start) {//didn't work?  let's try the other direction
2740             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2741                 if (curr==a) {
2742                     start = b;  //did it!  we go from b to a
2743                     end   = a;
2744                     //printf("B to A\n");
2745                     break;
2746                 }
2747             }
2748         }
2749         if (!start) {
2750             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2751                                                      _("Cannot find path between nodes."));
2752             return;
2753         }
2757         //Copy everything after 'end' to a new subpath
2758        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2759         for (curr=end ; curr ; curr=curr->n.other) {
2760             NRPathcode code = (NRPathcode) curr->code;
2761             if (curr == end)
2762                 code = NR_MOVETO;
2763             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2764                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2765         }
2767         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2768         for (curr = start->n.other ; curr  ; curr=next) {
2769             next = curr->n.other;
2770             sp_nodepath_node_destroy(curr);
2771         }
2773     }
2774     //###########################################
2775     //# END EDITS
2776     //###########################################
2778     //clean up the nodepath (such as for trivial subpaths)
2779     sp_nodepath_cleanup(nodepath);
2781     sp_nodepath_update_handles(nodepath);
2783     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2785     sp_nodepath_update_statusbar(nodepath);
2788 /**
2789  * Call sp_nodepath_set_line() for all selected segments.
2790  */
2791 void
2792 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2794     if (nodepath == NULL) return;
2796     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2797        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2798         g_assert(n->selected);
2799         if (n->p.other && n->p.other->selected) {
2800             sp_nodepath_set_line_type(n, code);
2801         }
2802     }
2804     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2807 /**
2808  * Call sp_nodepath_convert_node_type() for all selected nodes.
2809  */
2810 void
2811 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2813     if (nodepath == NULL) return;
2815     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2817     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2818         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2819     }
2821     sp_nodepath_update_repr(nodepath, _("Change node type"));
2824 /**
2825  * Change select status of node, update its own and neighbour handles.
2826  */
2827 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2829     node->selected = selected;
2831     if (selected) {
2832         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 11 : 9);
2833         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2834         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2835         sp_knot_update_ctrl(node->knot);
2836     } else {
2837         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 9 : 7);
2838         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2839         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2840         sp_knot_update_ctrl(node->knot);
2841     }
2843     sp_node_update_handles(node);
2844     if (node->n.other) sp_node_update_handles(node->n.other);
2845     if (node->p.other) sp_node_update_handles(node->p.other);
2848 /**
2849 \brief Select a node
2850 \param node     The node to select
2851 \param incremental   If true, add to selection, otherwise deselect others
2852 \param override   If true, always select this node, otherwise toggle selected status
2853 */
2854 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2856     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2858     if (incremental) {
2859         if (override) {
2860             if (!g_list_find(nodepath->selected, node)) {
2861                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2862             }
2863             sp_node_set_selected(node, TRUE);
2864         } else { // toggle
2865             if (node->selected) {
2866                 g_assert(g_list_find(nodepath->selected, node));
2867                 nodepath->selected = g_list_remove(nodepath->selected, node);
2868             } else {
2869                 g_assert(!g_list_find(nodepath->selected, node));
2870                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2871             }
2872             sp_node_set_selected(node, !node->selected);
2873         }
2874     } else {
2875         sp_nodepath_deselect(nodepath);
2876         nodepath->selected = g_list_prepend(nodepath->selected, node);
2877         sp_node_set_selected(node, TRUE);
2878     }
2880     sp_nodepath_update_statusbar(nodepath);
2884 /**
2885 \brief Deselect all nodes in the nodepath
2886 */
2887 void
2888 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2890     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2892     while (nodepath->selected) {
2893         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2894         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2895     }
2896     sp_nodepath_update_statusbar(nodepath);
2899 /**
2900 \brief Select or invert selection of all nodes in the nodepath
2901 */
2902 void
2903 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2905     if (!nodepath) return;
2907     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2908        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2909         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2910            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2911            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2912         }
2913     }
2916 /**
2917  * If nothing selected, does the same as sp_nodepath_select_all();
2918  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2919  * (i.e., similar to "select all in layer", with the "selected" subpaths
2920  * being treated as "layers" in the path).
2921  */
2922 void
2923 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2925     if (!nodepath) return;
2927     if (g_list_length (nodepath->selected) == 0) {
2928         sp_nodepath_select_all (nodepath, invert);
2929         return;
2930     }
2932     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2933     GSList *subpaths = NULL;
2935     for (GList *l = copy; l != NULL; l = l->next) {
2936         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2937         Inkscape::NodePath::SubPath *subpath = n->subpath;
2938         if (!g_slist_find (subpaths, subpath))
2939             subpaths = g_slist_prepend (subpaths, subpath);
2940     }
2942     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2943         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2944         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2945             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2946             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2947         }
2948     }
2950     g_slist_free (subpaths);
2951     g_list_free (copy);
2954 /**
2955  * \brief Select the node after the last selected; if none is selected,
2956  * select the first within path.
2957  */
2958 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2960     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2962    Inkscape::NodePath::Node *last = NULL;
2963     if (nodepath->selected) {
2964         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2965            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2966             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2967             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2968                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2969                 if (node->selected) {
2970                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2971                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2972                             if (spl->next) { // there's a next subpath
2973                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2974                                 last = subpath_next->first;
2975                             } else if (spl->prev) { // there's a previous subpath
2976                                 last = NULL; // to be set later to the first node of first subpath
2977                             } else {
2978                                 last = node->n.other;
2979                             }
2980                         } else {
2981                             last = node->n.other;
2982                         }
2983                     } else {
2984                         if (node->n.other) {
2985                             last = node->n.other;
2986                         } else {
2987                             if (spl->next) { // there's a next subpath
2988                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2989                                 last = subpath_next->first;
2990                             } else if (spl->prev) { // there's a previous subpath
2991                                 last = NULL; // to be set later to the first node of first subpath
2992                             } else {
2993                                 last = (Inkscape::NodePath::Node *) subpath->first;
2994                             }
2995                         }
2996                     }
2997                 }
2998             }
2999         }
3000         sp_nodepath_deselect(nodepath);
3001     }
3003     if (last) { // there's at least one more node after selected
3004         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3005     } else { // no more nodes, select the first one in first subpath
3006        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
3007         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
3008     }
3011 /**
3012  * \brief Select the node before the first selected; if none is selected,
3013  * select the last within path
3014  */
3015 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
3017     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
3019    Inkscape::NodePath::Node *last = NULL;
3020     if (nodepath->selected) {
3021         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
3022            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3023             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
3024                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3025                 if (node->selected) {
3026                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
3027                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
3028                             if (spl->prev) { // there's a prev subpath
3029                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3030                                 last = subpath_prev->last;
3031                             } else if (spl->next) { // there's a next subpath
3032                                 last = NULL; // to be set later to the last node of last subpath
3033                             } else {
3034                                 last = node->p.other;
3035                             }
3036                         } else {
3037                             last = node->p.other;
3038                         }
3039                     } else {
3040                         if (node->p.other) {
3041                             last = node->p.other;
3042                         } else {
3043                             if (spl->prev) { // there's a prev subpath
3044                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3045                                 last = subpath_prev->last;
3046                             } else if (spl->next) { // there's a next subpath
3047                                 last = NULL; // to be set later to the last node of last subpath
3048                             } else {
3049                                 last = (Inkscape::NodePath::Node *) subpath->last;
3050                             }
3051                         }
3052                     }
3053                 }
3054             }
3055         }
3056         sp_nodepath_deselect(nodepath);
3057     }
3059     if (last) { // there's at least one more node before selected
3060         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3061     } else { // no more nodes, select the last one in last subpath
3062         GList *spl = g_list_last(nodepath->subpaths);
3063        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3064         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
3065     }
3068 /**
3069  * \brief Select all nodes that are within the rectangle.
3070  */
3071 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, Geom::Rect const &b, gboolean incremental)
3073     if (!incremental) {
3074         sp_nodepath_deselect(nodepath);
3075     }
3077     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3078        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3079         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3080            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3082             if (b.contains(node->pos)) {
3083                 sp_nodepath_node_select(node, TRUE, TRUE);
3084             }
3085         }
3086     }
3090 void
3091 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3093     g_assert (n);
3094     g_assert (nodepath);
3095     g_assert (n->subpath->nodepath == nodepath);
3097     if (g_list_length (nodepath->selected) == 0) {
3098         if (grow > 0) {
3099             sp_nodepath_node_select(n, TRUE, TRUE);
3100         }
3101         return;
3102     }
3104     if (g_list_length (nodepath->selected) == 1) {
3105         if (grow < 0) {
3106             sp_nodepath_deselect (nodepath);
3107             return;
3108         }
3109     }
3111         double n_sel_range = 0, p_sel_range = 0;
3112             Inkscape::NodePath::Node *farthest_n_node = n;
3113             Inkscape::NodePath::Node *farthest_p_node = n;
3115         // Calculate ranges
3116         {
3117             double n_range = 0, p_range = 0;
3118             bool n_going = true, p_going = true;
3119             Inkscape::NodePath::Node *n_node = n;
3120             Inkscape::NodePath::Node *p_node = n;
3121             do {
3122                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3123                 if (n_node && n_going)
3124                     n_node = n_node->n.other;
3125                 if (n_node == NULL) {
3126                     n_going = false;
3127                 } else {
3128                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3129                     if (n_node->selected) {
3130                         n_sel_range = n_range;
3131                         farthest_n_node = n_node;
3132                     }
3133                     if (n_node == p_node) {
3134                         n_going = false;
3135                         p_going = false;
3136                     }
3137                 }
3138                 if (p_node && p_going)
3139                     p_node = p_node->p.other;
3140                 if (p_node == NULL) {
3141                     p_going = false;
3142                 } else {
3143                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3144                     if (p_node->selected) {
3145                         p_sel_range = p_range;
3146                         farthest_p_node = p_node;
3147                     }
3148                     if (p_node == n_node) {
3149                         n_going = false;
3150                         p_going = false;
3151                     }
3152                 }
3153             } while (n_going || p_going);
3154         }
3156     if (grow > 0) {
3157         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3158                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3159         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3160                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3161         }
3162     } else {
3163         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3164                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3165         } else if (farthest_p_node && farthest_p_node->selected) {
3166                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3167         }
3168     }
3171 void
3172 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3174     g_assert (n);
3175     g_assert (nodepath);
3176     g_assert (n->subpath->nodepath == nodepath);
3178     if (g_list_length (nodepath->selected) == 0) {
3179         if (grow > 0) {
3180             sp_nodepath_node_select(n, TRUE, TRUE);
3181         }
3182         return;
3183     }
3185     if (g_list_length (nodepath->selected) == 1) {
3186         if (grow < 0) {
3187             sp_nodepath_deselect (nodepath);
3188             return;
3189         }
3190     }
3192     Inkscape::NodePath::Node *farthest_selected = NULL;
3193     double farthest_dist = 0;
3195     Inkscape::NodePath::Node *closest_unselected = NULL;
3196     double closest_dist = NR_HUGE;
3198     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3199        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3200         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3201            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3202            if (node == n)
3203                continue;
3204            if (node->selected) {
3205                if (Geom::L2(node->pos - n->pos) > farthest_dist) {
3206                    farthest_dist = Geom::L2(node->pos - n->pos);
3207                    farthest_selected = node;
3208                }
3209            } else {
3210                if (Geom::L2(node->pos - n->pos) < closest_dist) {
3211                    closest_dist = Geom::L2(node->pos - n->pos);
3212                    closest_unselected = node;
3213                }
3214            }
3215         }
3216     }
3218     if (grow > 0) {
3219         if (closest_unselected) {
3220             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3221         }
3222     } else {
3223         if (farthest_selected) {
3224             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3225         }
3226     }
3230 /**
3231 \brief  Saves all nodes' and handles' current positions in their origin members
3232 */
3233 void
3234 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3236     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3237        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3238         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3239            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3240            n->origin = n->pos;
3241            n->p.origin = n->p.pos;
3242            n->n.origin = n->n.pos;
3243         }
3244     }
3247 /**
3248 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3249 */
3250 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3252     GList *r = NULL;
3253     if (nodepath->selected) {
3254         guint i = 0;
3255         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3256             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3257             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3258                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3259                 i++;
3260                 if (node->selected) {
3261                     r = g_list_append(r, GINT_TO_POINTER(i));
3262                 }
3263             }
3264         }
3265     }
3266     return r;
3269 /**
3270 \brief  Restores selection by selecting nodes whose positions are in the list
3271 */
3272 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3274     sp_nodepath_deselect(nodepath);
3276     guint i = 0;
3277     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3278        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3279         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3280            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3281             i++;
3282             if (g_list_find(r, GINT_TO_POINTER(i))) {
3283                 sp_nodepath_node_select(node, TRUE, TRUE);
3284             }
3285         }
3286     }
3290 /**
3291 \brief Adjusts handle according to node type and line code.
3292 */
3293 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3295     g_assert(node);
3297     // nothing to do for auto nodes (sp_node_adjust_handles() does the job)
3298     if (node->type == Inkscape::NodePath::NODE_AUTO)
3299         return;
3301    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3302    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3304    // nothing to do if we are an end node
3305     if (me->other == NULL) return;
3306     if (other->other == NULL) return;
3308     // nothing to do if we are a cusp node
3309     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3311     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3312     NRPathcode mecode;
3313     if (which_adjust == 1) {
3314         mecode = (NRPathcode)me->other->code;
3315     } else {
3316         mecode = (NRPathcode)node->code;
3317     }
3318     if (mecode == NR_LINETO) return;
3320     if (sp_node_side_is_line(node, other)) {
3321         // other is a line, and we are either smooth or symm
3322        Inkscape::NodePath::Node *othernode = other->other;
3323         double len = Geom::L2(me->pos - node->pos);
3324         Geom::Point delta = node->pos - othernode->pos;
3325         double linelen = Geom::L2(delta);
3326         if (linelen < 1e-18)
3327             return;
3328         me->pos = node->pos + (len / linelen)*delta;
3329         return;
3330     }
3332     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3333         // symmetrize 
3334         me->pos = 2 * node->pos - other->pos;
3335         return;
3336     } else {
3337         // smoothify
3338         double len = Geom::L2(me->pos - node->pos);
3339         Geom::Point delta = other->pos - node->pos;
3340         double otherlen = Geom::L2(delta);
3341         if (otherlen < 1e-18) return;
3342         me->pos = node->pos - (len / otherlen) * delta;
3343     }
3346 /**
3347  \brief Adjusts both handles according to node type and line code
3348  */
3349 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3351     g_assert(node);
3353     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3355     /* we are either smooth or symm */
3357     if (node->p.other == NULL) return;
3358     if (node->n.other == NULL) return;
3360     if (node->type == Inkscape::NodePath::NODE_AUTO) {
3361         sp_node_adjust_handles_auto(node);
3362         return;
3363     }
3365     if (sp_node_side_is_line(node, &node->p)) {
3366         sp_node_adjust_handle(node, 1);
3367         return;
3368     }
3370     if (sp_node_side_is_line(node, &node->n)) {
3371         sp_node_adjust_handle(node, -1);
3372         return;
3373     }
3375     /* both are curves */
3376     Geom::Point const delta( node->n.pos - node->p.pos );
3378     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3379         node->p.pos = node->pos - delta / 2;
3380         node->n.pos = node->pos + delta / 2;
3381         return;
3382     }
3384     /* We are smooth */
3385     double plen = Geom::L2(node->p.pos - node->pos);
3386     if (plen < 1e-18) return;
3387     double nlen = Geom::L2(node->n.pos - node->pos);
3388     if (nlen < 1e-18) return;
3389     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3390     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3393 static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node)
3395     if (node->p.other == NULL || node->n.other == NULL) {
3396         node->p.pos = node->pos;
3397         node->n.pos = node->pos;
3398         return;
3399     }
3401     Geom::Point leg_prev = to_2geom(node->p.other->pos - node->pos);
3402     Geom::Point leg_next = to_2geom(node->n.other->pos - node->pos);
3403  
3404     double norm_leg_prev = Geom::L2(leg_prev);
3405     double norm_leg_next = Geom::L2(leg_next);
3406  
3407     Geom::Point delta;
3408     if (norm_leg_next > 0.0) {
3409         delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
3410         delta.normalize();
3411     }
3412  
3413     node->p.pos = node->pos - norm_leg_prev / 3 * delta;
3414     node->n.pos = node->pos + norm_leg_next / 3 * delta;
3417 /**
3418  * Node event callback.
3419  */
3420 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3422     gboolean ret = FALSE;
3423     switch (event->type) {
3424         case GDK_ENTER_NOTIFY:
3425             Inkscape::NodePath::Path::active_node = n;
3426             break;
3427         case GDK_LEAVE_NOTIFY:
3428             Inkscape::NodePath::Path::active_node = NULL;
3429             break;
3430         case GDK_SCROLL:
3431             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3432                 switch (event->scroll.direction) {
3433                     case GDK_SCROLL_UP:
3434                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3435                         break;
3436                     case GDK_SCROLL_DOWN:
3437                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3438                         break;
3439                     default:
3440                         break;
3441                 }
3442                 ret = TRUE;
3443             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3444                 switch (event->scroll.direction) {
3445                     case GDK_SCROLL_UP:
3446                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3447                         break;
3448                     case GDK_SCROLL_DOWN:
3449                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3450                         break;
3451                     default:
3452                         break;
3453                 }
3454                 ret = TRUE;
3455             }
3456             break;
3457         case GDK_KEY_PRESS:
3458             switch (get_group0_keyval (&event->key)) {
3459                 case GDK_space:
3460                     if (event->key.state & GDK_BUTTON1_MASK) {
3461                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3462                         stamp_repr(nodepath);
3463                         ret = TRUE;
3464                     }
3465                     break;
3466                 case GDK_Page_Up:
3467                     if (event->key.state & GDK_CONTROL_MASK) {
3468                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3469                     } else {
3470                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3471                     }
3472                     break;
3473                 case GDK_Page_Down:
3474                     if (event->key.state & GDK_CONTROL_MASK) {
3475                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3476                     } else {
3477                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3478                     }
3479                     break;
3480                 default:
3481                     break;
3482             }
3483             break;
3484         default:
3485             break;
3486     }
3488     return ret;
3491 /**
3492  * Handle keypress on node; directly called.
3493  */
3494 gboolean node_key(GdkEvent *event)
3496     Inkscape::NodePath::Path *np;
3498     // there is no way to verify nodes so set active_node to nil when deleting!!
3499     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3501     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3502         gint ret = FALSE;
3503         switch (get_group0_keyval (&event->key)) {
3504             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3505             case GDK_BackSpace:
3506                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3507                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3508                 sp_nodepath_update_repr(np, _("Delete node"));
3509                 Inkscape::NodePath::Path::active_node = NULL;
3510                 ret = TRUE;
3511                 break;
3512             case GDK_c:
3513                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3514                 ret = TRUE;
3515                 break;
3516             case GDK_s:
3517                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3518                 ret = TRUE;
3519                 break;
3520             case GDK_a:
3521                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_AUTO);
3522                 ret = TRUE;
3523                 break;
3524             case GDK_y:
3525                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3526                 ret = TRUE;
3527                 break;
3528             case GDK_b:
3529                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3530                 ret = TRUE;
3531                 break;
3532         }
3533         return ret;
3534     }
3535     return FALSE;
3538 /**
3539  * Mouseclick on node callback.
3540  */
3541 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3543    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3545     if (state & GDK_CONTROL_MASK) {
3546         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3548         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3549             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3550                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3551             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3552                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3553             } else if (n->type == Inkscape::NodePath::NODE_SYMM) {
3554                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_AUTO);
3555             } else {
3556                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3557             }
3558             sp_nodepath_update_repr(nodepath, _("Change node type"));
3559             sp_nodepath_update_statusbar(nodepath);
3561         } else { //ctrl+alt+click: delete node
3562             GList *node_to_delete = NULL;
3563             node_to_delete = g_list_append(node_to_delete, n);
3564             sp_node_delete_preserve(node_to_delete);
3565         }
3567     } else {
3568         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3569     }
3572 /**
3573  * Mouse grabbed node callback.
3574  */
3575 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3577    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3579     if (!n->selected) {
3580         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3581     }
3583     n->is_dragging = true;
3584     // Reconstruct and store the location of the mouse pointer at the time when we started dragging (needed for snapping)
3585     n->subpath->nodepath->drag_origin_mouse = knot->grabbed_rel_pos + knot->drag_origin;  
3586     
3587     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3589     sp_nodepath_remember_origins (n->subpath->nodepath);
3592 /**
3593  * Mouse ungrabbed node callback.
3594  */
3595 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3597    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3599    n->dragging_out = NULL;
3600    n->is_dragging = false;
3601    n->subpath->nodepath->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
3602    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3604    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3607 /**
3608  * The point on a line, given by its angle, closest to the given point.
3609  * \param p  A point.
3610  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3611  * \param closest  Pointer to the point struct where the result is stored.
3612  * \todo FIXME: use dot product perhaps?
3613  */
3614 static void point_line_closest(Geom::Point *p, double a, Geom::Point *closest)
3616     if (a == HUGE_VAL) { // vertical
3617         *closest = Geom::Point(0, (*p)[Geom::Y]);
3618     } else {
3619         (*closest)[Geom::X] = ( a * (*p)[Geom::Y] + (*p)[Geom::X]) / (a*a + 1);
3620         (*closest)[Geom::Y] = a * (*closest)[Geom::X];
3621     }
3624 /**
3625  * Distance from the point to a line given by its angle.
3626  * \param p  A point.
3627  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3628  */
3629 static double point_line_distance(Geom::Point *p, double a)
3631     Geom::Point c;
3632     point_line_closest(p, a, &c);
3633     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]));
3636 /**
3637  * Callback for node "request" signal.
3638  * \todo fixme: This goes to "moved" event? (lauris)
3639  */
3640 static gboolean
3641 node_request(SPKnot */*knot*/, Geom::Point const &p, guint state, gpointer data)
3643     double yn, xn, yp, xp;
3644     double an, ap, na, pa;
3645     double d_an, d_ap, d_na, d_pa;
3646     gboolean collinear = FALSE;
3647     Geom::Point c;
3648     Geom::Point pr;
3650     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3652     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3654     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3655     if ( (!n->subpath->nodepath->straight_path) &&
3656          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3657            || n->dragging_out ) )
3658     {
3659        Geom::Point mouse = p;
3661        if (!n->dragging_out) {
3662            // This is the first drag-out event; find out which handle to drag out
3663            double appr_n = (n->n.other ? Geom::L2(n->n.other->pos - n->pos) - Geom::L2(n->n.other->pos - p) : -HUGE_VAL);
3664            double appr_p = (n->p.other ? Geom::L2(n->p.other->pos - n->pos) - Geom::L2(n->p.other->pos - p) : -HUGE_VAL);
3666            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3667                return FALSE;
3669            Inkscape::NodePath::NodeSide *opposite;
3670            if (appr_p > appr_n) { // closer to p
3671                n->dragging_out = &n->p;
3672                opposite = &n->n;
3673                n->code = NR_CURVETO;
3674            } else if (appr_p < appr_n) { // closer to n
3675                n->dragging_out = &n->n;
3676                opposite = &n->p;
3677                n->n.other->code = NR_CURVETO;
3678            } else { // p and n nodes are the same
3679                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3680                    n->dragging_out = &n->p;
3681                    opposite = &n->n;
3682                    n->code = NR_CURVETO;
3683                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3684                    n->dragging_out = &n->n;
3685                    opposite = &n->p;
3686                    n->n.other->code = NR_CURVETO;
3687                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3688                    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);
3689                    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);
3690                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3691                        n->dragging_out = &n->n;
3692                        opposite = &n->p;
3693                        n->n.other->code = NR_CURVETO;
3694                    } else { // closer to other's n handle
3695                        n->dragging_out = &n->p;
3696                        opposite = &n->n;
3697                        n->code = NR_CURVETO;
3698                    }
3699                }
3700            }
3702            // if there's another handle, make sure the one we drag out starts parallel to it
3703            if (opposite->pos != n->pos) {
3704                mouse = n->pos - Geom::L2(mouse - n->pos) * Geom::unit_vector(opposite->pos - n->pos);
3705            }
3707            // knots might not be created yet!
3708            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3709            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3710        }
3712        // pass this on to the handle-moved callback
3713        node_handle_moved(n->dragging_out->knot, mouse, state, (gpointer) n);
3714        sp_node_update_handles(n);
3715        return TRUE;
3716    }
3718     if (state & GDK_CONTROL_MASK) { // constrained motion
3720         // calculate relative distances of handles
3721         // n handle:
3722         yn = n->n.pos[Geom::Y] - n->pos[Geom::Y];
3723         xn = n->n.pos[Geom::X] - n->pos[Geom::X];
3724         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3725         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3726             if (n->n.other) { // if there is the next point
3727                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3728                     yn = n->n.other->origin[Geom::Y] - n->origin[Geom::Y]; // use origin because otherwise the direction will change as you drag
3729                     xn = n->n.other->origin[Geom::X] - n->origin[Geom::X];
3730             }
3731         }
3732         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3733         if (yn < 0) { xn = -xn; yn = -yn; }
3735         // p handle:
3736         yp = n->p.pos[Geom::Y] - n->pos[Geom::Y];
3737         xp = n->p.pos[Geom::X] - n->pos[Geom::X];
3738         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3739         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3740             if (n->p.other) {
3741                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3742                     yp = n->p.other->origin[Geom::Y] - n->origin[Geom::Y];
3743                     xp = n->p.other->origin[Geom::X] - n->origin[Geom::X];
3744             }
3745         }
3746         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3747         if (yp < 0) { xp = -xp; yp = -yp; }
3749         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3750             // sliding on handles, only if at least one of the handles is non-vertical
3751             // (otherwise it's the same as ctrl+drag anyway)
3753             // calculate angles of the handles
3754             if (xn == 0) {
3755                 if (yn == 0) { // no handle, consider it the continuation of the other one
3756                     an = 0;
3757                     collinear = TRUE;
3758                 }
3759                 else an = 0; // vertical; set the angle to horizontal
3760             } else an = yn/xn;
3762             if (xp == 0) {
3763                 if (yp == 0) { // no handle, consider it the continuation of the other one
3764                     ap = an;
3765                 }
3766                 else ap = 0; // vertical; set the angle to horizontal
3767             } else  ap = yp/xp;
3769             if (collinear) an = ap;
3771             // angles of the perpendiculars; HUGE_VAL means vertical
3772             if (an == 0) na = HUGE_VAL; else na = -1/an;
3773             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3775             // mouse point relative to the node's original pos
3776             pr = p - n->origin;
3778             // distances to the four lines (two handles and two perpendiculars)
3779             d_an = point_line_distance(&pr, an);
3780             d_na = point_line_distance(&pr, na);
3781             d_ap = point_line_distance(&pr, ap);
3782             d_pa = point_line_distance(&pr, pa);
3784             // find out which line is the closest, save its closest point in c
3785             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3786                 point_line_closest(&pr, an, &c);
3787             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3788                 point_line_closest(&pr, ap, &c);
3789             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3790                 point_line_closest(&pr, na, &c);
3791             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3792                 point_line_closest(&pr, pa, &c);
3793             }
3795             // move the node to the closest point
3796             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3797                                             n->origin[Geom::X] + c[Geom::X] - n->pos[Geom::X],
3798                                             n->origin[Geom::Y] + c[Geom::Y] - n->pos[Geom::Y], 
3799                                             true);
3801         } else {  // constraining to hor/vert
3803             if (fabs(p[Geom::X] - n->origin[Geom::X]) > fabs(p[Geom::Y] - n->origin[Geom::Y])) { // snap to hor
3804                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3805                                                 p[Geom::X] - n->pos[Geom::X], 
3806                                                 n->origin[Geom::Y] - n->pos[Geom::Y],
3807                                                 true, 
3808                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::X]));
3809             } else { // snap to vert
3810                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3811                                                 n->origin[Geom::X] - n->pos[Geom::X],
3812                                                 p[Geom::Y] - n->pos[Geom::Y],
3813                                                 true,
3814                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::Y]));
3815             }
3816         }
3817     } else { // move freely
3818         if (n->is_dragging) {
3819             if (state & GDK_MOD1_MASK) { // sculpt
3820                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, p - n->origin);
3821             } else {
3822                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3823                                             p[Geom::X] - n->pos[Geom::X],
3824                                             p[Geom::Y] - n->pos[Geom::Y],
3825                                             (state & GDK_SHIFT_MASK) == 0);
3826             }
3827         }
3828     }
3830     n->subpath->nodepath->desktop->scroll_to_point(p);
3832     return TRUE;
3835 /**
3836  * Node handle clicked callback.
3837  */
3838 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3840    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3842     if (state & GDK_CONTROL_MASK) { // "delete" handle
3843         if (n->p.knot == knot) {
3844             n->p.pos = n->pos;
3845         } else if (n->n.knot == knot) {
3846             n->n.pos = n->pos;
3847         }
3848         sp_node_update_handles(n);
3849         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3850         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3851         sp_nodepath_update_statusbar(nodepath);
3853     } else { // just select or add to selection, depending in Shift
3854         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3855     }
3858 /**
3859  * Node handle grabbed callback.
3860  */
3861 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3863    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3865     // convert auto -> smooth when dragging handle
3866    if (n->type == Inkscape::NodePath::NODE_AUTO) {
3867         n->type = Inkscape::NodePath::NODE_SMOOTH;
3868         sp_nodepath_update_node_knot (n);
3869    }
3871     if (!n->selected) {
3872         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3873     }
3875     // remember the origin point of the handle
3876     if (n->p.knot == knot) {
3877         n->p.origin_radial = n->p.pos - n->pos;
3878     } else if (n->n.knot == knot) {
3879         n->n.origin_radial = n->n.pos - n->pos;
3880     } else {
3881         g_assert_not_reached();
3882     }
3884     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3887 /**
3888  * Node handle ungrabbed callback.
3889  */
3890 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3892    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3894     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3895     if (n->p.knot == knot) {
3896         n->p.origin_radial.a = 0;
3897         sp_knot_set_position(knot, n->p.pos, state);
3898     } else if (n->n.knot == knot) {
3899         n->n.origin_radial.a = 0;
3900         sp_knot_set_position(knot, n->n.pos, state);
3901     } else {
3902         g_assert_not_reached();
3903     }
3905     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3908 /**
3909  * Node handle "request" signal callback.
3910  */
3911 static gboolean node_handle_request(SPKnot *knot, Geom::Point const &p, guint state, gpointer data)
3913     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3915     Inkscape::NodePath::NodeSide *me, *opposite;
3916     gint which;
3917     if (n->p.knot == knot) {
3918         me = &n->p;
3919         opposite = &n->n;
3920         which = -1;
3921     } else if (n->n.knot == knot) {
3922         me = &n->n;
3923         opposite = &n->p;
3924         which = 1;
3925     } else {
3926         me = opposite = NULL;
3927         which = 0;
3928         g_assert_not_reached();
3929     }
3931     SPDesktop *desktop = n->subpath->nodepath->desktop;
3932     SnapManager &m = desktop->namedview->snap_manager;
3933     m.setup(desktop, true, n->subpath->nodepath->item);
3934     Inkscape::SnappedPoint s;
3935     
3936     if ((state & GDK_SHIFT_MASK) != 0) {
3937         // We will not try to snap when the shift-key is pressed
3938         // so remove the old snap indicator and don't wait for it to time-out  
3939         desktop->snapindicator->remove_snappoint();
3940     }
3942     Inkscape::NodePath::Node *othernode = opposite->other;
3943     if (othernode) {
3944         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3945             /* We are smooth node adjacent with line */
3946             Geom::Point const delta = p - n->pos;
3947             Geom::Coord const len = Geom::L2(delta);
3948             Inkscape::NodePath::Node *othernode = opposite->other;
3949             Geom::Point const ndelta = n->pos - othernode->pos;
3950             Geom::Coord const linelen = Geom::L2(ndelta);
3951             Geom::Point ptemp = p;
3952             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3953                 Geom::Coord const scal = dot(delta, ndelta) / linelen;
3954                 ptemp = n->pos + (scal / linelen) * ndelta;
3955             }
3956             if ((state & GDK_SHIFT_MASK) == 0) {
3957                 s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, ptemp, Inkscape::Snapper::ConstraintLine(ptemp, ndelta));
3958             }
3959         } else {
3960             if ((state & GDK_SHIFT_MASK) == 0) {
3961                 s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p);
3962             }
3963         }
3964     } else {
3965         if ((state & GDK_SHIFT_MASK) == 0) {
3966             s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p);
3967         }
3968     }
3969     
3970     sp_node_adjust_handle(n, -which);
3972     return FALSE;
3975 /**
3976  * Node handle moved callback.
3977  */
3978 static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data)
3980    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3981    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3983    Inkscape::NodePath::NodeSide *me;
3984    Inkscape::NodePath::NodeSide *other;
3985     if (n->p.knot == knot) {
3986         me = &n->p;
3987         other = &n->n;
3988     } else if (n->n.knot == knot) {
3989         me = &n->n;
3990         other = &n->p;
3991     } else {
3992         me = NULL;
3993         other = NULL;
3994         g_assert_not_reached();
3995     }
3997     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3998     Radial rme(me->pos - n->pos);
3999     Radial rother(other->pos - n->pos);
4000     Radial rnew(p - n->pos);
4002     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
4003         int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
4004         /* 0 interpreted as "no snapping". */
4006         // 1. Snap to the closest PI/snaps angle, starting from zero.
4007         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
4009         // 2. Snap to the original angle, its opposite and perpendiculars
4010         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
4011             /* The closest PI/2 angle, starting from original angle */
4012             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
4014             // Snap to the closest.
4015             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
4016                        ? a_snapped
4017                        : a_ortho );
4018         }
4020         // 3. Snap to the angle of the opposite line, if any
4021         Inkscape::NodePath::Node *othernode = other->other;
4022         if (othernode) {
4023             Geom::Point other_to_snap(0,0);
4024             if (sp_node_side_is_line(n, other)) {
4025                 other_to_snap = othernode->pos - n->pos;
4026             } else {
4027                 other_to_snap = other->pos - n->pos;
4028             }
4029             if (Geom::L2(other_to_snap) > 1e-3) {
4030                 Radial rother_to_snap(other_to_snap);
4031                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
4032                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
4034                 // Snap to the closest.
4035                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
4036                        ? a_snapped
4037                        : a_oppo );
4038             }
4039         }
4041         rnew.a = a_snapped;
4042     }
4044     if (state & GDK_MOD1_MASK) {
4045         // lock handle length
4046         rnew.r = me->origin_radial.r;
4047     }
4049     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
4050         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
4051         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
4052         rother.a += rnew.a - rme.a;
4053         other->pos = Geom::Point(rother) + n->pos;
4054         if (other->knot) {
4055             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
4056             sp_knot_moveto(other->knot, other->pos);
4057         }
4058     }
4060     me->pos = Geom::Point(rnew) + n->pos;
4061     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
4063     // move knot, but without emitting the signal:
4064     // we cannot emit a "moved" signal because we're now processing it
4065     sp_knot_moveto(me->knot, me->pos);
4067     update_object(n->subpath->nodepath);
4069     /* status text */
4070     SPDesktop *desktop = n->subpath->nodepath->desktop;
4071     if (!desktop) return;
4072     SPEventContext *ec = desktop->event_context;
4073     if (!ec) return;
4075     Inkscape::MessageContext *mc = get_message_context(ec);
4077     if (!mc) return;
4079     double degrees = 180 / M_PI * rnew.a;
4080     if (degrees > 180) degrees -= 360;
4081     if (degrees < -180) degrees += 360;
4082     if (prefs->getBool("/options/compassangledisplay/value"))
4083         degrees = angle_to_compass (degrees);
4085     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
4087     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
4088          _("<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);
4090     g_string_free(length, TRUE);
4093 /**
4094  * Node handle event callback.
4095  */
4096 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
4098     gboolean ret = FALSE;
4099     switch (event->type) {
4100         case GDK_KEY_PRESS:
4101             switch (get_group0_keyval (&event->key)) {
4102                 case GDK_space:
4103                     if (event->key.state & GDK_BUTTON1_MASK) {
4104                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
4105                         stamp_repr(nodepath);
4106                         ret = TRUE;
4107                     }
4108                     break;
4109                 default:
4110                     break;
4111             }
4112             break;
4113         case GDK_ENTER_NOTIFY:
4114             // we use an experimentally determined threshold that seems to work fine
4115             if (Geom::L2(n->pos - knot->pos) < 0.75)
4116                 Inkscape::NodePath::Path::active_node = n;
4117             break;
4118         case GDK_LEAVE_NOTIFY:
4119             // we use an experimentally determined threshold that seems to work fine
4120             if (Geom::L2(n->pos - knot->pos) < 0.75)
4121                 Inkscape::NodePath::Path::active_node = NULL;
4122             break;
4123         default:
4124             break;
4125     }
4127     return ret;
4130 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
4131                                  Radial &rme, Radial &rother, gboolean const both)
4133     rme.a += angle;
4134     if ( both
4135          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4136          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4137     {
4138         rother.a += angle;
4139     }
4142 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
4143                                         Radial &rme, Radial &rother, gboolean const both)
4145     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
4147     gdouble r;
4148     if ( both
4149          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4150          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4151     {
4152         r = MAX(rme.r, rother.r);
4153     } else {
4154         r = rme.r;
4155     }
4157     gdouble const weird_angle = atan2(norm_angle, r);
4158 /* Bulia says norm_angle is just the visible distance that the
4159  * object's end must travel on the screen.  Left as 'angle' for want of
4160  * a better name.*/
4162     rme.a += weird_angle;
4163     if ( both
4164          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4165          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4166     {
4167         rother.a += weird_angle;
4168     }
4171 /**
4172  * Rotate one node.
4173  */
4174 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4176     Inkscape::NodePath::NodeSide *me, *other;
4177     bool both = false;
4179     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4180     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4182     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4183         me = &(n->p);
4184         other = &(n->n);
4185     } else if (!n->p.other) {
4186         me = &(n->n);
4187         other = &(n->p);
4188     } else {
4189         if (which > 0) { // right handle
4190             if (xn > xp) {
4191                 me = &(n->n);
4192                 other = &(n->p);
4193             } else {
4194                 me = &(n->p);
4195                 other = &(n->n);
4196             }
4197         } else if (which < 0){ // left handle
4198             if (xn <= xp) {
4199                 me = &(n->n);
4200                 other = &(n->p);
4201             } else {
4202                 me = &(n->p);
4203                 other = &(n->n);
4204             }
4205         } else { // both handles
4206             me = &(n->n);
4207             other = &(n->p);
4208             both = true;
4209         }
4210     }
4212     Radial rme(me->pos - n->pos);
4213     Radial rother(other->pos - n->pos);
4215     if (screen) {
4216         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4217     } else {
4218         node_rotate_one_internal (*n, angle, rme, rother, both);
4219     }
4221     me->pos = n->pos + Geom::Point(rme);
4223     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4224         other->pos =  n->pos + Geom::Point(rother);
4225     }
4227     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4228     // so here we just move all the knots without emitting move signals, for speed
4229     sp_node_update_handles(n, false);
4232 /**
4233  * Rotate selected nodes.
4234  */
4235 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4237     if (!nodepath || !nodepath->selected) return;
4239     if (g_list_length(nodepath->selected) == 1) {
4240        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4241         node_rotate_one (n, angle, which, screen);
4242     } else {
4243        // rotate as an object:
4245         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4246         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4247         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4248             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4249             box.expandTo (n->pos); // contain all selected nodes
4250         }
4252         gdouble rot;
4253         if (screen) {
4254             gdouble const zoom = nodepath->desktop->current_zoom();
4255             gdouble const zmove = angle / zoom;
4256             gdouble const r = Geom::L2(box.max() - box.midpoint());
4257             rot = atan2(zmove, r);
4258         } else {
4259             rot = angle;
4260         }
4262         Geom::Point rot_center;
4263         if (Inkscape::NodePath::Path::active_node == NULL)
4264             rot_center = box.midpoint();
4265         else
4266             rot_center = Inkscape::NodePath::Path::active_node->pos;
4268         Geom::Matrix t =
4269             Geom::Matrix (Geom::Translate(-rot_center)) *
4270             Geom::Matrix (Geom::Rotate(rot)) *
4271             Geom::Matrix (Geom::Translate(rot_center));
4273         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4274             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4275             n->pos *= t;
4276             n->n.pos *= t;
4277             n->p.pos *= t;
4278             sp_node_update_handles(n, false);
4279         }
4280     }
4282     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4285 /**
4286  * Scale one node.
4287  */
4288 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4290     bool both = false;
4291     Inkscape::NodePath::NodeSide *me, *other;
4293     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4294     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4296     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4297         me = &(n->p);
4298         other = &(n->n);
4299         n->code = NR_CURVETO;
4300     } else if (!n->p.other) {
4301         me = &(n->n);
4302         other = &(n->p);
4303         if (n->n.other)
4304             n->n.other->code = NR_CURVETO;
4305     } else {
4306         if (which > 0) { // right handle
4307             if (xn > xp) {
4308                 me = &(n->n);
4309                 other = &(n->p);
4310                 if (n->n.other)
4311                     n->n.other->code = NR_CURVETO;
4312             } else {
4313                 me = &(n->p);
4314                 other = &(n->n);
4315                 n->code = NR_CURVETO;
4316             }
4317         } else if (which < 0){ // left handle
4318             if (xn <= xp) {
4319                 me = &(n->n);
4320                 other = &(n->p);
4321                 if (n->n.other)
4322                     n->n.other->code = NR_CURVETO;
4323             } else {
4324                 me = &(n->p);
4325                 other = &(n->n);
4326                 n->code = NR_CURVETO;
4327             }
4328         } else { // both handles
4329             me = &(n->n);
4330             other = &(n->p);
4331             both = true;
4332             n->code = NR_CURVETO;
4333             if (n->n.other)
4334                 n->n.other->code = NR_CURVETO;
4335         }
4336     }
4338     Radial rme(me->pos - n->pos);
4339     Radial rother(other->pos - n->pos);
4341     rme.r += grow;
4342     if (rme.r < 0) rme.r = 0;
4343     if (rme.a == HUGE_VAL) {
4344         if (me->other) { // if direction is unknown, initialize it towards the next node
4345             Radial rme_next(me->other->pos - n->pos);
4346             rme.a = rme_next.a;
4347         } else { // if there's no next, initialize to 0
4348             rme.a = 0;
4349         }
4350     }
4351     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4352         rother.r += grow;
4353         if (rother.r < 0) rother.r = 0;
4354         if (rother.a == HUGE_VAL) {
4355             rother.a = rme.a + M_PI;
4356         }
4357     }
4359     me->pos = n->pos + Geom::Point(rme);
4361     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4362         other->pos = n->pos + Geom::Point(rother);
4363     }
4365     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4366     // so here we just move all the knots without emitting move signals, for speed
4367     sp_node_update_handles(n, false);
4370 /**
4371  * Scale selected nodes.
4372  */
4373 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4375     if (!nodepath || !nodepath->selected) return;
4377     if (g_list_length(nodepath->selected) == 1) {
4378         // scale handles of the single selected node
4379         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4380         node_scale_one (n, grow, which);
4381     } else {
4382         // scale nodes as an "object":
4384         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4385         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4386         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4387             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4388             box.expandTo (n->pos); // contain all selected nodes
4389         }
4391         if ( Geom::are_near(box.maxExtent(), 0) ) {
4392             SPEventContext *ec = nodepath->desktop->event_context;
4393             if (!ec) return;
4394             Inkscape::MessageContext *mc = get_message_context(ec);
4395             if (!mc) return;
4396             mc->setF(Inkscape::WARNING_MESSAGE,
4397                              _("Cannot scale nodes when all are at the same location."));
4398             return;
4399         }
4400         double scale = (box.maxExtent() + grow)/box.maxExtent();
4403         Geom::Point scale_center;
4404         if (Inkscape::NodePath::Path::active_node == NULL)
4405             scale_center = box.midpoint();
4406         else
4407             scale_center = Inkscape::NodePath::Path::active_node->pos;
4409         Geom::Matrix t =
4410             Geom::Translate(-scale_center) *
4411             Geom::Scale(scale, scale) *
4412             Geom::Translate(scale_center);
4414         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4415             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4416             n->pos *= t;
4417             n->n.pos *= t;
4418             n->p.pos *= t;
4419             sp_node_update_handles(n, false);
4420         }
4421     }
4423     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4426 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4428     if (!nodepath) return;
4429     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4432 /**
4433  * Flip selected nodes horizontally/vertically.
4434  */
4435 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis, boost::optional<Geom::Point> center)
4437     if (!nodepath || !nodepath->selected) return;
4439     if (g_list_length(nodepath->selected) == 1 && !center) {
4440         // flip handles of the single selected node
4441         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4442         double temp = n->p.pos[axis];
4443         n->p.pos[axis] = n->n.pos[axis];
4444         n->n.pos[axis] = temp;
4445         sp_node_update_handles(n, false);
4446     } else {
4447         // scale nodes as an "object":
4449         Geom::Rect box = sp_node_selected_bbox (nodepath);
4450         if (!center) {
4451             center = box.midpoint();
4452         }
4453         Geom::Matrix t =
4454             Geom::Matrix (Geom::Translate(- *center)) *
4455             Geom::Matrix ((axis == Geom::X)? Geom::Scale(-1, 1) : Geom::Scale(1, -1)) *
4456             Geom::Matrix (Geom::Translate(*center));
4458         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4459             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4460             n->pos *= t;
4461             n->n.pos *= t;
4462             n->p.pos *= t;
4463             sp_node_update_handles(n, false);
4464         }
4465     }
4467     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4470 Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4472     g_assert (nodepath->selected);
4474     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4475     Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4476     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4477         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4478         box.expandTo (n->pos); // contain all selected nodes
4479     }
4480     return box;
4483 //-----------------------------------------------
4484 /**
4485  * Return new subpath under given nodepath.
4486  */
4487 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4489     g_assert(nodepath);
4490     g_assert(nodepath->desktop);
4492    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4494     s->nodepath = nodepath;
4495     s->closed = FALSE;
4496     s->nodes = NULL;
4497     s->first = NULL;
4498     s->last = NULL;
4500     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4501     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4502     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4504     return s;
4507 /**
4508  * Destroy nodes in subpath, then subpath itself.
4509  */
4510 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4512     g_assert(subpath);
4513     g_assert(subpath->nodepath);
4514     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4516     while (subpath->nodes) {
4517         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4518     }
4520     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4522     g_free(subpath);
4525 /**
4526  * Link head to tail in subpath.
4527  */
4528 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4530     g_assert(!sp->closed);
4531     g_assert(sp->last != sp->first);
4532     g_assert(sp->first->code == NR_MOVETO);
4534     sp->closed = TRUE;
4536     //Link the head to the tail
4537     sp->first->p.other = sp->last;
4538     sp->last->n.other  = sp->first;
4539     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4540     sp->first          = sp->last;
4542     //Remove the extra end node
4543     sp_nodepath_node_destroy(sp->last->n.other);
4546 /**
4547  * Open closed (loopy) subpath at node.
4548  */
4549 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4551     g_assert(sp->closed);
4552     g_assert(n->subpath == sp);
4553     g_assert(sp->first == sp->last);
4555     /* We create new startpoint, current node will become last one */
4557    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4558                                                 &n->pos, &n->pos, &n->n.pos);
4561     sp->closed        = FALSE;
4563     //Unlink to make a head and tail
4564     sp->first         = new_path;
4565     sp->last          = n;
4566     n->n.other        = NULL;
4567     new_path->p.other = NULL;
4570 /**
4571  * Return new node in subpath with given properties.
4572  * \param pos Position of node.
4573  * \param ppos Handle position in previous direction
4574  * \param npos Handle position in previous direction
4575  */
4576 Inkscape::NodePath::Node *
4577 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)
4579     g_assert(sp);
4580     g_assert(sp->nodepath);
4581     g_assert(sp->nodepath->desktop);
4583     if (nodechunk == NULL)
4584         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4586     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4588     n->subpath  = sp;
4590     if (type != Inkscape::NodePath::NODE_NONE) {
4591         // use the type from sodipodi:nodetypes
4592         n->type = type;
4593     } else {
4594         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4595             // points are (almost) collinear
4596             if (Geom::L2(*pos - *ppos) < 1e-6 || Geom::L2(*pos - *npos) < 1e-6) {
4597                 // endnode, or a node with a retracted handle
4598                 n->type = Inkscape::NodePath::NODE_CUSP;
4599             } else {
4600                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4601             }
4602         } else {
4603             n->type = Inkscape::NodePath::NODE_CUSP;
4604         }
4605     }
4607     n->code     = code;
4608     n->selected = FALSE;
4609     n->pos      = *pos;
4610     n->p.pos    = *ppos;
4611     n->n.pos    = *npos;
4612     
4613     n->dragging_out = NULL;
4615     Inkscape::NodePath::Node *prev;
4616     if (next) {
4617         //g_assert(g_list_find(sp->nodes, next));
4618         prev = next->p.other;
4619     } else {
4620         prev = sp->last;
4621     }
4623     if (prev)
4624         prev->n.other = n;
4625     else
4626         sp->first = n;
4628     if (next)
4629         next->p.other = n;
4630     else
4631         sp->last = n;
4633     n->p.other = prev;
4634     n->n.other = next;
4636     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"));
4637     sp_knot_set_position(n->knot, *pos, 0);
4639     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4640     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4641     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4643     sp_nodepath_update_node_knot(n);
4645     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4646     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4647     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4648     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4649     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4650     sp_knot_show(n->knot);
4652     // We only create handle knots and lines on demand
4653     n->p.knot = NULL;
4654     n->p.line = NULL;
4655     n->n.knot = NULL;
4656     n->n.line = NULL;
4658     sp->nodes = g_list_prepend(sp->nodes, n);
4660     return n;
4663 /**
4664  * Destroy node and its knots, link neighbors in subpath.
4665  */
4666 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4668     g_assert(node);
4669     g_assert(node->subpath);
4670     g_assert(SP_IS_KNOT(node->knot));
4672    Inkscape::NodePath::SubPath *sp = node->subpath;
4674     if (node->selected) { // first, deselect
4675         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4676         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4677     }
4679     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4681     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4682     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4683     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4684     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4685     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4686     g_object_unref(G_OBJECT(node->knot));
4688     if (node->p.knot) {
4689         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4690         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4691         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4692         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4693         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4694         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4695         g_object_unref(G_OBJECT(node->p.knot));
4696         node->p.knot = NULL;
4697     }
4699     if (node->n.knot) {
4700         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4701         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4702         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4703         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4704         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4705         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4706         g_object_unref(G_OBJECT(node->n.knot));
4707         node->n.knot = NULL;
4708     }
4710     if (node->p.line)
4711         gtk_object_destroy(GTK_OBJECT(node->p.line));
4712     if (node->n.line)
4713         gtk_object_destroy(GTK_OBJECT(node->n.line));
4715     if (sp->nodes) { // there are others nodes on the subpath
4716         if (sp->closed) {
4717             if (sp->first == node) {
4718                 g_assert(sp->last == node);
4719                 sp->first = node->n.other;
4720                 sp->last = sp->first;
4721             }
4722             node->p.other->n.other = node->n.other;
4723             node->n.other->p.other = node->p.other;
4724         } else {
4725             if (sp->first == node) {
4726                 sp->first = node->n.other;
4727                 sp->first->code = NR_MOVETO;
4728             }
4729             if (sp->last == node) sp->last = node->p.other;
4730             if (node->p.other) node->p.other->n.other = node->n.other;
4731             if (node->n.other) node->n.other->p.other = node->p.other;
4732         }
4733     } else { // this was the last node on subpath
4734         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4735     }
4737     g_mem_chunk_free(nodechunk, node);
4740 /**
4741  * Returns one of the node's two sides.
4742  * \param which Indicates which side.
4743  * \return Pointer to previous node side if which==-1, next if which==1.
4744  */
4745 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4747     g_assert(node);
4748     Inkscape::NodePath::NodeSide * result = 0;
4749     switch (which) {
4750         case -1:
4751             result = &node->p;
4752             break;
4753         case 1:
4754             result = &node->n;
4755             break;
4756         default:
4757             g_assert_not_reached();
4758     }
4760     return result;
4763 /**
4764  * Return the other side of the node, given one of its sides.
4765  */
4766 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4768     g_assert(node);
4769     Inkscape::NodePath::NodeSide *result = 0;
4771     if (me == &node->p) {
4772         result = &node->n;
4773     } else if (me == &node->n) {
4774         result = &node->p;
4775     } else {
4776         g_assert_not_reached();
4777     }
4779     return result;
4782 /**
4783  * Return NRPathcode on the given side of the node.
4784  */
4785 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4787     g_assert(node);
4789     NRPathcode result = NR_END;
4790     if (me == &node->p) {
4791         if (node->p.other) {
4792             result = (NRPathcode)node->code;
4793         } else {
4794             result = NR_MOVETO;
4795         }
4796     } else if (me == &node->n) {
4797         if (node->n.other) {
4798             result = (NRPathcode)node->n.other->code;
4799         } else {
4800             result = NR_MOVETO;
4801         }
4802     } else {
4803         g_assert_not_reached();
4804     }
4806     return result;
4809 /**
4810  * Return node with the given index
4811  */
4812 Inkscape::NodePath::Node *
4813 sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index)
4815     Inkscape::NodePath::Node *e = NULL;
4817     if (!nodepath) {
4818         return e;
4819     }
4821     //find segment
4822     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4824         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4825         int n = g_list_length(sp->nodes);
4826         if (sp->closed) {
4827             n++;
4828         }
4830         //if the piece belongs to this subpath grab it
4831         //otherwise move onto the next subpath
4832         if (index < n) {
4833             e = sp->first;
4834             for (int i = 0; i < index; ++i) {
4835                 e = e->n.other;
4836             }
4837             break;
4838         } else {
4839             if (sp->closed) {
4840                 index -= (n+1);
4841             } else {
4842                 index -= n;
4843             }
4844         }
4845     }
4847     return e;
4850 /**
4851  * Returns plain text meaning of node type.
4852  */
4853 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4855     unsigned retracted = 0;
4856     bool endnode = false;
4858     for (int which = -1; which <= 1; which += 2) {
4859         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4860         if (side->other && Geom::L2(side->pos - node->pos) < 1e-6)
4861             retracted ++;
4862         if (!side->other)
4863             endnode = true;
4864     }
4866     if (retracted == 0) {
4867         if (endnode) {
4868                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4869                 return _("end node");
4870         } else {
4871             switch (node->type) {
4872                 case Inkscape::NodePath::NODE_CUSP:
4873                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4874                     return _("cusp");
4875                 case Inkscape::NodePath::NODE_SMOOTH:
4876                     // TRANSLATORS: "smooth" is an adjective here
4877                     return _("smooth");
4878                 case Inkscape::NodePath::NODE_AUTO:
4879                     return _("auto");
4880                 case Inkscape::NodePath::NODE_SYMM:
4881                     return _("symmetric");
4882             }
4883         }
4884     } else if (retracted == 1) {
4885         if (endnode) {
4886             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4887             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4888         } else {
4889             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4890         }
4891     } else {
4892         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4893     }
4895     return NULL;
4898 /**
4899  * Handles content of statusbar as long as node tool is active.
4900  */
4901 void
4902 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4904     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");
4905     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4907     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4908     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4909     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4910     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4912     SPDesktop *desktop = NULL;
4913     if (nodepath) {
4914         desktop = nodepath->desktop;
4915     } else {
4916         desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above
4917     }
4919     SPEventContext *ec = desktop->event_context;
4920     if (!ec) return;
4922     Inkscape::MessageContext *mc = get_message_context(ec);
4923     if (!mc) return;
4925     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4927     if (selected_nodes == 0) {
4928         Inkscape::Selection *sel = desktop->selection;
4929         if (!sel || sel->isEmpty()) {
4930             mc->setF(Inkscape::NORMAL_MESSAGE,
4931                      _("Select a single object to edit its nodes or handles."));
4932         } else {
4933             if (nodepath) {
4934             mc->setF(Inkscape::NORMAL_MESSAGE,
4935                      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.",
4936                               "<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.",
4937                               total_nodes),
4938                      total_nodes);
4939             } else {
4940                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4941                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4942                 } else {
4943                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4944                 }
4945             }
4946         }
4947     } else if (nodepath && selected_nodes == 1) {
4948         mc->setF(Inkscape::NORMAL_MESSAGE,
4949                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4950                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4951                           total_nodes),
4952                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4953     } else {
4954         if (selected_subpaths > 1) {
4955             mc->setF(Inkscape::NORMAL_MESSAGE,
4956                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4957                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4958                               total_nodes),
4959                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4960         } else {
4961             mc->setF(Inkscape::NORMAL_MESSAGE,
4962                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4963                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4964                               total_nodes),
4965                      selected_nodes, total_nodes, when_selected);
4966         }
4967     }
4970 /*
4971  * returns a *copy* of the curve of that object.
4972  */
4973 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4974     if (!object)
4975         return NULL;
4977     SPCurve *curve = NULL;
4978     if (SP_IS_PATH(object)) {
4979         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4980         curve = curve_new->copy();
4981     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4982         const gchar *svgd = object->repr->attribute(key);
4983         if (svgd) {
4984             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4985             SPCurve *curve_new = new SPCurve(pv);
4986             if (curve_new) {
4987                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4988             }
4989         }
4990     }
4992     return curve;
4995 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4996     if (!np || !np->object || !curve)
4997         return;
4999     if (SP_IS_PATH(np->object)) {
5000         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
5001             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
5002         } else {
5003             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
5004         }
5005     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
5006         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(np->object)->get_lpe();
5007         if (lpe) {
5008             Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( lpe->getParameter(np->repr_key) );
5009             if (pathparam) {
5010                 pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
5011                 np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
5012             }
5013         }
5014     }
5017 /*
5018 SPCanvasItem *
5019 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
5020     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
5022 */
5024 /*
5025 SPCanvasItem *
5026 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) {
5027     SPCurve *flash_curve = curve->copy();
5028     Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity();
5029     flash_curve->transform(i2d);
5030     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
5031     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
5032     // unless we also flash the nodes...
5033     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
5034     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
5035     sp_canvas_item_show(canvasitem);
5036     flash_curve->unref();
5037     return canvasitem;
5040 SPCanvasItem *
5041 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) {
5042     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5043     return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path),
5044                                            prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff));
5046 */
5048 SPCanvasItem *
5049 sp_nodepath_helperpath_from_path(SPDesktop *desktop, SPPath *path) {
5050     SPCurve *flash_curve = sp_path_get_curve_for_edit(path)->copy();
5051     Geom::Matrix i2d = sp_item_i2d_affine(SP_ITEM(path));
5052     flash_curve->transform(i2d);
5053     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
5054     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
5055     // unless we also flash the nodes...
5056     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5057     guint32 color = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
5058     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
5059     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
5060     sp_canvas_item_show(canvasitem);
5061     flash_curve->unref();
5062     return canvasitem;
5065 // TODO: Merge this with sp_nodepath_make_helper_item()!
5066 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
5067     np->show_helperpath = show;
5069     if (show) {
5070         SPCurve *helper_curve = np->curve->copy();
5071         helper_curve->transform(np->i2d);
5072         if (!np->helper_path) {
5073             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
5075             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
5076             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);
5077             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
5078             sp_canvas_item_move_to_z(np->helper_path, 0);
5079             sp_canvas_item_show(np->helper_path);
5080         } else {
5081             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
5082         }
5083         helper_curve->unref();
5084     } else {
5085         if (np->helper_path) {
5086             GtkObject *temp = np->helper_path;
5087             np->helper_path = NULL;
5088             gtk_object_destroy(temp);
5089         }
5090     }
5093 /* sp_nodepath_make_straight_path:
5094  *   Prevents user from curving the path by dragging a segment or activating handles etc.
5095  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
5096  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
5097  */
5098 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
5099     np->straight_path = true;
5100     np->show_handles = false;
5101     g_message("add code to make the path straight.");
5102     // do sp_nodepath_convert_node_type on all nodes?
5103     // coding tip: search for this text : "Make selected segments lines"
5106 /*
5107   Local Variables:
5108   mode:c++
5109   c-file-style:"stroustrup"
5110   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5111   indent-tabs-mode:nil
5112   fill-column:99
5113   End:
5114 */
5115 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :