Code

improve helperpath generation on mouse-over
[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 <2geom/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 &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     }
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;
1068             start->n.pos = start->pos;
1069             end->p.pos = end->pos;
1071             sp_node_adjust_handle(start, -1);
1072             sp_node_adjust_handle(end, 1);
1074         } else {
1075             Geom::Point delta = end->pos - start->pos;
1076             start->n.pos = start->pos + delta / 3;
1077             end->p.pos = end->pos - delta / 3;
1078             sp_node_adjust_handle(start, 1);
1079             sp_node_adjust_handle(end, -1);
1080         }
1082         sp_node_update_handles(start);
1083         sp_node_update_handles(end);
1084     }
1087 static void
1088 sp_nodepath_update_node_knot(Inkscape::NodePath::Node *node)
1090     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1091         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1092         node->knot->setSize (node->selected? 11 : 9);
1093         sp_knot_update_ctrl(node->knot);
1094     } else if (node->type == Inkscape::NodePath::NODE_AUTO) {
1095         node->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1096         node->knot->setSize (node->selected? 11 : 9);
1097         sp_knot_update_ctrl(node->knot);
1098     } else {
1099         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1100         node->knot->setSize (node->selected? 9 : 7);
1101         sp_knot_update_ctrl(node->knot);
1102     }
1106 /**
1107  * Change node type, and its handles accordingly.
1108  */
1109 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1111     g_assert(node);
1112     g_assert(node->subpath);
1114     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1115         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1116             type =Inkscape::NodePath::NODE_CUSP;
1117         }
1118     }
1120     node->type = type;
1122     sp_nodepath_update_node_knot(node);
1124     // if one of handles is mouseovered, preserve its position
1125     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1126         sp_node_adjust_handle(node, 1);
1127     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1128         sp_node_adjust_handle(node, -1);
1129     } else {
1130         sp_node_adjust_handles(node);
1131     }
1133     sp_node_update_handles(node);
1135     sp_nodepath_update_statusbar(node->subpath->nodepath);
1137     return node;
1140 bool
1141 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1143 // TODO clean up multiple returns
1144         Inkscape::NodePath::Node *othernode = side->other;
1145         if (!othernode)
1146             return false;
1147         NRPathcode const code = sp_node_path_code_from_side(node, side);
1148         if (code == NR_LINETO)
1149             return true;
1150         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1151         if (&node->p == side) {
1152             other_to_me = &othernode->n;
1153         } else if (&node->n == side) {
1154             other_to_me = &othernode->p;
1155         }
1156         if (!other_to_me)
1157             return false;
1158         bool is_line =
1159              (Geom::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1160               Geom::L2(node->pos - side->pos) < 1e-6);
1161         return is_line;
1164 /**
1165  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1166  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1167  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too.
1168  * If already cusp and set to cusp, retracts handles.
1169 */
1170 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1172     if (type == Inkscape::NodePath::NODE_AUTO) {
1173         if (node->p.other != NULL)
1174             node->code = NR_CURVETO;
1175         if (node->n.other != NULL)
1176             node->n.other->code = NR_CURVETO;
1177     }
1179     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1181 /*
1182   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1184         if (two_handles) {
1185             // do nothing, adjust_handles called via set_node_type will line them up
1186         } else if (one_handle) {
1187             if (opposite_to_handle_is_line) {
1188                 if (lined_up) {
1189                     // already half-smooth; pull opposite handle too making it fully smooth
1190                 } else {
1191                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1192                 }
1193             } else {
1194                 // pull opposite handle in line with the existing one
1195             }
1196         } else if (no_handles) {
1197             if (both_segments_are_lines OR both_segments_are_curves) {
1198                 //pull both handles
1199             } else {
1200                 // pull the handle opposite to line segment, making node half-smooth
1201             }
1202         }
1203 */
1204         bool p_has_handle = (Geom::L2(node->pos  - node->p.pos) > 1e-6);
1205         bool n_has_handle = (Geom::L2(node->pos  - node->n.pos) > 1e-6);
1206         bool p_is_line = sp_node_side_is_line(node, &node->p);
1207         bool n_is_line = sp_node_side_is_line(node, &node->n);
1209         if (p_has_handle && n_has_handle) {
1210             // do nothing, adjust_handles will line them up
1211         } else if (p_has_handle || n_has_handle) {
1212             if (p_has_handle && n_is_line) {
1213                 Radial line (node->n.other->pos - node->pos);
1214                 Radial handle (node->pos - node->p.pos);
1215                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1216                     // already half-smooth; pull opposite handle too making it fully smooth
1217                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1218                 } else {
1219                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1220                 }
1221             } else if (n_has_handle && p_is_line) {
1222                 Radial line (node->p.other->pos - node->pos);
1223                 Radial handle (node->pos - node->n.pos);
1224                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1225                     // already half-smooth; pull opposite handle too making it fully smooth
1226                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1227                 } else {
1228                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1229                 }
1230             } else if (p_has_handle && node->n.other) {
1231                 // pull n handle
1232                 node->n.other->code = NR_CURVETO;
1233                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1234                     Geom::L2(node->p.pos - node->pos) :
1235                     Geom::L2(node->n.other->pos - node->pos) / 3;
1236                 node->n.pos = node->pos - (len / Geom::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1237             } else if (n_has_handle && node->p.other) {
1238                 // pull p handle
1239                 node->code = NR_CURVETO;
1240                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1241                     Geom::L2(node->n.pos - node->pos) :
1242                     Geom::L2(node->p.other->pos - node->pos) / 3;
1243                 node->p.pos = node->pos - (len / Geom::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1244             }
1245         } else if (!p_has_handle && !n_has_handle) {
1246             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1247                 // no handles, but both segments are either lnes or curves:
1248                 //pull both handles
1250                 // convert both to curves:
1251                 node->code = NR_CURVETO;
1252                 node->n.other->code = NR_CURVETO;
1254                 sp_node_adjust_handles_auto(node);
1255             } else {
1256                 // pull the handle opposite to line segment, making it half-smooth
1257                 if (p_is_line && node->n.other) {
1258                     if (type != Inkscape::NodePath::NODE_SYMM) {
1259                         // pull n handle
1260                         node->n.other->code = NR_CURVETO;
1261                         double len =  Geom::L2(node->n.other->pos - node->pos) / 3;
1262                         node->n.pos = node->pos + (len / Geom::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1263                     }
1264                 } else if (n_is_line && node->p.other) {
1265                     if (type != Inkscape::NodePath::NODE_SYMM) {
1266                         // pull p handle
1267                         node->code = NR_CURVETO;
1268                         double len =  Geom::L2(node->p.other->pos - node->pos) / 3;
1269                         node->p.pos = node->pos + (len / Geom::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1270                     }
1271                 }
1272             }
1273         }
1274     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1275         // cusping a cusp: retract nodes
1276         node->p.pos = node->pos;
1277         node->n.pos = node->pos;
1278     }
1280     sp_nodepath_set_node_type (node, type);
1283 /**
1284  * Move node to point, and adjust its and neighbouring handles.
1285  */
1286 void sp_node_moveto(Inkscape::NodePath::Node *node, Geom::Point p)
1288     if (node->type == Inkscape::NodePath::NODE_AUTO) {
1289         node->pos = p;
1290         sp_node_adjust_handles_auto(node);
1291     } else {
1292         Geom::Point delta = p - node->pos;
1293         node->pos = p;
1295         node->p.pos += delta;
1296         node->n.pos += delta;
1297     }
1299     Inkscape::NodePath::Node *node_p = NULL;
1300     Inkscape::NodePath::Node *node_n = NULL;
1302     if (node->p.other) {
1303         if (node->code == NR_LINETO) {
1304             sp_node_adjust_handle(node, 1);
1305             sp_node_adjust_handle(node->p.other, -1);
1306             node_p = node->p.other;
1307         }
1308         if (!node->p.other->selected && node->p.other->type == Inkscape::NodePath::NODE_AUTO) {
1309             sp_node_adjust_handles_auto(node->p.other);
1310             node_p = node->p.other;
1311         }
1312     }
1313     if (node->n.other) {
1314         if (node->n.other->code == NR_LINETO) {
1315             sp_node_adjust_handle(node, -1);
1316             sp_node_adjust_handle(node->n.other, 1);
1317             node_n = node->n.other;
1318         }
1319         if (!node->n.other->selected && node->n.other->type == Inkscape::NodePath::NODE_AUTO) {
1320             sp_node_adjust_handles_auto(node->n.other);
1321             node_n = node->n.other;
1322         }
1323     }
1325     // this function is only called from batch movers that will update display at the end
1326     // themselves, so here we just move all the knots without emitting move signals, for speed
1327     sp_node_update_handles(node, false);
1328     if (node_n) {
1329         sp_node_update_handles(node_n, false);
1330     }
1331     if (node_p) {
1332         sp_node_update_handles(node_p, false);
1333     }
1336 /**
1337  * Call sp_node_moveto() for node selection and handle possible snapping.
1338  */
1339 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, Geom::Coord dx, Geom::Coord dy,
1340                                             bool const snap, bool constrained = false,
1341                                             Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point())
1343     Geom::Point delta(dx, dy);
1344     Geom::Point best_pt = delta;
1345     Inkscape::SnappedPoint best;
1347     if (snap) {
1348         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1349          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we
1350          * must provide that information. */
1352         // Build a list of the unselected nodes to which the snapper should snap
1353         std::vector<std::pair<Geom::Point, int> > unselected_nodes;
1354         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1355             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1356             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1357                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1358                 if (!node->selected) {
1359                     unselected_nodes.push_back(std::make_pair(to_2geom(node->pos), node->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPTARGET_NODE_SMOOTH : Inkscape::SNAPTARGET_NODE_CUSP));
1360                 }
1361             }
1362         }
1364         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1366         // When only the node closest to the mouse pointer is to be snapped
1367         // then we will not even try to snap to other points and discard those immediately
1368         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1369         bool closest_only = prefs->getBool("/options/snapclosestonly/value", false);
1371         Inkscape::NodePath::Node *closest_node = NULL;
1372         Geom::Coord closest_dist = NR_HUGE;
1374         if (closest_only) {
1375                 for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1376                         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1377                         Geom::Coord dist = Geom::L2(nodepath->drag_origin_mouse - n->origin);
1378                         if (dist < closest_dist) {
1379                                 closest_node = n;
1380                                 closest_dist = dist;
1381                         }
1382                 }
1383         }
1385         // Iterate through all selected nodes
1386         m.setup(nodepath->desktop, false, SP_PATH(nodepath->item), &unselected_nodes);
1387         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1388             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1389             if (!closest_only || n == closest_node) { //try to snap either all selected nodes or only the closest one
1390                     Inkscape::SnappedPoint s;
1391                     Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP);
1392                     if (constrained) {
1393                         Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1394                         dedicated_constraint.setPoint(n->pos);
1395                         s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type, dedicated_constraint);
1396                     } else {
1397                         s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type);
1398                     }
1400                     if (s.getSnapped()) {
1401                         s.setPointerDistance(Geom::L2(nodepath->drag_origin_mouse - n->origin));
1402                         if (!s.isOtherSnapBetter(best, true)) {
1403                                 best = s;
1404                                 best_pt = from_2geom(s.getPoint()) - n->pos;
1405                         }
1406                     }
1407             }
1408         }
1410         if (best.getSnapped()) {
1411             nodepath->desktop->snapindicator->set_new_snaptarget(best);
1412         } else {
1413             nodepath->desktop->snapindicator->remove_snaptarget();
1414         }
1415     }
1417     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1418         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1419         sp_node_moveto(n, n->pos + best_pt);
1420     }
1422     // do not update repr here so that node dragging is acceptably fast
1423     update_object(nodepath);
1426 /**
1427 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1428 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1429 near x = 0.
1430  */
1431 double
1432 sculpt_profile (double x, double alpha, guint profile)
1434     double result = 1;
1436     if (x >= 1) {
1437         result = 0;
1438     } else if (x <= 0) {
1439         result = 1;
1440     } else {
1441         switch (profile) {
1442             case SCULPT_PROFILE_LINEAR:
1443                 result = 1 - x;
1444                 break;
1445             case SCULPT_PROFILE_BELL:
1446                 result = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1447                 break;
1448             case SCULPT_PROFILE_ELLIPTIC:
1449                 result = sqrt(1 - x*x);
1450                 break;
1451             default:
1452                 g_assert_not_reached();
1453         }
1454     }
1456     return result;
1459 double
1460 bezier_length (Geom::Point a, Geom::Point ah, Geom::Point bh, Geom::Point b)
1462     // extremely primitive for now, don't have time to look for the real one
1463     double lower = Geom::L2(b - a);
1464     double upper = Geom::L2(ah - a) + Geom::L2(bh - ah) + Geom::L2(bh - b);
1465     return (lower + upper)/2;
1468 void
1469 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, Geom::Point delta, Geom::Point delta_n, Geom::Point delta_p)
1471     n->pos = n->origin + delta;
1472     n->n.pos = n->n.origin + delta_n;
1473     n->p.pos = n->p.origin + delta_p;
1474     sp_node_adjust_handles(n);
1475     sp_node_update_handles(n, false);
1478 /**
1479  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1480  * on how far they are from the dragged node n.
1481  */
1482 static void
1483 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, Geom::Point delta)
1485     g_assert (n);
1486     g_assert (nodepath);
1487     g_assert (n->subpath->nodepath == nodepath);
1489     double pressure = n->knot->pressure;
1490     if (pressure == 0)
1491         pressure = 0.5; // default
1492     pressure = CLAMP (pressure, 0.2, 0.8);
1494     // map pressure to alpha = 1/5 ... 5
1495     double alpha = 1 - 2 * fabs(pressure - 0.5);
1496     if (pressure > 0.5)
1497         alpha = 1/alpha;
1499     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1500     guint profile = prefs->getInt("/tools/nodes/sculpting_profile", SCULPT_PROFILE_BELL);
1502     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1503         // Only one subpath has selected nodes:
1504         // use linear mode, where the distance from n to node being dragged is calculated along the path
1506         double n_sel_range = 0, p_sel_range = 0;
1507         guint n_nodes = 0, p_nodes = 0;
1508         guint n_sel_nodes = 0, p_sel_nodes = 0;
1510         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1511         {
1512             double n_range = 0, p_range = 0;
1513             bool n_going = true, p_going = true;
1514             Inkscape::NodePath::Node *n_node = n;
1515             Inkscape::NodePath::Node *p_node = n;
1516             do {
1517                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1518                 if (n_node && n_going)
1519                     n_node = n_node->n.other;
1520                 if (n_node == NULL) {
1521                     n_going = false;
1522                 } else {
1523                     n_nodes ++;
1524                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1525                     if (n_node->selected) {
1526                         n_sel_nodes ++;
1527                         n_sel_range = n_range;
1528                     }
1529                     if (n_node == p_node) {
1530                         n_going = false;
1531                         p_going = false;
1532                     }
1533                 }
1534                 if (p_node && p_going)
1535                     p_node = p_node->p.other;
1536                 if (p_node == NULL) {
1537                     p_going = false;
1538                 } else {
1539                     p_nodes ++;
1540                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1541                     if (p_node->selected) {
1542                         p_sel_nodes ++;
1543                         p_sel_range = p_range;
1544                     }
1545                     if (p_node == n_node) {
1546                         n_going = false;
1547                         p_going = false;
1548                     }
1549                 }
1550             } while (n_going || p_going);
1551         }
1553         // Second pass: actually move nodes in this subpath
1554         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1555         {
1556             double n_range = 0, p_range = 0;
1557             bool n_going = true, p_going = true;
1558             Inkscape::NodePath::Node *n_node = n;
1559             Inkscape::NodePath::Node *p_node = n;
1560             do {
1561                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1562                 if (n_node && n_going)
1563                     n_node = n_node->n.other;
1564                 if (n_node == NULL) {
1565                     n_going = false;
1566                 } else {
1567                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1568                     if (n_node->selected) {
1569                         sp_nodepath_move_node_and_handles (n_node,
1570                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1571                                                            sculpt_profile ((n_range + Geom::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1572                                                            sculpt_profile ((n_range - Geom::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1573                     }
1574                     if (n_node == p_node) {
1575                         n_going = false;
1576                         p_going = false;
1577                     }
1578                 }
1579                 if (p_node && p_going)
1580                     p_node = p_node->p.other;
1581                 if (p_node == NULL) {
1582                     p_going = false;
1583                 } else {
1584                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1585                     if (p_node->selected) {
1586                         sp_nodepath_move_node_and_handles (p_node,
1587                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1588                                                            sculpt_profile ((p_range - Geom::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1589                                                            sculpt_profile ((p_range + Geom::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1590                     }
1591                     if (p_node == n_node) {
1592                         n_going = false;
1593                         p_going = false;
1594                     }
1595                 }
1596             } while (n_going || p_going);
1597         }
1599     } else {
1600         // Multiple subpaths have selected nodes:
1601         // use spatial mode, where the distance from n to node being dragged is measured directly as Geom::L2.
1602         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1603         // fix the pear-like shape when sculpting e.g. a ring
1605         // First pass: calculate range
1606         gdouble direct_range = 0;
1607         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1608             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1609             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1610                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1611                 if (node->selected) {
1612                     direct_range = MAX(direct_range, Geom::L2(node->origin - n->origin));
1613                 }
1614             }
1615         }
1617         // Second pass: actually move nodes
1618         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1619             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1620             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1621                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1622                 if (node->selected) {
1623                     if (direct_range > 1e-6) {
1624                         sp_nodepath_move_node_and_handles (node,
1625                                                        sculpt_profile (Geom::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1626                                                        sculpt_profile (Geom::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1627                                                        sculpt_profile (Geom::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1628                     } else {
1629                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1630                     }
1632                 }
1633             }
1634         }
1635     }
1637     // do not update repr here so that node dragging is acceptably fast
1638     update_object(nodepath);
1642 /**
1643  * Move node selection to point, adjust its and neighbouring handles,
1644  * handle possible snapping, and commit the change with possible undo.
1645  */
1646 void
1647 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1649     if (!nodepath) return;
1651     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1653     if (dx == 0) {
1654         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1655     } else if (dy == 0) {
1656         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1657     } else {
1658         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1659     }
1662 /**
1663  * Move node selection off screen and commit the change.
1664  */
1665 void
1666 sp_node_selected_move_screen(SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1668     // borrowed from sp_selection_move_screen in selection-chemistry.c
1669     // we find out the current zoom factor and divide deltas by it
1671     gdouble zoom = desktop->current_zoom();
1672     gdouble zdx = dx / zoom;
1673     gdouble zdy = dy / zoom;
1675     if (!nodepath) return;
1677     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1679     if (dx == 0) {
1680         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1681     } else if (dy == 0) {
1682         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1683     } else {
1684         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1685     }
1688 /**
1689  * Move selected nodes to the absolute position given
1690  */
1691 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis)
1693     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1694         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1695         Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]);
1696         sp_node_moveto(n, npos);
1697     }
1699     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1702 /**
1703  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return Geom::Nothing
1704  */
1705 boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1707     boost::optional<Geom::Coord> no_coord;
1708     g_return_val_if_fail(nodepath->selected, no_coord);
1710     // determine coordinate of first selected node
1711     GList *nsel = nodepath->selected;
1712     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1713     Geom::Coord coord = n->pos[axis];
1714     bool coincide = true;
1716     // compare it to the coordinates of all the other selected nodes
1717     for (GList *l = nsel->next; l != NULL; l = l->next) {
1718         n = (Inkscape::NodePath::Node *) l->data;
1719         if (n->pos[axis] != coord) {
1720             coincide = false;
1721         }
1722     }
1723     if (coincide) {
1724         return coord;
1725     } else {
1726         Geom::Rect bbox = sp_node_selected_bbox(nodepath);
1727         // currently we return the coordinate of the bounding box midpoint because I don't know how
1728         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1729         return bbox.midpoint()[axis];
1730     }
1733 /** If they don't yet exist, creates knot and line for the given side of the node */
1734 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1736     if (!side->knot) {
1737         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"));
1739         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1740         side->knot->setSize (7);
1741         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1742         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1743         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1744         sp_knot_update_ctrl(side->knot);
1746         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1747         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1748         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1749         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1750         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1751         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1752     }
1754     if (!side->line) {
1755         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1756                                         SP_TYPE_CTRLLINE, NULL);
1757     }
1760 /**
1761  * Ensure the given handle of the node is visible/invisible, update its screen position
1762  */
1763 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1765     g_assert(node != NULL);
1767    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1768     NRPathcode code = sp_node_path_code_from_side(node, side);
1770     show_handle = show_handle && (code == NR_CURVETO) && (Geom::L2(side->pos - node->pos) > 1e-6);
1772     if (show_handle) {
1773         if (!side->knot) { // No handle knot at all
1774             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1775             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1776             side->knot->pos = side->pos;
1777             if (side->knot->item)
1778                 SP_CTRL(side->knot->item)->moveto(side->pos);
1779             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1780             sp_knot_show(side->knot);
1781         } else {
1782             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1783                 if (fire_move_signals) {
1784                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1785                 } else {
1786                     sp_knot_moveto(side->knot, side->pos);
1787                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1788                 }
1789             }
1790             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1791                 sp_knot_show(side->knot);
1792             }
1793         }
1794         sp_canvas_item_show(side->line);
1795     } else {
1796         if (side->knot) {
1797             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1798                 sp_knot_hide(side->knot);
1799             }
1800         }
1801         if (side->line) {
1802             sp_canvas_item_hide(side->line);
1803         }
1804     }
1807 /**
1808  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1809  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1810  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1811  * updated; otherwise, just move the knots silently (used in batch moves).
1812  */
1813 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1815     g_assert(node != NULL);
1817     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1818         sp_knot_show(node->knot);
1819     }
1821     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1822         if (fire_move_signals)
1823             sp_knot_set_position(node->knot, node->pos, 0);
1824         else
1825             sp_knot_moveto(node->knot, node->pos);
1826     }
1828     gboolean show_handles = node->selected;
1829     if (node->p.other != NULL) {
1830         if (node->p.other->selected) show_handles = TRUE;
1831     }
1832     if (node->n.other != NULL) {
1833         if (node->n.other->selected) show_handles = TRUE;
1834     }
1836     if (node->subpath->nodepath->show_handles == false)
1837         show_handles = FALSE;
1839     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1840     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1843 /**
1844  * Call sp_node_update_handles() for all nodes on subpath.
1845  */
1846 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1848     g_assert(subpath != NULL);
1850     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1851         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1852     }
1855 /**
1856  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1857  */
1858 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1860     g_assert(nodepath != NULL);
1862     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1863         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1864     }
1867 void
1868 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1870     if (nodepath) {
1871         nodepath->show_handles = show;
1872         sp_nodepath_update_handles(nodepath);
1873     }
1876 /**
1877  * Adds all selected nodes in nodepath to list.
1878  */
1879 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1881     StlConv<Node *>::list(l, selected);
1882 /// \todo this adds a copying, rework when the selection becomes a stl list
1885 /**
1886  * Align selected nodes on the specified axis.
1887  */
1888 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1890     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1891         return;
1892     }
1894     if ( !nodepath->selected->next ) { // only one node selected
1895         return;
1896     }
1897    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1898     Geom::Point dest(pNode->pos);
1899     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1900         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1901         if (pNode) {
1902             dest[axis] = pNode->pos[axis];
1903             sp_node_moveto(pNode, dest);
1904         }
1905     }
1907     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1910 /// Helper struct.
1911 struct NodeSort
1913    Inkscape::NodePath::Node *_node;
1914     Geom::Coord _coord;
1915     /// \todo use vectorof pointers instead of calling copy ctor
1916     NodeSort(Inkscape::NodePath::Node *node, Geom::Dim2 axis) :
1917         _node(node), _coord(node->pos[axis])
1918     {}
1920 };
1922 static bool operator<(NodeSort const &a, NodeSort const &b)
1924     return (a._coord < b._coord);
1927 /**
1928  * Distribute selected nodes on the specified axis.
1929  */
1930 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1932     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1933         return;
1934     }
1936     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1937         return;
1938     }
1940    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1941     std::vector<NodeSort> sorted;
1942     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1943         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1944         if (pNode) {
1945             NodeSort n(pNode, axis);
1946             sorted.push_back(n);
1947             //dest[axis] = pNode->pos[axis];
1948             //sp_node_moveto(pNode, dest);
1949         }
1950     }
1951     std::sort(sorted.begin(), sorted.end());
1952     unsigned int len = sorted.size();
1953     //overall bboxes span
1954     float dist = (sorted.back()._coord -
1955                   sorted.front()._coord);
1956     //new distance between each bbox
1957     float step = (dist) / (len - 1);
1958     float pos = sorted.front()._coord;
1959     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1960           it < sorted.end();
1961           it ++ )
1962     {
1963         Geom::Point dest((*it)._node->pos);
1964         dest[axis] = pos;
1965         sp_node_moveto((*it)._node, dest);
1966         pos += step;
1967     }
1969     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1973 /**
1974  * Call sp_nodepath_line_add_node() for all selected segments.
1975  */
1976 void
1977 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1979     if (!nodepath) {
1980         return;
1981     }
1983     GList *nl = NULL;
1985     int n_added = 0;
1987     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1988        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1989         g_assert(t->selected);
1990         if (t->p.other && t->p.other->selected) {
1991             nl = g_list_prepend(nl, t);
1992         }
1993     }
1995     while (nl) {
1996        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1997        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1998        sp_nodepath_node_select(n, TRUE, FALSE);
1999        n_added ++;
2000        nl = g_list_remove(nl, t);
2001     }
2003     /** \todo fixme: adjust ? */
2004     sp_nodepath_update_handles(nodepath);
2006     if (n_added > 1) {
2007         sp_nodepath_update_repr(nodepath, _("Add nodes"));
2008     } else if (n_added > 0) {
2009         sp_nodepath_update_repr(nodepath, _("Add node"));
2010     }
2012     sp_nodepath_update_statusbar(nodepath);
2015 /**
2016  * Select segment nearest to point
2017  */
2018 void
2019 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p, bool toggle)
2021     if (!nodepath) {
2022         return;
2023     }
2025     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2026     Geom::PathVector const &pathv = curve->get_pathvector();
2027     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2028     if (!pvpos) {
2029         g_print ("Possible error?\n");
2030         return;
2031     }
2033     // calculate index for nodepath's representation.
2034     unsigned int segment_index = floor(pvpos->t) + 1;
2035     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2036         segment_index += pathv[i].size() + 1;
2037         if (pathv[i].closed()) {
2038             segment_index += 1;
2039         }
2040     }
2042     curve->unref();
2044     //find segment to segment
2045     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2047     //fixme: this can return NULL, so check before proceeding.
2048     g_return_if_fail(e != NULL);
2050     gboolean force = FALSE;
2051     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
2052         force = TRUE;
2053     }
2054     sp_nodepath_node_select(e, (gboolean) toggle, force);
2055     if (e->p.other)
2056         sp_nodepath_node_select(e->p.other, TRUE, force);
2058     sp_nodepath_update_handles(nodepath);
2060     sp_nodepath_update_statusbar(nodepath);
2063 /**
2064  * Add a node nearest to point
2065  */
2066 void
2067 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p)
2069     if (!nodepath) {
2070         return;
2071     }
2073     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2074     Geom::PathVector const &pathv = curve->get_pathvector();
2075     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2076     if (!pvpos) {
2077         g_print ("Possible error?\n");
2078         return;
2079     }
2081     // calculate index for nodepath's representation.
2082     double int_part;
2083     double t = std::modf(pvpos->t, &int_part);
2084     unsigned int segment_index = (unsigned int)int_part + 1;
2085     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2086         segment_index += pathv[i].size() + 1;
2087         if (pathv[i].closed()) {
2088             segment_index += 1;
2089         }
2090     }
2092     curve->unref();
2094     //find segment to split
2095     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2096     if (!e) {
2097         return;
2098     }
2100     //don't know why but t seems to flip for lines
2101     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2102         t = 1.0 - t;
2103     }
2105     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2106     sp_nodepath_node_select(n, FALSE, TRUE);
2108     /* fixme: adjust ? */
2109     sp_nodepath_update_handles(nodepath);
2111     sp_nodepath_update_repr(nodepath, _("Add node"));
2113     sp_nodepath_update_statusbar(nodepath);
2116 /*
2117  * Adjusts a segment so that t moves by a certain delta for dragging
2118  * converts lines to curves
2119  *
2120  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2121  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2122  */
2123 void
2124 sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, Geom::Point delta)
2126     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node);
2128     //fixme: e and e->p can be NULL, so check for those before proceeding
2129     g_return_if_fail(e != NULL);
2130     g_return_if_fail(&e->p != NULL);
2132     if (e->type == Inkscape::NodePath::NODE_AUTO) {
2133         e->type = Inkscape::NodePath::NODE_SMOOTH;
2134         sp_nodepath_update_node_knot (e);
2135     }
2136     if (e->p.other->type == Inkscape::NodePath::NODE_AUTO) {
2137         e->p.other->type = Inkscape::NodePath::NODE_SMOOTH;
2138         sp_nodepath_update_node_knot (e->p.other);
2139     }
2141     /* feel good is an arbitrary parameter that distributes the delta between handles
2142      * if t of the drag point is less than 1/6 distance form the endpoint only
2143      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2144      */
2145     double feel_good;
2146     if (t <= 1.0 / 6.0)
2147         feel_good = 0;
2148     else if (t <= 0.5)
2149         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2150     else if (t <= 5.0 / 6.0)
2151         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2152     else
2153         feel_good = 1;
2155     //if we're dragging a line convert it to a curve
2156     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2157         sp_nodepath_set_line_type(e, NR_CURVETO);
2158     }
2160     Geom::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2161     Geom::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2162     e->p.other->n.pos += offsetcoord0;
2163     e->p.pos += offsetcoord1;
2165     // adjust handles of adjacent nodes where necessary
2166     sp_node_adjust_handle(e,1);
2167     sp_node_adjust_handle(e->p.other,-1);
2169     sp_nodepath_update_handles(e->subpath->nodepath);
2171     update_object(e->subpath->nodepath);
2173     sp_nodepath_update_statusbar(e->subpath->nodepath);
2177 /**
2178  * Call sp_nodepath_break() for all selected segments.
2179  */
2180 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2182     if (!nodepath) return;
2184     GList *tempin = g_list_copy(nodepath->selected);
2185     GList *temp = NULL;
2186     for (GList *l = tempin; l != NULL; l = l->next) {
2187        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2188        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2189         if (nn == NULL) continue; // no break, no new node
2190         temp = g_list_prepend(temp, nn);
2191     }
2192     g_list_free(tempin);
2194     if (temp) {
2195         sp_nodepath_deselect(nodepath);
2196     }
2197     for (GList *l = temp; l != NULL; l = l->next) {
2198         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2199     }
2201     sp_nodepath_update_handles(nodepath);
2203     sp_nodepath_update_repr(nodepath, _("Break path"));
2206 /**
2207  * Duplicate the selected node(s).
2208  */
2209 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2211     if (!nodepath) {
2212         return;
2213     }
2215     GList *temp = NULL;
2216     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2217        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2218        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2219         if (nn == NULL) continue; // could not duplicate
2220         temp = g_list_prepend(temp, nn);
2221     }
2223     if (temp) {
2224         sp_nodepath_deselect(nodepath);
2225     }
2226     for (GList *l = temp; l != NULL; l = l->next) {
2227         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2228     }
2230     sp_nodepath_update_handles(nodepath);
2232     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2235 /**
2236  *  Internal function to join two nodes by merging them into one.
2237  */
2238 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2240     /* a and b are endpoints */
2242     // if one of the two nodes is mouseovered, fix its position
2243     Geom::Point c;
2244     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2245         c = a->pos;
2246     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2247         c = b->pos;
2248     } else {
2249         // otherwise, move joined node to the midpoint
2250         c = (a->pos + b->pos) / 2;
2251     }
2253     if (a->subpath == b->subpath) {
2254        Inkscape::NodePath::SubPath *sp = a->subpath;
2255         sp_nodepath_subpath_close(sp);
2256         sp_node_moveto (sp->first, c);
2258         sp_nodepath_update_handles(sp->nodepath);
2259         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2260         return;
2261     }
2263     /* a and b are separate subpaths */
2264     Inkscape::NodePath::SubPath *sa = a->subpath;
2265     Inkscape::NodePath::SubPath *sb = b->subpath;
2266     Geom::Point p;
2267     Inkscape::NodePath::Node *n;
2268     NRPathcode code;
2269     if (a == sa->first) {
2270         // we will now reverse sa, so that a is its last node, not first, and drop that node
2271         p = sa->first->n.pos;
2272         code = (NRPathcode)sa->first->n.other->code;
2273         // create new subpath
2274        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2275        // create a first moveto node on it
2276         n = sa->last;
2277         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2278         n = n->p.other;
2279         if (n == sa->first) n = NULL;
2280         while (n) {
2281             // copy the rest of the nodes from sa to t, going backwards
2282             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2283             n = n->p.other;
2284             if (n == sa->first) n = NULL;
2285         }
2286         // replace sa with t
2287         sp_nodepath_subpath_destroy(sa);
2288         sa = t;
2289     } else if (a == sa->last) {
2290         // a is already last, just drop it
2291         p = sa->last->p.pos;
2292         code = (NRPathcode)sa->last->code;
2293         sp_nodepath_node_destroy(sa->last);
2294     } else {
2295         code = NR_END;
2296         g_assert_not_reached();
2297     }
2299     if (b == sb->first) {
2300         // copy all nodes from b to a, forward
2301         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2302         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2303             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2304         }
2305     } else if (b == sb->last) {
2306         // copy all nodes from b to a, backward
2307         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2308         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2309             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2310         }
2311     } else {
2312         g_assert_not_reached();
2313     }
2314     /* and now destroy sb */
2316     sp_nodepath_subpath_destroy(sb);
2318     sp_nodepath_update_handles(sa->nodepath);
2320     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2322     sp_nodepath_update_statusbar(nodepath);
2325 /**
2326  *  Internal function to join two nodes by adding a segment between them.
2327  */
2328 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2330     if (a->subpath == b->subpath) {
2331        Inkscape::NodePath::SubPath *sp = a->subpath;
2333         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2334         sp->closed = TRUE;
2336         sp->first->p.other = sp->last;
2337         sp->last->n.other  = sp->first;
2339         sp_node_handle_mirror_p_to_n(sp->last);
2340         sp_node_handle_mirror_n_to_p(sp->first);
2342         sp->first->code = sp->last->code;
2343         sp->first       = sp->last;
2345         sp_nodepath_update_handles(sp->nodepath);
2347         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2349         return;
2350     }
2352     /* a and b are separate subpaths */
2353     Inkscape::NodePath::SubPath *sa = a->subpath;
2354     Inkscape::NodePath::SubPath *sb = b->subpath;
2356     Inkscape::NodePath::Node *n;
2357     Geom::Point p;
2358     NRPathcode code;
2359     if (a == sa->first) {
2360         code = (NRPathcode) sa->first->n.other->code;
2361        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2362         n = sa->last;
2363         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2364         for (n = n->p.other; n != NULL; n = n->p.other) {
2365             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2366         }
2367         sp_nodepath_subpath_destroy(sa);
2368         sa = t;
2369     } else if (a == sa->last) {
2370         code = (NRPathcode)sa->last->code;
2371     } else {
2372         code = NR_END;
2373         g_assert_not_reached();
2374     }
2376     if (b == sb->first) {
2377         n = sb->first;
2378         sp_node_handle_mirror_p_to_n(sa->last);
2379         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2380         sp_node_handle_mirror_n_to_p(sa->last);
2381         for (n = n->n.other; n != NULL; n = n->n.other) {
2382             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2383         }
2384     } else if (b == sb->last) {
2385         n = sb->last;
2386         sp_node_handle_mirror_p_to_n(sa->last);
2387         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2388         sp_node_handle_mirror_n_to_p(sa->last);
2389         for (n = n->p.other; n != NULL; n = n->p.other) {
2390             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2391         }
2392     } else {
2393         g_assert_not_reached();
2394     }
2395     /* and now destroy sb */
2397     sp_nodepath_subpath_destroy(sb);
2399     sp_nodepath_update_handles(sa->nodepath);
2401     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2404 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2406 /**
2407  * Internal function to handle joining two nodes.
2408  */
2409 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2411     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2413     if (g_list_length(nodepath->selected) != 2) {
2414         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2415         return;
2416     }
2418     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2419     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2421     g_assert(a != b);
2422     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2423         // someone tried to join an orphan node (i.e. a single-node subpath).
2424         // this is not worth an error message, just fail silently.
2425         return;
2426     }
2428     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2429         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2430         return;
2431     }
2433     switch(mode) {
2434         case NODE_JOIN_ENDPOINTS:
2435             do_node_selected_join(nodepath, a, b);
2436             break;
2437         case NODE_JOIN_SEGMENT:
2438             do_node_selected_join_segment(nodepath, a, b);
2439             break;
2440     }
2443 /**
2444  *  Join two nodes by merging them into one.
2445  */
2446 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2448     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2451 /**
2452  *  Join two nodes by adding a segment between them.
2453  */
2454 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2456     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2459 /**
2460  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2461  */
2462 void sp_node_delete_preserve(GList *nodes_to_delete)
2464     GSList *nodepaths = NULL;
2466     while (nodes_to_delete) {
2467         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2468         Inkscape::NodePath::SubPath *sp = node->subpath;
2469         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2470         Inkscape::NodePath::Node *sample_cursor = NULL;
2471         Inkscape::NodePath::Node *sample_end = NULL;
2472         Inkscape::NodePath::Node *delete_cursor = node;
2473         bool just_delete = false;
2475         //find the start of this contiguous selection
2476         //move left to the first node that is not selected
2477         //or the start of the non-closed path
2478         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2479             delete_cursor = curr;
2480         }
2482         //just delete at the beginning of an open path
2483         if (!delete_cursor->p.other) {
2484             sample_cursor = delete_cursor;
2485             just_delete = true;
2486         } else {
2487             sample_cursor = delete_cursor->p.other;
2488         }
2490         //calculate points for each segment
2491         int rate = 5;
2492         float period = 1.0 / rate;
2493         std::vector<Geom::Point> data;
2494         if (!just_delete) {
2495             data.push_back(sample_cursor->pos);
2496             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2497                 //just delete at the end of an open path
2498                 if (!sp->closed && curr == sp->last) {
2499                     just_delete = true;
2500                     break;
2501                 }
2503                 //sample points on the contiguous selected segment
2504                 Geom::Point *bez;
2505                 bez = new Geom::Point [4];
2506                 bez[0] = curr->pos;
2507                 bez[1] = curr->n.pos;
2508                 bez[2] = curr->n.other->p.pos;
2509                 bez[3] = curr->n.other->pos;
2510                 for (int i=1; i<rate; i++) {
2511                     gdouble t = i * period;
2512                     Geom::Point p = bezier_pt(3, bez, t);
2513                     data.push_back(p);
2514                 }
2515                 data.push_back(curr->n.other->pos);
2517                 sample_end = curr->n.other;
2518                 //break if we've come full circle or hit the end of the selection
2519                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2520                     break;
2521                 }
2522             }
2523         }
2525         if (!just_delete) {
2526             //calculate the best fitting single segment and adjust the endpoints
2527             Geom::Point *adata;
2528             adata = new Geom::Point [data.size()];
2529             copy(data.begin(), data.end(), adata);
2531             Geom::Point *bez;
2532             bez = new Geom::Point [4];
2533             //would decreasing error create a better fitting approximation?
2534             gdouble error = 1.0;
2535             gint ret;
2536             ret = Geom::bezier_fit_cubic (bez, adata, data.size(), error);
2538             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2539             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2540             //the resulting nodes behave as expected.
2541             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2542                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2543             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2544                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2546             //adjust endpoints
2547             sample_cursor->n.pos = bez[1];
2548             sample_end->p.pos = bez[2];
2549         }
2551         //destroy this contiguous selection
2552         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2553             Inkscape::NodePath::Node *temp = delete_cursor;
2554             if (delete_cursor->n.other == delete_cursor) {
2555                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2556                 delete_cursor = NULL;
2557             } else {
2558                 delete_cursor = delete_cursor->n.other;
2559             }
2560             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2561             sp_nodepath_node_destroy(temp);
2562         }
2564         sp_nodepath_update_handles(nodepath);
2566         if (!g_slist_find(nodepaths, nodepath))
2567             nodepaths = g_slist_prepend (nodepaths, nodepath);
2568     }
2570     for (GSList *i = nodepaths; i; i = i->next) {
2571         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2572         // different nodepaths will give us one undo event per nodepath
2573         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2575         // if the entire nodepath is removed, delete the selected object.
2576         if (nodepath->subpaths == NULL ||
2577             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2578             //at least 2
2579             sp_nodepath_get_node_count(nodepath) < 2) {
2580             SPDocument *document = sp_desktop_document (nodepath->desktop);
2581             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2582             //delete this nodepath's object, not the entire selection! (though at this time, this
2583             //does not matter)
2584             sp_selection_delete(nodepath->desktop);
2585             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2586                               _("Delete nodes"));
2587         } else {
2588             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2589             sp_nodepath_update_statusbar(nodepath);
2590         }
2591     }
2593     g_slist_free (nodepaths);
2596 /**
2597  * Delete one or more selected nodes.
2598  */
2599 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2601     if (!nodepath) return;
2602     if (!nodepath->selected) return;
2604     /** \todo fixme: do it the right way */
2605     while (nodepath->selected) {
2606        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2607         sp_nodepath_node_destroy(node);
2608     }
2611     //clean up the nodepath (such as for trivial subpaths)
2612     sp_nodepath_cleanup(nodepath);
2614     sp_nodepath_update_handles(nodepath);
2616     // if the entire nodepath is removed, delete the selected object.
2617     if (nodepath->subpaths == NULL ||
2618         sp_nodepath_get_node_count(nodepath) < 2) {
2619         SPDocument *document = sp_desktop_document (nodepath->desktop);
2620         sp_selection_delete(nodepath->desktop);
2621         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2622                           _("Delete nodes"));
2623         return;
2624     }
2626     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2628     sp_nodepath_update_statusbar(nodepath);
2631 /**
2632  * Delete one or more segments between two selected nodes.
2633  * This is the code for 'split'.
2634  */
2635 void
2636 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2638    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2639    Inkscape::NodePath::Node *curr, *next;     //Iterators
2641     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2643     if (g_list_length(nodepath->selected) != 2) {
2644         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2645                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2646         return;
2647     }
2649     //Selected nodes, not inclusive
2650    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2651    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2653     if ( ( a==b)                       ||  //same node
2654          (a->subpath  != b->subpath )  ||  //not the same path
2655          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2656          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2657     {
2658         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2659                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2660         return;
2661     }
2663     //###########################################
2664     //# BEGIN EDITS
2665     //###########################################
2666     //##################################
2667     //# CLOSED PATH
2668     //##################################
2669     if (a->subpath->closed) {
2672         gboolean reversed = FALSE;
2674         //Since we can go in a circle, we need to find the shorter distance.
2675         //  a->b or b->a
2676         start = end = NULL;
2677         int distance    = 0;
2678         int minDistance = 0;
2679         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2680             if (curr==b) {
2681                 //printf("a to b:%d\n", distance);
2682                 start = a;//go from a to b
2683                 end   = b;
2684                 minDistance = distance;
2685                 //printf("A to B :\n");
2686                 break;
2687             }
2688             distance++;
2689         }
2691         //try again, the other direction
2692         distance = 0;
2693         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2694             if (curr==a) {
2695                 //printf("b to a:%d\n", distance);
2696                 if (distance < minDistance) {
2697                     start    = b;  //we go from b to a
2698                     end      = a;
2699                     reversed = TRUE;
2700                     //printf("B to A\n");
2701                 }
2702                 break;
2703             }
2704             distance++;
2705         }
2708         //Copy everything from 'end' to 'start' to a new subpath
2709        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2710         for (curr=end ; curr ; curr=curr->n.other) {
2711             NRPathcode code = (NRPathcode) curr->code;
2712             if (curr == end)
2713                 code = NR_MOVETO;
2714             sp_nodepath_node_new(t, NULL,
2715                                  (Inkscape::NodePath::NodeType)curr->type, code,
2716                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2717             if (curr == start)
2718                 break;
2719         }
2720         sp_nodepath_subpath_destroy(a->subpath);
2723     }
2727     //##################################
2728     //# OPEN PATH
2729     //##################################
2730     else {
2732         //We need to get the direction of the list between A and B
2733         //Can we walk from a to b?
2734         start = end = NULL;
2735         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2736             if (curr==b) {
2737                 start = a;  //did it!  we go from a to b
2738                 end   = b;
2739                 //printf("A to B\n");
2740                 break;
2741             }
2742         }
2743         if (!start) {//didn't work?  let's try the other direction
2744             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2745                 if (curr==a) {
2746                     start = b;  //did it!  we go from b to a
2747                     end   = a;
2748                     //printf("B to A\n");
2749                     break;
2750                 }
2751             }
2752         }
2753         if (!start) {
2754             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2755                                                      _("Cannot find path between nodes."));
2756             return;
2757         }
2761         //Copy everything after 'end' to a new subpath
2762        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2763         for (curr=end ; curr ; curr=curr->n.other) {
2764             NRPathcode code = (NRPathcode) curr->code;
2765             if (curr == end)
2766                 code = NR_MOVETO;
2767             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2768                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2769         }
2771         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2772         for (curr = start->n.other ; curr  ; curr=next) {
2773             next = curr->n.other;
2774             sp_nodepath_node_destroy(curr);
2775         }
2777     }
2778     //###########################################
2779     //# END EDITS
2780     //###########################################
2782     //clean up the nodepath (such as for trivial subpaths)
2783     sp_nodepath_cleanup(nodepath);
2785     sp_nodepath_update_handles(nodepath);
2787     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2789     sp_nodepath_update_statusbar(nodepath);
2792 /**
2793  * Call sp_nodepath_set_line() for all selected segments.
2794  */
2795 void
2796 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2798     if (nodepath == NULL) return;
2800     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2801        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2802         g_assert(n->selected);
2803         if (n->p.other && n->p.other->selected) {
2804             sp_nodepath_set_line_type(n, code);
2805         }
2806     }
2808     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2811 /**
2812  * Call sp_nodepath_convert_node_type() for all selected nodes.
2813  */
2814 void
2815 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2817     if (nodepath == NULL) return;
2819     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2821     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2822         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2823     }
2825     sp_nodepath_update_repr(nodepath, _("Change node type"));
2828 /**
2829  * Change select status of node, update its own and neighbour handles.
2830  */
2831 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2833     node->selected = selected;
2835     if (selected) {
2836         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 11 : 9);
2837         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2838         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2839         sp_knot_update_ctrl(node->knot);
2840     } else {
2841         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 9 : 7);
2842         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2843         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2844         sp_knot_update_ctrl(node->knot);
2845     }
2847     sp_node_update_handles(node);
2848     if (node->n.other) sp_node_update_handles(node->n.other);
2849     if (node->p.other) sp_node_update_handles(node->p.other);
2852 /**
2853 \brief Select a node
2854 \param node     The node to select
2855 \param incremental   If true, add to selection, otherwise deselect others
2856 \param override   If true, always select this node, otherwise toggle selected status
2857 */
2858 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2860     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2862     if (incremental) {
2863         if (override) {
2864             if (!g_list_find(nodepath->selected, node)) {
2865                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2866             }
2867             sp_node_set_selected(node, TRUE);
2868         } else { // toggle
2869             if (node->selected) {
2870                 g_assert(g_list_find(nodepath->selected, node));
2871                 nodepath->selected = g_list_remove(nodepath->selected, node);
2872             } else {
2873                 g_assert(!g_list_find(nodepath->selected, node));
2874                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2875             }
2876             sp_node_set_selected(node, !node->selected);
2877         }
2878     } else {
2879         sp_nodepath_deselect(nodepath);
2880         nodepath->selected = g_list_prepend(nodepath->selected, node);
2881         sp_node_set_selected(node, TRUE);
2882     }
2884     sp_nodepath_update_statusbar(nodepath);
2888 /**
2889 \brief Deselect all nodes in the nodepath
2890 */
2891 void
2892 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2894     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2896     while (nodepath->selected) {
2897         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2898         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2899     }
2900     sp_nodepath_update_statusbar(nodepath);
2903 /**
2904 \brief Select or invert selection of all nodes in the nodepath
2905 */
2906 void
2907 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2909     if (!nodepath) return;
2911     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2912        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2913         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2914            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2915            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2916         }
2917     }
2920 /**
2921  * If nothing selected, does the same as sp_nodepath_select_all();
2922  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2923  * (i.e., similar to "select all in layer", with the "selected" subpaths
2924  * being treated as "layers" in the path).
2925  */
2926 void
2927 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2929     if (!nodepath) return;
2931     if (g_list_length (nodepath->selected) == 0) {
2932         sp_nodepath_select_all (nodepath, invert);
2933         return;
2934     }
2936     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2937     GSList *subpaths = NULL;
2939     for (GList *l = copy; l != NULL; l = l->next) {
2940         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2941         Inkscape::NodePath::SubPath *subpath = n->subpath;
2942         if (!g_slist_find (subpaths, subpath))
2943             subpaths = g_slist_prepend (subpaths, subpath);
2944     }
2946     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2947         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2948         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2949             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2950             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2951         }
2952     }
2954     g_slist_free (subpaths);
2955     g_list_free (copy);
2958 /**
2959  * \brief Select the node after the last selected; if none is selected,
2960  * select the first within path.
2961  */
2962 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2964     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2966    Inkscape::NodePath::Node *last = NULL;
2967     if (nodepath->selected) {
2968         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2969            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2970             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2971             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2972                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2973                 if (node->selected) {
2974                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2975                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2976                             if (spl->next) { // there's a next subpath
2977                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2978                                 last = subpath_next->first;
2979                             } else if (spl->prev) { // there's a previous subpath
2980                                 last = NULL; // to be set later to the first node of first subpath
2981                             } else {
2982                                 last = node->n.other;
2983                             }
2984                         } else {
2985                             last = node->n.other;
2986                         }
2987                     } else {
2988                         if (node->n.other) {
2989                             last = node->n.other;
2990                         } else {
2991                             if (spl->next) { // there's a next subpath
2992                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2993                                 last = subpath_next->first;
2994                             } else if (spl->prev) { // there's a previous subpath
2995                                 last = NULL; // to be set later to the first node of first subpath
2996                             } else {
2997                                 last = (Inkscape::NodePath::Node *) subpath->first;
2998                             }
2999                         }
3000                     }
3001                 }
3002             }
3003         }
3004         sp_nodepath_deselect(nodepath);
3005     }
3007     if (last) { // there's at least one more node after selected
3008         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3009     } else { // no more nodes, select the first one in first subpath
3010        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
3011         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
3012     }
3015 /**
3016  * \brief Select the node before the first selected; if none is selected,
3017  * select the last within path
3018  */
3019 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
3021     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
3023    Inkscape::NodePath::Node *last = NULL;
3024     if (nodepath->selected) {
3025         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
3026            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3027             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
3028                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3029                 if (node->selected) {
3030                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
3031                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
3032                             if (spl->prev) { // there's a prev subpath
3033                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3034                                 last = subpath_prev->last;
3035                             } else if (spl->next) { // there's a next subpath
3036                                 last = NULL; // to be set later to the last node of last subpath
3037                             } else {
3038                                 last = node->p.other;
3039                             }
3040                         } else {
3041                             last = node->p.other;
3042                         }
3043                     } else {
3044                         if (node->p.other) {
3045                             last = node->p.other;
3046                         } else {
3047                             if (spl->prev) { // there's a prev subpath
3048                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3049                                 last = subpath_prev->last;
3050                             } else if (spl->next) { // there's a next subpath
3051                                 last = NULL; // to be set later to the last node of last subpath
3052                             } else {
3053                                 last = (Inkscape::NodePath::Node *) subpath->last;
3054                             }
3055                         }
3056                     }
3057                 }
3058             }
3059         }
3060         sp_nodepath_deselect(nodepath);
3061     }
3063     if (last) { // there's at least one more node before selected
3064         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3065     } else { // no more nodes, select the last one in last subpath
3066         GList *spl = g_list_last(nodepath->subpaths);
3067        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3068         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
3069     }
3072 /**
3073  * \brief Select all nodes that are within the rectangle.
3074  */
3075 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, Geom::Rect const &b, gboolean incremental)
3077     if (!incremental) {
3078         sp_nodepath_deselect(nodepath);
3079     }
3081     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3082        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3083         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3084            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3086             if (b.contains(node->pos)) {
3087                 sp_nodepath_node_select(node, TRUE, TRUE);
3088             }
3089         }
3090     }
3094 void
3095 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3097     g_assert (n);
3098     g_assert (nodepath);
3099     g_assert (n->subpath->nodepath == nodepath);
3101     if (g_list_length (nodepath->selected) == 0) {
3102         if (grow > 0) {
3103             sp_nodepath_node_select(n, TRUE, TRUE);
3104         }
3105         return;
3106     }
3108     if (g_list_length (nodepath->selected) == 1) {
3109         if (grow < 0) {
3110             sp_nodepath_deselect (nodepath);
3111             return;
3112         }
3113     }
3115         double n_sel_range = 0, p_sel_range = 0;
3116             Inkscape::NodePath::Node *farthest_n_node = n;
3117             Inkscape::NodePath::Node *farthest_p_node = n;
3119         // Calculate ranges
3120         {
3121             double n_range = 0, p_range = 0;
3122             bool n_going = true, p_going = true;
3123             Inkscape::NodePath::Node *n_node = n;
3124             Inkscape::NodePath::Node *p_node = n;
3125             do {
3126                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3127                 if (n_node && n_going)
3128                     n_node = n_node->n.other;
3129                 if (n_node == NULL) {
3130                     n_going = false;
3131                 } else {
3132                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3133                     if (n_node->selected) {
3134                         n_sel_range = n_range;
3135                         farthest_n_node = n_node;
3136                     }
3137                     if (n_node == p_node) {
3138                         n_going = false;
3139                         p_going = false;
3140                     }
3141                 }
3142                 if (p_node && p_going)
3143                     p_node = p_node->p.other;
3144                 if (p_node == NULL) {
3145                     p_going = false;
3146                 } else {
3147                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3148                     if (p_node->selected) {
3149                         p_sel_range = p_range;
3150                         farthest_p_node = p_node;
3151                     }
3152                     if (p_node == n_node) {
3153                         n_going = false;
3154                         p_going = false;
3155                     }
3156                 }
3157             } while (n_going || p_going);
3158         }
3160     if (grow > 0) {
3161         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3162                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3163         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3164                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3165         }
3166     } else {
3167         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3168                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3169         } else if (farthest_p_node && farthest_p_node->selected) {
3170                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3171         }
3172     }
3175 void
3176 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3178     g_assert (n);
3179     g_assert (nodepath);
3180     g_assert (n->subpath->nodepath == nodepath);
3182     if (g_list_length (nodepath->selected) == 0) {
3183         if (grow > 0) {
3184             sp_nodepath_node_select(n, TRUE, TRUE);
3185         }
3186         return;
3187     }
3189     if (g_list_length (nodepath->selected) == 1) {
3190         if (grow < 0) {
3191             sp_nodepath_deselect (nodepath);
3192             return;
3193         }
3194     }
3196     Inkscape::NodePath::Node *farthest_selected = NULL;
3197     double farthest_dist = 0;
3199     Inkscape::NodePath::Node *closest_unselected = NULL;
3200     double closest_dist = NR_HUGE;
3202     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3203        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3204         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3205            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3206            if (node == n)
3207                continue;
3208            if (node->selected) {
3209                if (Geom::L2(node->pos - n->pos) > farthest_dist) {
3210                    farthest_dist = Geom::L2(node->pos - n->pos);
3211                    farthest_selected = node;
3212                }
3213            } else {
3214                if (Geom::L2(node->pos - n->pos) < closest_dist) {
3215                    closest_dist = Geom::L2(node->pos - n->pos);
3216                    closest_unselected = node;
3217                }
3218            }
3219         }
3220     }
3222     if (grow > 0) {
3223         if (closest_unselected) {
3224             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3225         }
3226     } else {
3227         if (farthest_selected) {
3228             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3229         }
3230     }
3234 /**
3235 \brief  Saves all nodes' and handles' current positions in their origin members
3236 */
3237 void
3238 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3240     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3241        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3242         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3243            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3244            n->origin = n->pos;
3245            n->p.origin = n->p.pos;
3246            n->n.origin = n->n.pos;
3247         }
3248     }
3251 /**
3252 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3253 */
3254 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3256     GList *r = NULL;
3257     if (nodepath->selected) {
3258         guint i = 0;
3259         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3260             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3261             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3262                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3263                 i++;
3264                 if (node->selected) {
3265                     r = g_list_append(r, GINT_TO_POINTER(i));
3266                 }
3267             }
3268         }
3269     }
3270     return r;
3273 /**
3274 \brief  Restores selection by selecting nodes whose positions are in the list
3275 */
3276 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3278     sp_nodepath_deselect(nodepath);
3280     guint i = 0;
3281     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3282        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3283         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3284            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3285             i++;
3286             if (g_list_find(r, GINT_TO_POINTER(i))) {
3287                 sp_nodepath_node_select(node, TRUE, TRUE);
3288             }
3289         }
3290     }
3294 /**
3295 \brief Adjusts handle according to node type and line code.
3296 */
3297 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3299     g_assert(node);
3301     // nothing to do for auto nodes (sp_node_adjust_handles() does the job)
3302     if (node->type == Inkscape::NodePath::NODE_AUTO)
3303         return;
3305    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3306    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3308    // nothing to do if we are an end node
3309     if (me->other == NULL) return;
3310     if (other->other == NULL) return;
3312     // nothing to do if we are a cusp node
3313     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3315     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3316     NRPathcode mecode;
3317     if (which_adjust == 1) {
3318         mecode = (NRPathcode)me->other->code;
3319     } else {
3320         mecode = (NRPathcode)node->code;
3321     }
3322     if (mecode == NR_LINETO) return;
3324     if (sp_node_side_is_line(node, other)) {
3325         // other is a line, and we are either smooth or symm
3326        Inkscape::NodePath::Node *othernode = other->other;
3327         double len = Geom::L2(me->pos - node->pos);
3328         Geom::Point delta = node->pos - othernode->pos;
3329         double linelen = Geom::L2(delta);
3330         if (linelen < 1e-18)
3331             return;
3332         me->pos = node->pos + (len / linelen)*delta;
3333         return;
3334     }
3336     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3337         // symmetrize
3338         me->pos = 2 * node->pos - other->pos;
3339         return;
3340     } else {
3341         // smoothify
3342         double len = Geom::L2(me->pos - node->pos);
3343         Geom::Point delta = other->pos - node->pos;
3344         double otherlen = Geom::L2(delta);
3345         if (otherlen < 1e-18) return;
3346         me->pos = node->pos - (len / otherlen) * delta;
3347     }
3350 /**
3351  \brief Adjusts both handles according to node type and line code
3352  */
3353 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3355     g_assert(node);
3357     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3359     /* we are either smooth or symm */
3361     if (node->p.other == NULL) return;
3362     if (node->n.other == NULL) return;
3364     if (node->type == Inkscape::NodePath::NODE_AUTO) {
3365         sp_node_adjust_handles_auto(node);
3366         return;
3367     }
3369     if (sp_node_side_is_line(node, &node->p)) {
3370         sp_node_adjust_handle(node, 1);
3371         return;
3372     }
3374     if (sp_node_side_is_line(node, &node->n)) {
3375         sp_node_adjust_handle(node, -1);
3376         return;
3377     }
3379     /* both are curves */
3380     Geom::Point const delta( node->n.pos - node->p.pos );
3382     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3383         node->p.pos = node->pos - delta / 2;
3384         node->n.pos = node->pos + delta / 2;
3385         return;
3386     }
3388     /* We are smooth */
3389     double plen = Geom::L2(node->p.pos - node->pos);
3390     if (plen < 1e-18) return;
3391     double nlen = Geom::L2(node->n.pos - node->pos);
3392     if (nlen < 1e-18) return;
3393     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3394     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3397 static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node)
3399     if (node->p.other == NULL || node->n.other == NULL) {
3400         node->p.pos = node->pos;
3401         node->n.pos = node->pos;
3402         return;
3403     }
3405     Geom::Point leg_prev = to_2geom(node->p.other->pos - node->pos);
3406     Geom::Point leg_next = to_2geom(node->n.other->pos - node->pos);
3408     double norm_leg_prev = Geom::L2(leg_prev);
3409     double norm_leg_next = Geom::L2(leg_next);
3411     Geom::Point delta;
3412     if (norm_leg_next > 0.0) {
3413         delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
3414         delta.normalize();
3415     }
3417     node->p.pos = node->pos - norm_leg_prev / 3 * delta;
3418     node->n.pos = node->pos + norm_leg_next / 3 * delta;
3421 /**
3422  * Node event callback.
3423  */
3424 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3426     gboolean ret = FALSE;
3427     switch (event->type) {
3428         case GDK_ENTER_NOTIFY:
3429             Inkscape::NodePath::Path::active_node = n;
3430             break;
3431         case GDK_LEAVE_NOTIFY:
3432             Inkscape::NodePath::Path::active_node = NULL;
3433             break;
3434         case GDK_SCROLL:
3435             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3436                 switch (event->scroll.direction) {
3437                     case GDK_SCROLL_UP:
3438                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3439                         break;
3440                     case GDK_SCROLL_DOWN:
3441                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3442                         break;
3443                     default:
3444                         break;
3445                 }
3446                 ret = TRUE;
3447             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3448                 switch (event->scroll.direction) {
3449                     case GDK_SCROLL_UP:
3450                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3451                         break;
3452                     case GDK_SCROLL_DOWN:
3453                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3454                         break;
3455                     default:
3456                         break;
3457                 }
3458                 ret = TRUE;
3459             }
3460             break;
3461         case GDK_KEY_PRESS:
3462             switch (get_group0_keyval (&event->key)) {
3463                 case GDK_space:
3464                     if (event->key.state & GDK_BUTTON1_MASK) {
3465                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3466                         stamp_repr(nodepath);
3467                         ret = TRUE;
3468                     }
3469                     break;
3470                 case GDK_Page_Up:
3471                     if (event->key.state & GDK_CONTROL_MASK) {
3472                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3473                     } else {
3474                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3475                     }
3476                     break;
3477                 case GDK_Page_Down:
3478                     if (event->key.state & GDK_CONTROL_MASK) {
3479                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3480                     } else {
3481                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3482                     }
3483                     break;
3484                 default:
3485                     break;
3486             }
3487             break;
3488         default:
3489             break;
3490     }
3492     return ret;
3495 /**
3496  * Handle keypress on node; directly called.
3497  */
3498 gboolean node_key(GdkEvent *event)
3500     Inkscape::NodePath::Path *np;
3502     // there is no way to verify nodes so set active_node to nil when deleting!!
3503     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3505     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3506         gint ret = FALSE;
3507         switch (get_group0_keyval (&event->key)) {
3508             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3509             case GDK_BackSpace:
3510                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3511                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3512                 sp_nodepath_update_repr(np, _("Delete node"));
3513                 Inkscape::NodePath::Path::active_node = NULL;
3514                 ret = TRUE;
3515                 break;
3516             case GDK_c:
3517                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3518                 ret = TRUE;
3519                 break;
3520             case GDK_s:
3521                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3522                 ret = TRUE;
3523                 break;
3524             case GDK_a:
3525                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_AUTO);
3526                 ret = TRUE;
3527                 break;
3528             case GDK_y:
3529                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3530                 ret = TRUE;
3531                 break;
3532             case GDK_b:
3533                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3534                 ret = TRUE;
3535                 break;
3536         }
3537         return ret;
3538     }
3539     return FALSE;
3542 /**
3543  * Mouseclick on node callback.
3544  */
3545 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3547    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3549     if (state & GDK_CONTROL_MASK) {
3550         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3552         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3553             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3554                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3555             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3556                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3557             } else if (n->type == Inkscape::NodePath::NODE_SYMM) {
3558                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_AUTO);
3559             } else {
3560                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3561             }
3562             sp_nodepath_update_repr(nodepath, _("Change node type"));
3563             sp_nodepath_update_statusbar(nodepath);
3565         } else { //ctrl+alt+click: delete node
3566             GList *node_to_delete = NULL;
3567             node_to_delete = g_list_append(node_to_delete, n);
3568             sp_node_delete_preserve(node_to_delete);
3569         }
3571     } else {
3572         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3573     }
3576 /**
3577  * Mouse grabbed node callback.
3578  */
3579 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3581    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3583     if (!n->selected) {
3584         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3585     }
3587     n->is_dragging = true;
3588     sp_canvas_set_snap_delay_active(n->subpath->nodepath->desktop->canvas, true);
3589     // Reconstruct and store the location of the mouse pointer at the time when we started dragging (needed for snapping)
3590     n->subpath->nodepath->drag_origin_mouse = knot->grabbed_rel_pos + knot->drag_origin;
3592     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3594     sp_nodepath_remember_origins (n->subpath->nodepath);
3597 /**
3598  * Mouse ungrabbed node callback.
3599  */
3600 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3602    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3604    n->dragging_out = NULL;
3605    n->is_dragging = false;
3606    sp_canvas_set_snap_delay_active(n->subpath->nodepath->desktop->canvas, false);
3607    n->subpath->nodepath->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
3608    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3610    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3613 /**
3614  * The point on a line, given by its angle, closest to the given point.
3615  * \param p  A point.
3616  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3617  * \param closest  Pointer to the point struct where the result is stored.
3618  * \todo FIXME: use dot product perhaps?
3619  */
3620 static void point_line_closest(Geom::Point *p, double a, Geom::Point *closest)
3622     if (a == HUGE_VAL) { // vertical
3623         *closest = Geom::Point(0, (*p)[Geom::Y]);
3624     } else {
3625         (*closest)[Geom::X] = ( a * (*p)[Geom::Y] + (*p)[Geom::X]) / (a*a + 1);
3626         (*closest)[Geom::Y] = a * (*closest)[Geom::X];
3627     }
3630 /**
3631  * Distance from the point to a line given by its angle.
3632  * \param p  A point.
3633  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3634  */
3635 static double point_line_distance(Geom::Point *p, double a)
3637     Geom::Point c;
3638     point_line_closest(p, a, &c);
3639     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]));
3642 /**
3643  * Callback for node "request" signal.
3644  * \todo fixme: This goes to "moved" event? (lauris)
3645  */
3646 static gboolean
3647 node_request(SPKnot */*knot*/, Geom::Point const &p, guint state, gpointer data)
3649     double yn, xn, yp, xp;
3650     double an, ap, na, pa;
3651     double d_an, d_ap, d_na, d_pa;
3652     gboolean collinear = FALSE;
3653     Geom::Point c;
3654     Geom::Point pr;
3656     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3658     n->subpath->nodepath->desktop->snapindicator->remove_snaptarget();
3660     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3661     if ( (!n->subpath->nodepath->straight_path) &&
3662          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3663            || n->dragging_out ) )
3664     {
3665        Geom::Point mouse = p;
3667        if (!n->dragging_out) {
3668            // This is the first drag-out event; find out which handle to drag out
3669            double appr_n = (n->n.other ? Geom::L2(n->n.other->pos - n->pos) - Geom::L2(n->n.other->pos - p) : -HUGE_VAL);
3670            double appr_p = (n->p.other ? Geom::L2(n->p.other->pos - n->pos) - Geom::L2(n->p.other->pos - p) : -HUGE_VAL);
3672            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3673                return FALSE;
3675            Inkscape::NodePath::NodeSide *opposite;
3676            if (appr_p > appr_n) { // closer to p
3677                n->dragging_out = &n->p;
3678                opposite = &n->n;
3679                n->code = NR_CURVETO;
3680            } else if (appr_p < appr_n) { // closer to n
3681                n->dragging_out = &n->n;
3682                opposite = &n->p;
3683                n->n.other->code = NR_CURVETO;
3684            } else { // p and n nodes are the same
3685                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3686                    n->dragging_out = &n->p;
3687                    opposite = &n->n;
3688                    n->code = NR_CURVETO;
3689                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3690                    n->dragging_out = &n->n;
3691                    opposite = &n->p;
3692                    n->n.other->code = NR_CURVETO;
3693                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3694                    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);
3695                    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);
3696                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3697                        n->dragging_out = &n->n;
3698                        opposite = &n->p;
3699                        n->n.other->code = NR_CURVETO;
3700                    } else { // closer to other's n handle
3701                        n->dragging_out = &n->p;
3702                        opposite = &n->n;
3703                        n->code = NR_CURVETO;
3704                    }
3705                }
3706            }
3708            // if there's another handle, make sure the one we drag out starts parallel to it
3709            if (opposite->pos != n->pos) {
3710                mouse = n->pos - Geom::L2(mouse - n->pos) * Geom::unit_vector(opposite->pos - n->pos);
3711            }
3713            // knots might not be created yet!
3714            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3715            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3716        }
3718        // pass this on to the handle-moved callback
3719        node_handle_moved(n->dragging_out->knot, mouse, state, (gpointer) n);
3720        sp_node_update_handles(n);
3721        return TRUE;
3722    }
3724     if (state & GDK_CONTROL_MASK) { // constrained motion
3726         // calculate relative distances of handles
3727         // n handle:
3728         yn = n->n.pos[Geom::Y] - n->pos[Geom::Y];
3729         xn = n->n.pos[Geom::X] - n->pos[Geom::X];
3730         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3731         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3732             if (n->n.other) { // if there is the next point
3733                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3734                     yn = n->n.other->origin[Geom::Y] - n->origin[Geom::Y]; // use origin because otherwise the direction will change as you drag
3735                     xn = n->n.other->origin[Geom::X] - n->origin[Geom::X];
3736             }
3737         }
3738         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3739         if (yn < 0) { xn = -xn; yn = -yn; }
3741         // p handle:
3742         yp = n->p.pos[Geom::Y] - n->pos[Geom::Y];
3743         xp = n->p.pos[Geom::X] - n->pos[Geom::X];
3744         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3745         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3746             if (n->p.other) {
3747                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3748                     yp = n->p.other->origin[Geom::Y] - n->origin[Geom::Y];
3749                     xp = n->p.other->origin[Geom::X] - n->origin[Geom::X];
3750             }
3751         }
3752         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3753         if (yp < 0) { xp = -xp; yp = -yp; }
3755         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3756             // sliding on handles, only if at least one of the handles is non-vertical
3757             // (otherwise it's the same as ctrl+drag anyway)
3759             // calculate angles of the handles
3760             if (xn == 0) {
3761                 if (yn == 0) { // no handle, consider it the continuation of the other one
3762                     an = 0;
3763                     collinear = TRUE;
3764                 }
3765                 else an = 0; // vertical; set the angle to horizontal
3766             } else an = yn/xn;
3768             if (xp == 0) {
3769                 if (yp == 0) { // no handle, consider it the continuation of the other one
3770                     ap = an;
3771                 }
3772                 else ap = 0; // vertical; set the angle to horizontal
3773             } else  ap = yp/xp;
3775             if (collinear) an = ap;
3777             // angles of the perpendiculars; HUGE_VAL means vertical
3778             if (an == 0) na = HUGE_VAL; else na = -1/an;
3779             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3781             // mouse point relative to the node's original pos
3782             pr = p - n->origin;
3784             // distances to the four lines (two handles and two perpendiculars)
3785             d_an = point_line_distance(&pr, an);
3786             d_na = point_line_distance(&pr, na);
3787             d_ap = point_line_distance(&pr, ap);
3788             d_pa = point_line_distance(&pr, pa);
3790             // find out which line is the closest, save its closest point in c
3791             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3792                 point_line_closest(&pr, an, &c);
3793             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3794                 point_line_closest(&pr, ap, &c);
3795             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3796                 point_line_closest(&pr, na, &c);
3797             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3798                 point_line_closest(&pr, pa, &c);
3799             }
3801             // move the node to the closest point
3802             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3803                                             n->origin[Geom::X] + c[Geom::X] - n->pos[Geom::X],
3804                                             n->origin[Geom::Y] + c[Geom::Y] - n->pos[Geom::Y],
3805                                             true);
3807         } else {  // constraining to hor/vert
3809             if (fabs(p[Geom::X] - n->origin[Geom::X]) > fabs(p[Geom::Y] - n->origin[Geom::Y])) { // snap to hor
3810                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3811                                                 p[Geom::X] - n->pos[Geom::X],
3812                                                 n->origin[Geom::Y] - n->pos[Geom::Y],
3813                                                 true,
3814                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::X]));
3815             } else { // snap to vert
3816                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3817                                                 n->origin[Geom::X] - n->pos[Geom::X],
3818                                                 p[Geom::Y] - n->pos[Geom::Y],
3819                                                 true,
3820                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::Y]));
3821             }
3822         }
3823     } else { // move freely
3824         if (n->is_dragging) {
3825             if (state & GDK_MOD1_MASK) { // sculpt
3826                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, p - n->origin);
3827             } else {
3828                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3829                                             p[Geom::X] - n->pos[Geom::X],
3830                                             p[Geom::Y] - n->pos[Geom::Y],
3831                                             (state & GDK_SHIFT_MASK) == 0);
3832             }
3833         }
3834     }
3836     n->subpath->nodepath->desktop->scroll_to_point(p);
3838     return TRUE;
3841 /**
3842  * Node handle clicked callback.
3843  */
3844 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3846    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3848     if (state & GDK_CONTROL_MASK) { // "delete" handle
3849         if (n->p.knot == knot) {
3850             n->p.pos = n->pos;
3851         } else if (n->n.knot == knot) {
3852             n->n.pos = n->pos;
3853         }
3854         sp_node_update_handles(n);
3855         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3856         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3857         sp_nodepath_update_statusbar(nodepath);
3859     } else { // just select or add to selection, depending in Shift
3860         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3861     }
3864 /**
3865  * Node handle grabbed callback.
3866  */
3867 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3869    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3871     // convert auto -> smooth when dragging handle
3872    if (n->type == Inkscape::NodePath::NODE_AUTO) {
3873         n->type = Inkscape::NodePath::NODE_SMOOTH;
3874         sp_nodepath_update_node_knot (n);
3875    }
3877     if (!n->selected) {
3878         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3879     }
3881     // remember the origin point of the handle
3882     if (n->p.knot == knot) {
3883         n->p.origin_radial = n->p.pos - n->pos;
3884     } else if (n->n.knot == knot) {
3885         n->n.origin_radial = n->n.pos - n->pos;
3886     } else {
3887         g_assert_not_reached();
3888     }
3890     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3893 /**
3894  * Node handle ungrabbed callback.
3895  */
3896 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3898    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3900     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3901     if (n->p.knot == knot) {
3902         n->p.origin_radial.a = 0;
3903         sp_knot_set_position(knot, n->p.pos, state);
3904     } else if (n->n.knot == knot) {
3905         n->n.origin_radial.a = 0;
3906         sp_knot_set_position(knot, n->n.pos, state);
3907     } else {
3908         g_assert_not_reached();
3909     }
3911     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3914 /**
3915  * Node handle "request" signal callback.
3916  */
3917 static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, gpointer data)
3919     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3921     Inkscape::NodePath::NodeSide *me, *opposite;
3922     gint which;
3923     if (n->p.knot == knot) {
3924         me = &n->p;
3925         opposite = &n->n;
3926         which = -1;
3927     } else if (n->n.knot == knot) {
3928         me = &n->n;
3929         opposite = &n->p;
3930         which = 1;
3931     } else {
3932         me = opposite = NULL;
3933         which = 0;
3934         g_assert_not_reached();
3935     }
3937     SPDesktop *desktop = n->subpath->nodepath->desktop;
3938     SnapManager &m = desktop->namedview->snap_manager;
3939     m.setup(desktop, true, n->subpath->nodepath->item);
3940     Inkscape::SnappedPoint s;
3942     if ((state & GDK_SHIFT_MASK) != 0) {
3943         // We will not try to snap when the shift-key is pressed
3944         // so remove the old snap indicator and don't wait for it to time-out
3945         desktop->snapindicator->remove_snaptarget();
3946     }
3948     Inkscape::NodePath::Node *othernode = opposite->other;
3949     Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP);
3950     if (othernode) {
3951         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3952             /* We are smooth node adjacent with line */
3953             Geom::Point const delta = p - n->pos;
3954             Geom::Coord const len = Geom::L2(delta);
3955             Inkscape::NodePath::Node *othernode = opposite->other;
3956             Geom::Point const ndelta = n->pos - othernode->pos;
3957             Geom::Coord const linelen = Geom::L2(ndelta);
3958             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3959                 Geom::Coord const scal = dot(delta, ndelta) / linelen;
3960                 p = n->pos + (scal / linelen) * ndelta;
3961             }
3962             if ((state & GDK_SHIFT_MASK) == 0) {
3963                 s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type, Inkscape::Snapper::ConstraintLine(p, ndelta));
3964             }
3965         } else {
3966             if ((state & GDK_SHIFT_MASK) == 0) {
3967                 s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type);
3968             }
3969         }
3970     } else {
3971         if ((state & GDK_SHIFT_MASK) == 0) {
3972             s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type);
3973         }
3974     }
3976     s.getPoint(p);
3978     sp_node_adjust_handle(n, -which);
3980     return FALSE;
3983 /**
3984  * Node handle moved callback.
3985  */
3986 static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data)
3988    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3989    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3991    Inkscape::NodePath::NodeSide *me;
3992    Inkscape::NodePath::NodeSide *other;
3993     if (n->p.knot == knot) {
3994         me = &n->p;
3995         other = &n->n;
3996     } else if (n->n.knot == knot) {
3997         me = &n->n;
3998         other = &n->p;
3999     } else {
4000         me = NULL;
4001         other = NULL;
4002         g_assert_not_reached();
4003     }
4005     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
4006     Radial rme(me->pos - n->pos);
4007     Radial rother(other->pos - n->pos);
4008     Radial rnew(p - n->pos);
4010     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
4011         int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
4012         /* 0 interpreted as "no snapping". */
4014         // 1. Snap to the closest PI/snaps angle, starting from zero.
4015         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
4017         // 2. Snap to the original angle, its opposite and perpendiculars
4018         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
4019             /* The closest PI/2 angle, starting from original angle */
4020             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
4022             // Snap to the closest.
4023             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
4024                        ? a_snapped
4025                        : a_ortho );
4026         }
4028         // 3. Snap to the angle of the opposite line, if any
4029         Inkscape::NodePath::Node *othernode = other->other;
4030         if (othernode) {
4031             Geom::Point other_to_snap(0,0);
4032             if (sp_node_side_is_line(n, other)) {
4033                 other_to_snap = othernode->pos - n->pos;
4034             } else {
4035                 other_to_snap = other->pos - n->pos;
4036             }
4037             if (Geom::L2(other_to_snap) > 1e-3) {
4038                 Radial rother_to_snap(other_to_snap);
4039                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
4040                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
4042                 // Snap to the closest.
4043                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
4044                        ? a_snapped
4045                        : a_oppo );
4046             }
4047         }
4049         rnew.a = a_snapped;
4050     }
4052     if (state & GDK_MOD1_MASK) {
4053         // lock handle length
4054         rnew.r = me->origin_radial.r;
4055     }
4057     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
4058         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
4059         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
4060         rother.a += rnew.a - rme.a;
4061         other->pos = Geom::Point(rother) + n->pos;
4062         if (other->knot) {
4063             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
4064             sp_knot_moveto(other->knot, other->pos);
4065         }
4066     }
4068     me->pos = Geom::Point(rnew) + n->pos;
4069     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
4071     // move knot, but without emitting the signal:
4072     // we cannot emit a "moved" signal because we're now processing it
4073     sp_knot_moveto(me->knot, me->pos);
4075     update_object(n->subpath->nodepath);
4077     /* status text */
4078     SPDesktop *desktop = n->subpath->nodepath->desktop;
4079     if (!desktop) return;
4080     SPEventContext *ec = desktop->event_context;
4081     if (!ec) return;
4083     Inkscape::MessageContext *mc = get_message_context(ec);
4085     if (!mc) return;
4087     double degrees = 180 / M_PI * rnew.a;
4088     if (degrees > 180) degrees -= 360;
4089     if (degrees < -180) degrees += 360;
4090     if (prefs->getBool("/options/compassangledisplay/value"))
4091         degrees = angle_to_compass (degrees);
4093     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
4095     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
4096          _("<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);
4098     g_string_free(length, TRUE);
4101 /**
4102  * Node handle event callback.
4103  */
4104 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
4106     gboolean ret = FALSE;
4107     switch (event->type) {
4108         case GDK_KEY_PRESS:
4109             switch (get_group0_keyval (&event->key)) {
4110                 case GDK_space:
4111                     if (event->key.state & GDK_BUTTON1_MASK) {
4112                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
4113                         stamp_repr(nodepath);
4114                         ret = TRUE;
4115                     }
4116                     break;
4117                 default:
4118                     break;
4119             }
4120             break;
4121         case GDK_ENTER_NOTIFY:
4122             // we use an experimentally determined threshold that seems to work fine
4123             if (Geom::L2(n->pos - knot->pos) < 0.75)
4124                 Inkscape::NodePath::Path::active_node = n;
4125             break;
4126         case GDK_LEAVE_NOTIFY:
4127             // we use an experimentally determined threshold that seems to work fine
4128             if (Geom::L2(n->pos - knot->pos) < 0.75)
4129                 Inkscape::NodePath::Path::active_node = NULL;
4130             break;
4131         default:
4132             break;
4133     }
4135     return ret;
4138 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
4139                                  Radial &rme, Radial &rother, gboolean const both)
4141     rme.a += angle;
4142     if ( both
4143          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4144          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4145     {
4146         rother.a += angle;
4147     }
4150 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
4151                                         Radial &rme, Radial &rother, gboolean const both)
4153     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
4155     gdouble r;
4156     if ( both
4157          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4158          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4159     {
4160         r = MAX(rme.r, rother.r);
4161     } else {
4162         r = rme.r;
4163     }
4165     gdouble const weird_angle = atan2(norm_angle, r);
4166 /* Bulia says norm_angle is just the visible distance that the
4167  * object's end must travel on the screen.  Left as 'angle' for want of
4168  * a better name.*/
4170     rme.a += weird_angle;
4171     if ( both
4172          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4173          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4174     {
4175         rother.a += weird_angle;
4176     }
4179 /**
4180  * Rotate one node.
4181  */
4182 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4184     Inkscape::NodePath::NodeSide *me, *other;
4185     bool both = false;
4187     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4188     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4190     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4191         me = &(n->p);
4192         other = &(n->n);
4193     } else if (!n->p.other) {
4194         me = &(n->n);
4195         other = &(n->p);
4196     } else {
4197         if (which > 0) { // right 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 if (which < 0){ // left handle
4206             if (xn <= xp) {
4207                 me = &(n->n);
4208                 other = &(n->p);
4209             } else {
4210                 me = &(n->p);
4211                 other = &(n->n);
4212             }
4213         } else { // both handles
4214             me = &(n->n);
4215             other = &(n->p);
4216             both = true;
4217         }
4218     }
4220     Radial rme(me->pos - n->pos);
4221     Radial rother(other->pos - n->pos);
4223     if (screen) {
4224         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4225     } else {
4226         node_rotate_one_internal (*n, angle, rme, rother, both);
4227     }
4229     me->pos = n->pos + Geom::Point(rme);
4231     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4232         other->pos =  n->pos + Geom::Point(rother);
4233     }
4235     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4236     // so here we just move all the knots without emitting move signals, for speed
4237     sp_node_update_handles(n, false);
4240 /**
4241  * Rotate selected nodes.
4242  */
4243 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4245     if (!nodepath || !nodepath->selected) return;
4247     if (g_list_length(nodepath->selected) == 1) {
4248        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4249         node_rotate_one (n, angle, which, screen);
4250     } else {
4251        // rotate as an object:
4253         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4254         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4255         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4256             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4257             box.expandTo (n->pos); // contain all selected nodes
4258         }
4260         gdouble rot;
4261         if (screen) {
4262             gdouble const zoom = nodepath->desktop->current_zoom();
4263             gdouble const zmove = angle / zoom;
4264             gdouble const r = Geom::L2(box.max() - box.midpoint());
4265             rot = atan2(zmove, r);
4266         } else {
4267             rot = angle;
4268         }
4270         Geom::Point rot_center;
4271         if (Inkscape::NodePath::Path::active_node == NULL)
4272             rot_center = box.midpoint();
4273         else
4274             rot_center = Inkscape::NodePath::Path::active_node->pos;
4276         Geom::Matrix t =
4277             Geom::Matrix (Geom::Translate(-rot_center)) *
4278             Geom::Matrix (Geom::Rotate(rot)) *
4279             Geom::Matrix (Geom::Translate(rot_center));
4281         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4282             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4283             n->pos *= t;
4284             n->n.pos *= t;
4285             n->p.pos *= t;
4286             sp_node_update_handles(n, false);
4287         }
4288     }
4290     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4293 /**
4294  * Scale one node.
4295  */
4296 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4298     bool both = false;
4299     Inkscape::NodePath::NodeSide *me, *other;
4301     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4302     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4304     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4305         me = &(n->p);
4306         other = &(n->n);
4307         n->code = NR_CURVETO;
4308     } else if (!n->p.other) {
4309         me = &(n->n);
4310         other = &(n->p);
4311         if (n->n.other)
4312             n->n.other->code = NR_CURVETO;
4313     } else {
4314         if (which > 0) { // right handle
4315             if (xn > xp) {
4316                 me = &(n->n);
4317                 other = &(n->p);
4318                 if (n->n.other)
4319                     n->n.other->code = NR_CURVETO;
4320             } else {
4321                 me = &(n->p);
4322                 other = &(n->n);
4323                 n->code = NR_CURVETO;
4324             }
4325         } else if (which < 0){ // left handle
4326             if (xn <= xp) {
4327                 me = &(n->n);
4328                 other = &(n->p);
4329                 if (n->n.other)
4330                     n->n.other->code = NR_CURVETO;
4331             } else {
4332                 me = &(n->p);
4333                 other = &(n->n);
4334                 n->code = NR_CURVETO;
4335             }
4336         } else { // both handles
4337             me = &(n->n);
4338             other = &(n->p);
4339             both = true;
4340             n->code = NR_CURVETO;
4341             if (n->n.other)
4342                 n->n.other->code = NR_CURVETO;
4343         }
4344     }
4346     Radial rme(me->pos - n->pos);
4347     Radial rother(other->pos - n->pos);
4349     rme.r += grow;
4350     if (rme.r < 0) rme.r = 0;
4351     if (rme.a == HUGE_VAL) {
4352         if (me->other) { // if direction is unknown, initialize it towards the next node
4353             Radial rme_next(me->other->pos - n->pos);
4354             rme.a = rme_next.a;
4355         } else { // if there's no next, initialize to 0
4356             rme.a = 0;
4357         }
4358     }
4359     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4360         rother.r += grow;
4361         if (rother.r < 0) rother.r = 0;
4362         if (rother.a == HUGE_VAL) {
4363             rother.a = rme.a + M_PI;
4364         }
4365     }
4367     me->pos = n->pos + Geom::Point(rme);
4369     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4370         other->pos = n->pos + Geom::Point(rother);
4371     }
4373     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4374     // so here we just move all the knots without emitting move signals, for speed
4375     sp_node_update_handles(n, false);
4378 /**
4379  * Scale selected nodes.
4380  */
4381 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4383     if (!nodepath || !nodepath->selected) return;
4385     if (g_list_length(nodepath->selected) == 1) {
4386         // scale handles of the single selected node
4387         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4388         node_scale_one (n, grow, which);
4389     } else {
4390         // scale nodes as an "object":
4392         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4393         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4394         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4395             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4396             box.expandTo (n->pos); // contain all selected nodes
4397         }
4399         if ( Geom::are_near(box.maxExtent(), 0) ) {
4400             SPEventContext *ec = nodepath->desktop->event_context;
4401             if (!ec) return;
4402             Inkscape::MessageContext *mc = get_message_context(ec);
4403             if (!mc) return;
4404             mc->setF(Inkscape::WARNING_MESSAGE,
4405                              _("Cannot scale nodes when all are at the same location."));
4406             return;
4407         }
4408         double scale = (box.maxExtent() + grow)/box.maxExtent();
4411         Geom::Point scale_center;
4412         if (Inkscape::NodePath::Path::active_node == NULL)
4413             scale_center = box.midpoint();
4414         else
4415             scale_center = Inkscape::NodePath::Path::active_node->pos;
4417         Geom::Matrix t =
4418             Geom::Translate(-scale_center) *
4419             Geom::Scale(scale, scale) *
4420             Geom::Translate(scale_center);
4422         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4423             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4424             n->pos *= t;
4425             n->n.pos *= t;
4426             n->p.pos *= t;
4427             sp_node_update_handles(n, false);
4428         }
4429     }
4431     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4434 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4436     if (!nodepath) return;
4437     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4440 /**
4441  * Flip selected nodes horizontally/vertically.
4442  */
4443 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis, boost::optional<Geom::Point> center)
4445     if (!nodepath || !nodepath->selected) return;
4447     if (g_list_length(nodepath->selected) == 1 && !center) {
4448         // flip handles of the single selected node
4449         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4450         double temp = n->p.pos[axis];
4451         n->p.pos[axis] = n->n.pos[axis];
4452         n->n.pos[axis] = temp;
4453         sp_node_update_handles(n, false);
4454     } else {
4455         // scale nodes as an "object":
4457         Geom::Rect box = sp_node_selected_bbox (nodepath);
4458         if (!center) {
4459             center = box.midpoint();
4460         }
4461         Geom::Matrix t =
4462             Geom::Matrix (Geom::Translate(- *center)) *
4463             Geom::Matrix ((axis == Geom::X)? Geom::Scale(-1, 1) : Geom::Scale(1, -1)) *
4464             Geom::Matrix (Geom::Translate(*center));
4466         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4467             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4468             n->pos *= t;
4469             n->n.pos *= t;
4470             n->p.pos *= t;
4471             sp_node_update_handles(n, false);
4472         }
4473     }
4475     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4478 Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4480     g_assert (nodepath->selected);
4482     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4483     Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4484     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4485         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4486         box.expandTo (n->pos); // contain all selected nodes
4487     }
4488     return box;
4491 //-----------------------------------------------
4492 /**
4493  * Return new subpath under given nodepath.
4494  */
4495 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4497     g_assert(nodepath);
4498     g_assert(nodepath->desktop);
4500    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4502     s->nodepath = nodepath;
4503     s->closed = FALSE;
4504     s->nodes = NULL;
4505     s->first = NULL;
4506     s->last = NULL;
4508     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4509     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4510     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4512     return s;
4515 /**
4516  * Destroy nodes in subpath, then subpath itself.
4517  */
4518 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4520     g_assert(subpath);
4521     g_assert(subpath->nodepath);
4522     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4524     while (subpath->nodes) {
4525         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4526     }
4528     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4530     g_free(subpath);
4533 /**
4534  * Link head to tail in subpath.
4535  */
4536 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4538     g_assert(!sp->closed);
4539     g_assert(sp->last != sp->first);
4540     g_assert(sp->first->code == NR_MOVETO);
4542     sp->closed = TRUE;
4544     //Link the head to the tail
4545     sp->first->p.other = sp->last;
4546     sp->last->n.other  = sp->first;
4547     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4548     sp->first          = sp->last;
4550     //Remove the extra end node
4551     sp_nodepath_node_destroy(sp->last->n.other);
4554 /**
4555  * Open closed (loopy) subpath at node.
4556  */
4557 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4559     g_assert(sp->closed);
4560     g_assert(n->subpath == sp);
4561     g_assert(sp->first == sp->last);
4563     /* We create new startpoint, current node will become last one */
4565    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4566                                                 &n->pos, &n->pos, &n->n.pos);
4569     sp->closed        = FALSE;
4571     //Unlink to make a head and tail
4572     sp->first         = new_path;
4573     sp->last          = n;
4574     n->n.other        = NULL;
4575     new_path->p.other = NULL;
4578 /**
4579  * Return new node in subpath with given properties.
4580  * \param pos Position of node.
4581  * \param ppos Handle position in previous direction
4582  * \param npos Handle position in previous direction
4583  */
4584 Inkscape::NodePath::Node *
4585 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)
4587     g_assert(sp);
4588     g_assert(sp->nodepath);
4589     g_assert(sp->nodepath->desktop);
4591     if (nodechunk == NULL)
4592         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4594     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4596     n->subpath  = sp;
4598     if (type != Inkscape::NodePath::NODE_NONE) {
4599         // use the type from sodipodi:nodetypes
4600         n->type = type;
4601     } else {
4602         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4603             // points are (almost) collinear
4604             if (Geom::L2(*pos - *ppos) < 1e-6 || Geom::L2(*pos - *npos) < 1e-6) {
4605                 // endnode, or a node with a retracted handle
4606                 n->type = Inkscape::NodePath::NODE_CUSP;
4607             } else {
4608                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4609             }
4610         } else {
4611             n->type = Inkscape::NodePath::NODE_CUSP;
4612         }
4613     }
4615     n->code     = code;
4616     n->selected = FALSE;
4617     n->pos      = *pos;
4618     n->p.pos    = *ppos;
4619     n->n.pos    = *npos;
4621     n->dragging_out = NULL;
4623     Inkscape::NodePath::Node *prev;
4624     if (next) {
4625         //g_assert(g_list_find(sp->nodes, next));
4626         prev = next->p.other;
4627     } else {
4628         prev = sp->last;
4629     }
4631     if (prev)
4632         prev->n.other = n;
4633     else
4634         sp->first = n;
4636     if (next)
4637         next->p.other = n;
4638     else
4639         sp->last = n;
4641     n->p.other = prev;
4642     n->n.other = next;
4644     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"));
4645     sp_knot_set_position(n->knot, *pos, 0);
4647     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4648     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4649     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4651     sp_nodepath_update_node_knot(n);
4653     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4654     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4655     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4656     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4657     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4658     sp_knot_show(n->knot);
4660     // We only create handle knots and lines on demand
4661     n->p.knot = NULL;
4662     n->p.line = NULL;
4663     n->n.knot = NULL;
4664     n->n.line = NULL;
4666     sp->nodes = g_list_prepend(sp->nodes, n);
4668     return n;
4671 /**
4672  * Destroy node and its knots, link neighbors in subpath.
4673  */
4674 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4676     g_assert(node);
4677     g_assert(node->subpath);
4678     g_assert(SP_IS_KNOT(node->knot));
4680    Inkscape::NodePath::SubPath *sp = node->subpath;
4682     if (node->selected) { // first, deselect
4683         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4684         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4685     }
4687     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4689     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4690     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4691     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4692     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4693     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4694     g_object_unref(G_OBJECT(node->knot));
4696     if (node->p.knot) {
4697         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4698         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4699         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4700         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4701         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4702         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4703         g_object_unref(G_OBJECT(node->p.knot));
4704         node->p.knot = NULL;
4705     }
4707     if (node->n.knot) {
4708         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4709         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4710         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4711         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4712         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4713         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4714         g_object_unref(G_OBJECT(node->n.knot));
4715         node->n.knot = NULL;
4716     }
4718     if (node->p.line)
4719         gtk_object_destroy(GTK_OBJECT(node->p.line));
4720     if (node->n.line)
4721         gtk_object_destroy(GTK_OBJECT(node->n.line));
4723     if (sp->nodes) { // there are others nodes on the subpath
4724         if (sp->closed) {
4725             if (sp->first == node) {
4726                 g_assert(sp->last == node);
4727                 sp->first = node->n.other;
4728                 sp->last = sp->first;
4729             }
4730             node->p.other->n.other = node->n.other;
4731             node->n.other->p.other = node->p.other;
4732         } else {
4733             if (sp->first == node) {
4734                 sp->first = node->n.other;
4735                 sp->first->code = NR_MOVETO;
4736             }
4737             if (sp->last == node) sp->last = node->p.other;
4738             if (node->p.other) node->p.other->n.other = node->n.other;
4739             if (node->n.other) node->n.other->p.other = node->p.other;
4740         }
4741     } else { // this was the last node on subpath
4742         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4743     }
4745     g_mem_chunk_free(nodechunk, node);
4748 /**
4749  * Returns one of the node's two sides.
4750  * \param which Indicates which side.
4751  * \return Pointer to previous node side if which==-1, next if which==1.
4752  */
4753 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4755     g_assert(node);
4756     Inkscape::NodePath::NodeSide * result = 0;
4757     switch (which) {
4758         case -1:
4759             result = &node->p;
4760             break;
4761         case 1:
4762             result = &node->n;
4763             break;
4764         default:
4765             g_assert_not_reached();
4766     }
4768     return result;
4771 /**
4772  * Return the other side of the node, given one of its sides.
4773  */
4774 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4776     g_assert(node);
4777     Inkscape::NodePath::NodeSide *result = 0;
4779     if (me == &node->p) {
4780         result = &node->n;
4781     } else if (me == &node->n) {
4782         result = &node->p;
4783     } else {
4784         g_assert_not_reached();
4785     }
4787     return result;
4790 /**
4791  * Return NRPathcode on the given side of the node.
4792  */
4793 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4795     g_assert(node);
4797     NRPathcode result = NR_END;
4798     if (me == &node->p) {
4799         if (node->p.other) {
4800             result = (NRPathcode)node->code;
4801         } else {
4802             result = NR_MOVETO;
4803         }
4804     } else if (me == &node->n) {
4805         if (node->n.other) {
4806             result = (NRPathcode)node->n.other->code;
4807         } else {
4808             result = NR_MOVETO;
4809         }
4810     } else {
4811         g_assert_not_reached();
4812     }
4814     return result;
4817 /**
4818  * Return node with the given index
4819  */
4820 Inkscape::NodePath::Node *
4821 sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index)
4823     Inkscape::NodePath::Node *e = NULL;
4825     if (!nodepath) {
4826         return e;
4827     }
4829     //find segment
4830     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4832         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4833         int n = g_list_length(sp->nodes);
4834         if (sp->closed) {
4835             n++;
4836         }
4838         //if the piece belongs to this subpath grab it
4839         //otherwise move onto the next subpath
4840         if (index < n) {
4841             e = sp->first;
4842             for (int i = 0; i < index; ++i) {
4843                 e = e->n.other;
4844             }
4845             break;
4846         } else {
4847             if (sp->closed) {
4848                 index -= (n+1);
4849             } else {
4850                 index -= n;
4851             }
4852         }
4853     }
4855     return e;
4858 /**
4859  * Returns plain text meaning of node type.
4860  */
4861 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4863     unsigned retracted = 0;
4864     bool endnode = false;
4866     for (int which = -1; which <= 1; which += 2) {
4867         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4868         if (side->other && Geom::L2(side->pos - node->pos) < 1e-6)
4869             retracted ++;
4870         if (!side->other)
4871             endnode = true;
4872     }
4874     if (retracted == 0) {
4875         if (endnode) {
4876                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4877                 return _("end node");
4878         } else {
4879             switch (node->type) {
4880                 case Inkscape::NodePath::NODE_CUSP:
4881                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4882                     return _("cusp");
4883                 case Inkscape::NodePath::NODE_SMOOTH:
4884                     // TRANSLATORS: "smooth" is an adjective here
4885                     return _("smooth");
4886                 case Inkscape::NodePath::NODE_AUTO:
4887                     return _("auto");
4888                 case Inkscape::NodePath::NODE_SYMM:
4889                     return _("symmetric");
4890             }
4891         }
4892     } else if (retracted == 1) {
4893         if (endnode) {
4894             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4895             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4896         } else {
4897             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4898         }
4899     } else {
4900         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4901     }
4903     return NULL;
4906 /**
4907  * Handles content of statusbar as long as node tool is active.
4908  */
4909 void
4910 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4912     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");
4913     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4915     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4916     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4917     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4918     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4920     SPDesktop *desktop = NULL;
4921     if (nodepath) {
4922         desktop = nodepath->desktop;
4923     } else {
4924         desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above
4925     }
4927     SPEventContext *ec = desktop->event_context;
4928     if (!ec) return;
4930     Inkscape::MessageContext *mc = get_message_context(ec);
4931     if (!mc) return;
4933     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4935     if (selected_nodes == 0) {
4936         Inkscape::Selection *sel = desktop->selection;
4937         if (!sel || sel->isEmpty()) {
4938             mc->setF(Inkscape::NORMAL_MESSAGE,
4939                      _("Select a single object to edit its nodes or handles."));
4940         } else {
4941             if (nodepath) {
4942             mc->setF(Inkscape::NORMAL_MESSAGE,
4943                      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.",
4944                               "<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.",
4945                               total_nodes),
4946                      total_nodes);
4947             } else {
4948                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4949                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4950                 } else {
4951                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4952                 }
4953             }
4954         }
4955     } else if (nodepath && selected_nodes == 1) {
4956         mc->setF(Inkscape::NORMAL_MESSAGE,
4957                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4958                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4959                           total_nodes),
4960                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4961     } else {
4962         if (selected_subpaths > 1) {
4963             mc->setF(Inkscape::NORMAL_MESSAGE,
4964                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4965                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4966                               total_nodes),
4967                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4968         } else {
4969             mc->setF(Inkscape::NORMAL_MESSAGE,
4970                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4971                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4972                               total_nodes),
4973                      selected_nodes, total_nodes, when_selected);
4974         }
4975     }
4978 /*
4979  * returns a *copy* of the curve of that object.
4980  */
4981 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4982     if (!object)
4983         return NULL;
4985     SPCurve *curve = NULL;
4986     if (SP_IS_PATH(object)) {
4987         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4988         curve = curve_new->copy();
4989     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4990         const gchar *svgd = object->repr->attribute(key);
4991         if (svgd) {
4992             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4993             SPCurve *curve_new = new SPCurve(pv);
4994             if (curve_new) {
4995                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4996             }
4997         }
4998     }
5000     return curve;
5003 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
5004     if (!np || !np->object || !curve)
5005         return;
5007     if (SP_IS_PATH(np->object)) {
5008         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
5009             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
5010         } else {
5011             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
5012         }
5013     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
5014         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(np->object)->get_lpe();
5015         if (lpe) {
5016             Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( lpe->getParameter(np->repr_key) );
5017             if (pathparam) {
5018                 pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
5019                 np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
5020             }
5021         }
5022     }
5025 /*
5026 SPCanvasItem *
5027 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
5028     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
5030 */
5033 /// \todo this code to generate a helper canvasitem from an spcurve should be moved to different file
5034 SPCanvasItem *
5035 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const Geom::Matrix & i2d, guint32 color = 0xff0000ff) {
5036     SPCurve *flash_curve = curve->copy();
5037     flash_curve->transform(i2d);
5038     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
5039     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
5040     // unless we also flash the nodes...
5041     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
5042     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
5043     sp_canvas_item_show(canvasitem);
5044     flash_curve->unref();
5045     return canvasitem;
5048 SPCanvasItem *
5049 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item) {
5050     if (!item || !desktop) {
5051         return NULL;
5052     }
5054     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5055     guint32 color = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
5057     Geom::Matrix i2d = sp_item_i2d_affine(item);
5059     SPCurve *curve = NULL;
5060     if (SP_IS_PATH(item)) {
5061         curve = sp_path_get_curve_for_edit(SP_PATH(item));
5062     } else {
5063         g_warning ("-----> sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item): TODO: generate the helper path for this item type!\n");
5064         return NULL;
5065     }
5067     SPCanvasItem * helperpath = sp_nodepath_generate_helperpath(desktop, curve, i2d, color);
5069     curve->unref();
5071     return helperpath;
5075 // TODO: Merge this with sp_nodepath_make_helper_item()!
5076 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
5077     np->show_helperpath = show;
5079     if (show) {
5080         SPCurve *helper_curve = np->curve->copy();
5081         helper_curve->transform(np->i2d);
5082         if (!np->helper_path) {
5083             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
5085             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
5086             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);
5087             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
5088             sp_canvas_item_move_to_z(np->helper_path, 0);
5089             sp_canvas_item_show(np->helper_path);
5090         } else {
5091             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
5092         }
5093         helper_curve->unref();
5094     } else {
5095         if (np->helper_path) {
5096             GtkObject *temp = np->helper_path;
5097             np->helper_path = NULL;
5098             gtk_object_destroy(temp);
5099         }
5100     }
5103 /* sp_nodepath_make_straight_path:
5104  *   Prevents user from curving the path by dragging a segment or activating handles etc.
5105  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
5106  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
5107  */
5108 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
5109     np->straight_path = true;
5110     np->show_handles = false;
5111     g_message("add code to make the path straight.");
5112     // do sp_nodepath_convert_node_type on all nodes?
5113     // coding tip: search for this text : "Make selected segments lines"
5116 /*
5117   Local Variables:
5118   mode:c++
5119   c-file-style:"stroustrup"
5120   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5121   indent-tabs-mode:nil
5122   fill-column:99
5123   End:
5124 */
5125 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :