Code

NR::Maybe => boost::optional
[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 "shape-editor.h"
41 #include "selection-chemistry.h"
42 #include "selection.h"
43 #include "xml/repr.h"
44 #include "prefs-utils.h"
45 #include "sp-metrics.h"
46 #include "sp-path.h"
47 #include "libnr/nr-matrix-ops.h"
48 #include "svg/svg.h"
49 #include "verbs.h"
50 #include "display/bezier-utils.h"
51 #include <vector>
52 #include <algorithm>
53 #include <cstring>
54 #include <cmath>
55 #include <string>
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 class NR::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);
124 /* Node event callbacks */
125 static void node_clicked(SPKnot *knot, guint state, gpointer data);
126 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
127 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
128 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
130 /* Handle event callbacks */
131 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
132 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
133 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
134 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
135 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
136 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
138 /* Constructors and destructors */
140 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
141 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
142 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
143 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
144 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
145                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
146 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
148 /* Helpers */
150 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
151 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
152 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
154 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
155 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
157 // active_node indicates mouseover node
158 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
160 static SPCanvasItem *
161 sp_nodepath_make_helper_item(Inkscape::NodePath::Path *np, /*SPDesktop *desktop, */const SPCurve *curve, bool show = false) {
162     SPCurve *helper_curve = curve->copy();
163     helper_curve->transform(to_2geom(np->i2d));
164     SPCanvasItem *helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
165     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
166     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helper_path), 0, SP_WIND_RULE_NONZERO);
167     sp_canvas_item_move_to_z(helper_path, 0);
168     if (show) {
169         sp_canvas_item_show(helper_path);
170     }
171     helper_curve->unref();
172     return helper_path;
175 static SPCanvasItem *
176 canvasitem_from_pathvec(Inkscape::NodePath::Path *np, Geom::PathVector const &pathv, bool show) {
177     SPCurve *helper_curve = new SPCurve(pathv);
178     return sp_nodepath_make_helper_item(np, helper_curve, show);
181 static void
182 sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) {
183     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> >* helper_path_vec;
184     if (!SP_IS_LPE_ITEM(np->item)) {
185         g_print ("Only LPEItems can have helperpaths!\n");
186         return;
187     }
189     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
190     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
191     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
192         Inkscape::LivePathEffect::LPEObjectReference *lperef = (*i);
193         Inkscape::LivePathEffect::Effect *lpe = lperef->lpeobject->lpe;
194         // create new canvas items from the effect's helper paths
195         std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
196         for (std::vector<Geom::PathVector>::iterator j = hpaths.begin(); j != hpaths.end(); ++j) {
197             (*np->helper_path_vec)[lpe].push_back(canvasitem_from_pathvec(np, *j, true));
198         }
199     }
202 void
203 sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np) {
204     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> >* helper_path_vec;
205     if (!SP_IS_LPE_ITEM(np->item)) {
206         g_print ("Only LPEItems can have helperpaths!\n");
207         return;
208     }
210     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
211     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
212     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
213         Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->lpe;
214         /* update canvas items from the effect's helper paths; note that this code relies on the
215          * fact that getHelperPaths() will always return the same number of helperpaths in the same
216          * order as during their creation in sp_nodepath_create_helperpaths
217          */
218         std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
219         for (unsigned int j = 0; j < hpaths.size(); ++j) {
220             SPCurve *curve = new SPCurve(hpaths[j]);
221             curve->transform(to_2geom(np->i2d));
222             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(((*np->helper_path_vec)[lpe])[j]), curve);
223             curve = curve->unref();
224         }
225     }
228 static void
229 sp_nodepath_destroy_helperpaths(Inkscape::NodePath::Path *np) {
230     for (HelperPathList::iterator i = np->helper_path_vec->begin(); i != np->helper_path_vec->end(); ++i) {
231         for (std::vector<SPCanvasItem *>::iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) {
232             GtkObject *temp = *j;
233             *j = NULL;
234             gtk_object_destroy(temp);
235         }
236     }
240 /**
241  * \brief Creates new nodepath from item
242  */
243 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
245     Inkscape::XML::Node *repr = object->repr;
247     /** \todo
248      * FIXME: remove this. We don't want to edit paths inside flowtext.
249      * Instead we will build our flowtext with cloned paths, so that the
250      * real paths are outside the flowtext and thus editable as usual.
251      */
252     if (SP_IS_FLOWTEXT(object)) {
253         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
254             if SP_IS_FLOWREGION(child) {
255                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
256                 if (grandchild && SP_IS_PATH(grandchild)) {
257                     object = SP_ITEM(grandchild);
258                     break;
259                 }
260             }
261         }
262     }
264     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
266     if (curve == NULL)
267         return NULL;
269     if (curve->get_segment_count() < 1) {
270         curve->unref();
271         return NULL; // prevent crash for one-node paths
272     }
274     //Create new nodepath
275     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
276     if (!np) {
277         curve->unref();
278         return NULL;
279     }
281     // Set defaults
282     np->desktop     = desktop;
283     np->object      = object;
284     np->subpaths    = NULL;
285     np->selected    = NULL;
286     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
287     np->local_change = 0;
288     np->show_handles = show_handles;
289     np->helper_path = NULL;
290     np->helper_path_vec = new HelperPathList;
291     np->helperpath_rgba = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
292     np->helperpath_width = 1.0;
293     np->curve = curve->copy();
294     np->show_helperpath = (prefs_get_int_attribute ("tools.nodes", "show_helperpath",  0) == 1);
295     if (SP_IS_LPE_ITEM(object)) {
296         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
297         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
298             np->show_helperpath = true;
299         }            
300     }
301     np->straight_path = false;
302     if (IS_LIVEPATHEFFECT(object) && item) {
303         np->item = item;
304     } else {
305         np->item = SP_ITEM(object);
306     }
308     // we need to update item's transform from the repr here,
309     // because they may be out of sync when we respond
310     // to a change in repr by regenerating nodepath     --bb
311     sp_object_read_attr(SP_OBJECT(np->item), "transform");
313     np->i2d  = from_2geom(sp_item_i2d_affine(np->item));
314     np->d2i  = np->i2d.inverse();
316     np->repr = repr;
317     if (repr_key_in) { // apparantly the object is an LPEObject
318         np->repr_key = g_strdup(repr_key_in);
319         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
320         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
321         if (lpeparam) {
322             lpeparam->param_setup_nodepath(np);
323         }
324     } else {
325         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
326         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
327             np->repr_key = g_strdup("inkscape:original-d");
329             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
330             if (lpe) {
331                 lpe->setup_nodepath(np);
332             }
333         } else {
334             np->repr_key = g_strdup("d");
335         }
336     }
338     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
339      * So for example a closed rectangle has a nodetypestring of length 5.
340      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
341     Geom::PathVector pathv_sanitized = pathv_to_linear_and_cubic_beziers(np->curve->get_pathvector());
342     np->curve->set_pathvector(pathv_sanitized);
343     guint length = np->curve->get_segment_count();
344     for (Geom::PathVector::const_iterator pit = pathv_sanitized.begin(); pit != pathv_sanitized.end(); ++pit) {
345         length += pit->empty() ? 0 : 1;
346     }
348     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
349     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
351     // create the subpath(s) from the bpath
352     subpaths_from_pathvector(np, pathv_sanitized, typestr);
354     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
355     np->subpaths = g_list_reverse(np->subpaths);
357     delete[] typestr;
358     curve->unref();
360     // Draw helper curve
361     if (np->show_helperpath) {
362         np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true);
363     }
365     sp_nodepath_create_helperpaths(np);
367     return np;
370 /**
371  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
372  */
373 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
375     if (!np)  //soft fail, like delete
376         return;
378     while (np->subpaths) {
379         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
380     }
382     //Inform the ShapeEditor that made me, if any, that I am gone.
383     if (np->shape_editor)
384         np->shape_editor->nodepath_destroyed();
386     g_assert(!np->selected);
388     if (np->helper_path) {
389         GtkObject *temp = np->helper_path;
390         np->helper_path = NULL;
391         gtk_object_destroy(temp);
392     }
393     if (np->curve) {
394         np->curve->unref();
395         np->curve = NULL;
396     }
398     if (np->repr_key) {
399         g_free(np->repr_key);
400         np->repr_key = NULL;
401     }
402     if (np->repr_nodetypes_key) {
403         g_free(np->repr_nodetypes_key);
404         np->repr_nodetypes_key = NULL;
405     }
407     sp_nodepath_destroy_helperpaths(np);
408     delete np->helper_path_vec;
409     np->helper_path_vec = NULL;
411     np->desktop = NULL;
413     g_free(np);
416 /**
417  *  Return the node count of a given NodeSubPath.
418  */
419 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
421     if (!subpath)
422         return 0;
423     gint nodeCount = g_list_length(subpath->nodes);
424     return nodeCount;
427 /**
428  *  Return the node count of a given NodePath.
429  */
430 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
432     if (!np)
433         return 0;
434     gint nodeCount = 0;
435     for (GList *item = np->subpaths ; item ; item=item->next) {
436        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
437         nodeCount += g_list_length(subpath->nodes);
438     }
439     return nodeCount;
442 /**
443  *  Return the subpath count of a given NodePath.
444  */
445 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
447     if (!np)
448         return 0;
449     return g_list_length (np->subpaths);
452 /**
453  *  Return the selected node count of a given NodePath.
454  */
455 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
457     if (!np)
458         return 0;
459     return g_list_length (np->selected);
462 /**
463  *  Return the number of subpaths where nodes are selected in a given NodePath.
464  */
465 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
467     if (!np)
468         return 0;
469     if (!np->selected)
470         return 0;
471     if (!np->selected->next)
472         return 1;
473     gint count = 0;
474     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
475         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
476         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
477             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
478             if (node->selected) {
479                 count ++;
480                 break;
481             }
482         }
483     }
484     return count;
487 /**
488  * Clean up a nodepath after editing.
489  *
490  * Currently we are deleting trivial subpaths.
491  */
492 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
494     GList *badSubPaths = NULL;
496     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
497     for (GList *l = nodepath->subpaths; l ; l=l->next) {
498        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
499        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
500             badSubPaths = g_list_append(badSubPaths, sp);
501     }
503     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
504     //also removes the subpath from nodepath->subpaths
505     for (GList *l = badSubPaths; l ; l=l->next) {
506        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
507         sp_nodepath_subpath_destroy(sp);
508     }
510     g_list_free(badSubPaths);
513 /**
514  * Create new nodepaths from pathvector, make it subpaths of np.
515  * \param t The node type array.
516  */
517 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
519     guint i = 0;  // index into node type array
520     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
521         if (pit->empty())
522             continue;  // don't add single knot paths
524         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
526         NR::Point ppos = from_2geom(pit->initialPoint()) * np->i2d;
527         NRPathcode pcode = NR_MOVETO;
529         /* 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)*/
530         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
531             if( dynamic_cast<Geom::LineSegment const*>(&*cit) ||
532                 dynamic_cast<Geom::HLineSegment const*>(&*cit) ||
533                 dynamic_cast<Geom::VLineSegment const*>(&*cit) )
534             {
535                 NR::Point pos = from_2geom(cit->initialPoint()) * np->i2d;
536                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
538                 ppos = from_2geom(cit->finalPoint()) * np->i2d;
539                 pcode = NR_LINETO;
540             }
541             else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
542                 std::vector<Geom::Point> points = cubic_bezier->points();
543                 NR::Point pos = from_2geom(points[0]) * np->i2d;
544                 NR::Point npos = from_2geom(points[1]) * np->i2d;
545                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
547                 ppos = from_2geom(points[2]) * np->i2d;
548                 pcode = NR_CURVETO;
549             }
550         }
552         if (pit->closed()) {
553             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
554             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
555              * If the length is zero, don't add it to the nodepath. */
556             Geom::Curve const &closing_seg = pit->back_closed();
557             if ( ! closing_seg.isDegenerate() ) {
558                 NR::Point pos = from_2geom(closing_seg.finalPoint()) * np->i2d;
559                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
560             }
562             sp_nodepath_subpath_close(sp);
563         }
564     }
567 /**
568  * Convert from sodipodi:nodetypes to new style type array.
569  */
570 static
571 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
573     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
575     guint pos = 0;
577     if (types) {
578         for (guint i = 0; types[i] && ( i < length ); i++) {
579             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
580             if (types[i] != '\0') {
581                 switch (types[i]) {
582                     case 's':
583                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
584                         break;
585                     case 'z':
586                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
587                         break;
588                     case 'c':
589                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
590                         break;
591                     default:
592                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
593                         break;
594                 }
595             }
596         }
597     }
599     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
601     return typestr;
604 /**
605  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
606  * updated but repr is not (for speed). Used during curve and node drag.
607  */
608 static void update_object(Inkscape::NodePath::Path *np)
610     g_assert(np);
612     np->curve->unref();
613     np->curve = create_curve(np);
615     sp_nodepath_set_curve(np, np->curve);
617     if (np->show_helperpath) {
618         SPCurve * helper_curve = np->curve->copy();
619         helper_curve->transform(to_2geom(np->i2d));
620         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
621         helper_curve->unref();
622     }
624     // updating helperpaths of LPEItems is now done in sp_lpe_item_update();
625     //sp_nodepath_update_helperpaths(np);
627     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
628     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
629     np->shape_editor->update_knotholder();
632 /**
633  * Update XML path node with data from path object.
634  */
635 static void update_repr_internal(Inkscape::NodePath::Path *np)
637     g_assert(np);
639     Inkscape::XML::Node *repr = np->object->repr;
641     np->curve->unref();
642     np->curve = create_curve(np);
644     gchar *typestr = create_typestr(np);
645     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
647     // determine if path has an effect applied and write to correct "d" attribute.
648     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
649         np->local_change++;
650         repr->setAttribute(np->repr_key, svgpath);
651     }
653     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
654         np->local_change++;
655         repr->setAttribute(np->repr_nodetypes_key, typestr);
656     }
658     g_free(svgpath);
659     g_free(typestr);
661     if (np->show_helperpath) {
662         SPCurve * helper_curve = np->curve->copy();
663         helper_curve->transform(to_2geom(np->i2d));
664         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
665         helper_curve->unref();
666     }
668     // TODO: do we need this call here? after all, update_object() should have been called just before
669     //sp_nodepath_update_helperpaths(np);
672 /**
673  * Update XML path node with data from path object, commit changes forever.
674  */
675 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
677     //fixme: np can be NULL, so check before proceeding
678     g_return_if_fail(np != NULL);
680     update_repr_internal(np);
681     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
683     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
684                      annotation);
687 /**
688  * Update XML path node with data from path object, commit changes with undo.
689  */
690 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
692     update_repr_internal(np);
693     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
694                            annotation);
697 /**
698  * Make duplicate of path, replace corresponding XML node in tree, commit.
699  */
700 static void stamp_repr(Inkscape::NodePath::Path *np)
702     g_assert(np);
704     Inkscape::XML::Node *old_repr = np->object->repr;
705     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
707     // remember the position of the item
708     gint pos = old_repr->position();
709     // remember parent
710     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
712     SPCurve *curve = create_curve(np);
713     gchar *typestr = create_typestr(np);
715     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
717     new_repr->setAttribute(np->repr_key, svgpath);
718     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
720     // add the new repr to the parent
721     parent->appendChild(new_repr);
722     // move to the saved position
723     new_repr->setPosition(pos > 0 ? pos : 0);
725     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
726                      _("Stamp"));
728     Inkscape::GC::release(new_repr);
729     g_free(svgpath);
730     g_free(typestr);
731     curve->unref();
734 /**
735  * Create curve from path.
736  */
737 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
739     SPCurve *curve = new SPCurve();
741     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
742        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
743         curve->moveto(sp->first->pos * np->d2i);
744        Inkscape::NodePath::Node *n = sp->first->n.other;
745         while (n) {
746             NR::Point const end_pt = n->pos * np->d2i;
747             switch (n->code) {
748                 case NR_LINETO:
749                     curve->lineto(end_pt);
750                     break;
751                 case NR_CURVETO:
752                     curve->curveto(n->p.other->n.pos * np->d2i,
753                                      n->p.pos * np->d2i,
754                                      end_pt);
755                     break;
756                 default:
757                     g_assert_not_reached();
758                     break;
759             }
760             if (n != sp->last) {
761                 n = n->n.other;
762             } else {
763                 n = NULL;
764             }
765         }
766         if (sp->closed) {
767             curve->closepath();
768         }
769     }
771     return curve;
774 /**
775  * Convert path type string to sodipodi:nodetypes style.
776  */
777 static gchar *create_typestr(Inkscape::NodePath::Path *np)
779     gchar *typestr = g_new(gchar, 32);
780     gint len = 32;
781     gint pos = 0;
783     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
784        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
786         if (pos >= len) {
787             typestr = g_renew(gchar, typestr, len + 32);
788             len += 32;
789         }
791         typestr[pos++] = 'c';
793        Inkscape::NodePath::Node *n;
794         n = sp->first->n.other;
795         while (n) {
796             gchar code;
798             switch (n->type) {
799                 case Inkscape::NodePath::NODE_CUSP:
800                     code = 'c';
801                     break;
802                 case Inkscape::NodePath::NODE_SMOOTH:
803                     code = 's';
804                     break;
805                 case Inkscape::NodePath::NODE_SYMM:
806                     code = 'z';
807                     break;
808                 default:
809                     g_assert_not_reached();
810                     code = '\0';
811                     break;
812             }
814             if (pos >= len) {
815                 typestr = g_renew(gchar, typestr, len + 32);
816                 len += 32;
817             }
819             typestr[pos++] = code;
821             if (n != sp->last) {
822                 n = n->n.other;
823             } else {
824                 n = NULL;
825             }
826         }
827     }
829     if (pos >= len) {
830         typestr = g_renew(gchar, typestr, len + 1);
831         len += 1;
832     }
834     typestr[pos++] = '\0';
836     return typestr;
839 /**
840  * Returns current path in context. // later eliminate this function at all!
841  */
842 static Inkscape::NodePath::Path *sp_nodepath_current()
844     if (!SP_ACTIVE_DESKTOP) {
845         return NULL;
846     }
848     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
850     if (!SP_IS_NODE_CONTEXT(event_context)) {
851         return NULL;
852     }
854     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
859 /**
860  \brief Fills node and handle positions for three nodes, splitting line
861   marked by end at distance t.
862  */
863 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
865     g_assert(new_path != NULL);
866     g_assert(end      != NULL);
868     g_assert(end->p.other == new_path);
869    Inkscape::NodePath::Node *start = new_path->p.other;
870     g_assert(start);
872     if (end->code == NR_LINETO) {
873         new_path->type =Inkscape::NodePath::NODE_CUSP;
874         new_path->code = NR_LINETO;
875         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
876     } else {
877         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
878         new_path->code = NR_CURVETO;
879         gdouble s      = 1 - t;
880         for (int dim = 0; dim < 2; dim++) {
881             NR::Coord const f000 = start->pos[dim];
882             NR::Coord const f001 = start->n.pos[dim];
883             NR::Coord const f011 = end->p.pos[dim];
884             NR::Coord const f111 = end->pos[dim];
885             NR::Coord const f00t = s * f000 + t * f001;
886             NR::Coord const f01t = s * f001 + t * f011;
887             NR::Coord const f11t = s * f011 + t * f111;
888             NR::Coord const f0tt = s * f00t + t * f01t;
889             NR::Coord const f1tt = s * f01t + t * f11t;
890             NR::Coord const fttt = s * f0tt + t * f1tt;
891             start->n.pos[dim]    = f00t;
892             new_path->p.pos[dim] = f0tt;
893             new_path->pos[dim]   = fttt;
894             new_path->n.pos[dim] = f1tt;
895             end->p.pos[dim]      = f11t;
896         }
897     }
900 /**
901  * Adds new node on direct line between two nodes, activates handles of all
902  * three nodes.
903  */
904 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
906     g_assert(end);
907     g_assert(end->subpath);
908     g_assert(g_list_find(end->subpath->nodes, end));
910    Inkscape::NodePath::Node *start = end->p.other;
911     g_assert( start->n.other == end );
912    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
913                                                end,
914                                                (NRPathcode)end->code == NR_LINETO?
915                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
916                                                (NRPathcode)end->code,
917                                                &start->pos, &start->pos, &start->n.pos);
918     sp_nodepath_line_midpoint(newnode, end, t);
920     sp_node_adjust_handles(start);
921     sp_node_update_handles(start);
922     sp_node_update_handles(newnode);
923     sp_node_adjust_handles(end);
924     sp_node_update_handles(end);
926     return newnode;
929 /**
930 \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
931 */
932 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
934     g_assert(node);
935     g_assert(node->subpath);
936     g_assert(g_list_find(node->subpath->nodes, node));
938    Inkscape::NodePath::SubPath *sp = node->subpath;
939     Inkscape::NodePath::Path *np    = sp->nodepath;
941     if (sp->closed) {
942         sp_nodepath_subpath_open(sp, node);
943         return sp->first;
944     } else {
945         // no break for end nodes
946         if (node == sp->first) return NULL;
947         if (node == sp->last ) return NULL;
949         // create a new subpath
950        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
952         // duplicate the break node as start of the new subpath
953         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
955         // attach rest of curve to new node
956         g_assert(node->n.other);
957         newnode->n.other = node->n.other; node->n.other = NULL;
958         newnode->n.other->p.other = newnode;
959         newsubpath->last = sp->last;
960         sp->last = node;
961         node = newnode;
962         while (node->n.other) {
963             node = node->n.other;
964             node->subpath = newsubpath;
965             sp->nodes = g_list_remove(sp->nodes, node);
966             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
967         }
970         return newnode;
971     }
974 /**
975  * Duplicate node and connect to neighbours.
976  */
977 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
979     g_assert(node);
980     g_assert(node->subpath);
981     g_assert(g_list_find(node->subpath->nodes, node));
983    Inkscape::NodePath::SubPath *sp = node->subpath;
985     NRPathcode code = (NRPathcode) node->code;
986     if (code == NR_MOVETO) { // if node is the endnode,
987         node->code = NR_LINETO; // new one is inserted before it, so change that to line
988     }
990     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
992     if (!node->n.other || !node->p.other) // if node is an endnode, select it
993         return node;
994     else
995         return newnode; // otherwise select the newly created node
998 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
1000     node->p.pos = (node->pos + (node->pos - node->n.pos));
1003 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
1005     node->n.pos = (node->pos + (node->pos - node->p.pos));
1008 /**
1009  * Change line type at node, with side effects on neighbours.
1010  */
1011 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
1013     g_assert(end);
1014     g_assert(end->subpath);
1015     g_assert(end->p.other);
1017     if (end->code == static_cast< guint > ( code ) )
1018         return;
1020    Inkscape::NodePath::Node *start = end->p.other;
1022     end->code = code;
1024     if (code == NR_LINETO) {
1025         if (start->code == NR_LINETO) {
1026             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
1027         }
1028         if (end->n.other) {
1029             if (end->n.other->code == NR_LINETO) {
1030                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
1031             }
1032         }
1033     } else {
1034         NR::Point delta = end->pos - start->pos;
1035         start->n.pos = start->pos + delta / 3;
1036         end->p.pos = end->pos - delta / 3;
1037         sp_node_adjust_handle(start, 1);
1038         sp_node_adjust_handle(end, -1);
1039     }
1041     sp_node_update_handles(start);
1042     sp_node_update_handles(end);
1045 /**
1046  * Change node type, and its handles accordingly.
1047  */
1048 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1050     g_assert(node);
1051     g_assert(node->subpath);
1053     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1054         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1055             type =Inkscape::NodePath::NODE_CUSP;
1056         }
1057     }
1059     node->type = type;
1061     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1062         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1063         node->knot->setSize (node->selected? 11 : 9);
1064         sp_knot_update_ctrl(node->knot);
1065     } else {
1066         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1067         node->knot->setSize (node->selected? 9 : 7);
1068         sp_knot_update_ctrl(node->knot);
1069     }
1071     // if one of handles is mouseovered, preserve its position
1072     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1073         sp_node_adjust_handle(node, 1);
1074     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1075         sp_node_adjust_handle(node, -1);
1076     } else {
1077         sp_node_adjust_handles(node);
1078     }
1080     sp_node_update_handles(node);
1082     sp_nodepath_update_statusbar(node->subpath->nodepath);
1084     return node;
1087 bool
1088 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1090         Inkscape::NodePath::Node *othernode = side->other;
1091         if (!othernode)
1092             return false;
1093         NRPathcode const code = sp_node_path_code_from_side(node, side);
1094         if (code == NR_LINETO)
1095             return true;
1096         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1097         if (&node->p == side) {
1098             other_to_me = &othernode->n;
1099         } else if (&node->n == side) {
1100             other_to_me = &othernode->p;
1101         } 
1102         if (!other_to_me)
1103             return false;
1104         bool is_line = 
1105              (NR::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1106               NR::L2(node->pos - side->pos) < 1e-6);
1107         return is_line;
1110 /**
1111  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1112  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1113  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1114  * If already cusp and set to cusp, retracts handles.
1115 */
1116 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1118     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1120 /* 
1121   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1122  
1123         if (two_handles) {
1124             // do nothing, adjust_handles called via set_node_type will line them up
1125         } else if (one_handle) {
1126             if (opposite_to_handle_is_line) {
1127                 if (lined_up) {
1128                     // already half-smooth; pull opposite handle too making it fully smooth
1129                 } else {
1130                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1131                 }
1132             } else {
1133                 // pull opposite handle in line with the existing one
1134             }
1135         } else if (no_handles) {
1136             if (both_segments_are_lines OR both_segments_are_curves) {
1137                 //pull both handles
1138             } else {
1139                 // pull the handle opposite to line segment, making node half-smooth
1140             }
1141         }
1142 */
1143         bool p_has_handle = (NR::L2(node->pos  - node->p.pos) > 1e-6);
1144         bool n_has_handle = (NR::L2(node->pos  - node->n.pos) > 1e-6);
1145         bool p_is_line = sp_node_side_is_line(node, &node->p);
1146         bool n_is_line = sp_node_side_is_line(node, &node->n);
1148         if (p_has_handle && n_has_handle) {
1149             // do nothing, adjust_handles will line them up
1150         } else if (p_has_handle || n_has_handle) {
1151             if (p_has_handle && n_is_line) {
1152                 Radial line (node->n.other->pos - node->pos);
1153                 Radial handle (node->pos - node->p.pos);
1154                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1155                     // already half-smooth; pull opposite handle too making it fully smooth
1156                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1157                 } else {
1158                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1159                 }
1160             } else if (n_has_handle && p_is_line) {
1161                 Radial line (node->p.other->pos - node->pos);
1162                 Radial handle (node->pos - node->n.pos);
1163                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1164                     // already half-smooth; pull opposite handle too making it fully smooth
1165                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1166                 } else {
1167                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1168                 }
1169             } else if (p_has_handle && node->n.other) {
1170                 // pull n handle
1171                 node->n.other->code = NR_CURVETO;
1172                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1173                     NR::L2(node->p.pos - node->pos) :
1174                     NR::L2(node->n.other->pos - node->pos) / 3;
1175                 node->n.pos = node->pos - (len / NR::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1176             } else if (n_has_handle && node->p.other) {
1177                 // pull p handle
1178                 node->code = NR_CURVETO;
1179                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1180                     NR::L2(node->n.pos - node->pos) :
1181                     NR::L2(node->p.other->pos - node->pos) / 3;
1182                 node->p.pos = node->pos - (len / NR::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1183             }
1184         } else if (!p_has_handle && !n_has_handle) {
1185             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1186                 // no handles, but both segments are either lnes or curves:
1187                 //pull both handles
1189                 // convert both to curves:
1190                 node->code = NR_CURVETO;
1191                 node->n.other->code = NR_CURVETO;
1193                 NR::Point leg_prev = node->pos - node->p.other->pos;
1194                 NR::Point leg_next = node->pos - node->n.other->pos;
1196                 double norm_leg_prev = L2(leg_prev);
1197                 double norm_leg_next = L2(leg_next);
1199                 NR::Point delta;
1200                 if (norm_leg_next > 0.0) {
1201                     delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1202                     (&delta)->normalize();
1203                 }
1205                 if (type == Inkscape::NodePath::NODE_SYMM) {
1206                     double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1207                     node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1208                     node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1209                 } else {
1210                     // length of handle is proportional to distance to adjacent node
1211                     node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1212                     node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1213                 }
1215             } else {
1216                 // pull the handle opposite to line segment, making it half-smooth
1217                 if (p_is_line && node->n.other) {
1218                     if (type != Inkscape::NodePath::NODE_SYMM) {
1219                         // pull n handle
1220                         node->n.other->code = NR_CURVETO;
1221                         double len =  NR::L2(node->n.other->pos - node->pos) / 3;
1222                         node->n.pos = node->pos + (len / NR::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1223                     }
1224                 } else if (n_is_line && node->p.other) {
1225                     if (type != Inkscape::NodePath::NODE_SYMM) {
1226                         // pull p handle
1227                         node->code = NR_CURVETO;
1228                         double len =  NR::L2(node->p.other->pos - node->pos) / 3;
1229                         node->p.pos = node->pos + (len / NR::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1230                     }
1231                 }
1232             }
1233         }
1234     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1235         // cusping a cusp: retract nodes
1236         node->p.pos = node->pos;
1237         node->n.pos = node->pos;
1238     }
1240     sp_nodepath_set_node_type (node, type);
1243 /**
1244  * Move node to point, and adjust its and neighbouring handles.
1245  */
1246 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1248     NR::Point delta = p - node->pos;
1249     node->pos = p;
1251     node->p.pos += delta;
1252     node->n.pos += delta;
1254     Inkscape::NodePath::Node *node_p = NULL;
1255     Inkscape::NodePath::Node *node_n = NULL;
1257     if (node->p.other) {
1258         if (node->code == NR_LINETO) {
1259             sp_node_adjust_handle(node, 1);
1260             sp_node_adjust_handle(node->p.other, -1);
1261             node_p = node->p.other;
1262         }
1263     }
1264     if (node->n.other) {
1265         if (node->n.other->code == NR_LINETO) {
1266             sp_node_adjust_handle(node, -1);
1267             sp_node_adjust_handle(node->n.other, 1);
1268             node_n = node->n.other;
1269         }
1270     }
1272     // this function is only called from batch movers that will update display at the end
1273     // themselves, so here we just move all the knots without emitting move signals, for speed
1274     sp_node_update_handles(node, false);
1275     if (node_n) {
1276         sp_node_update_handles(node_n, false);
1277     }
1278     if (node_p) {
1279         sp_node_update_handles(node_p, false);
1280     }
1283 /**
1284  * Call sp_node_moveto() for node selection and handle possible snapping.
1285  */
1286 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1287                                             bool const snap, bool constrained = false, 
1288                                             Inkscape::Snapper::ConstraintLine const &constraint = NR::Point())
1290     NR::Coord best = NR_HUGE;
1291     NR::Point delta(dx, dy);
1292     NR::Point best_pt = delta;
1293     Inkscape::SnappedPoint best_abs;
1294     
1295     if (snap) {    
1296         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1297          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1298          * must provide that information. */
1299           
1300         // Build a list of the unselected nodes to which the snapper should snap 
1301         std::vector<NR::Point> unselected_nodes;
1302         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1303             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1304             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1305                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1306                 if (!node->selected) {
1307                     unselected_nodes.push_back(node->pos);
1308                 }    
1309             }
1310         }        
1311         
1312         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1313         
1314         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1315             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1316             m.setup(NULL, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1317             Inkscape::SnappedPoint s;
1318             if (constrained) {
1319                 Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1320                 dedicated_constraint.setPoint(n->pos);
1321                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, dedicated_constraint);
1322             } else {
1323                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta);
1324             }            
1325             if (s.getSnapped() && (s.getDistance() < best)) {
1326                 best = s.getDistance();
1327                 best_abs = s;
1328                 best_pt = s.getPoint() - n->pos;
1329             }
1330         }
1331                         
1332         if (best_abs.getSnapped()) {
1333             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1334         } else {
1335             nodepath->desktop->snapindicator->remove_snappoint();    
1336         }
1337     }
1339     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1340         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1341         sp_node_moveto(n, n->pos + best_pt);
1342     }
1344     // do not update repr here so that node dragging is acceptably fast
1345     update_object(nodepath);
1348 /**
1349 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1350 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1351 near x = 0.
1352  */
1353 double
1354 sculpt_profile (double x, double alpha, guint profile)
1356     if (x >= 1)
1357         return 0;
1358     if (x <= 0)
1359         return 1;
1361     switch (profile) {
1362         case SCULPT_PROFILE_LINEAR:
1363         return 1 - x;
1364         case SCULPT_PROFILE_BELL:
1365         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1366         case SCULPT_PROFILE_ELLIPTIC:
1367         return sqrt(1 - x*x);
1368     }
1370     return 1;
1373 double
1374 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1376     // extremely primitive for now, don't have time to look for the real one
1377     double lower = NR::L2(b - a);
1378     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1379     return (lower + upper)/2;
1382 void
1383 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1385     n->pos = n->origin + delta;
1386     n->n.pos = n->n.origin + delta_n;
1387     n->p.pos = n->p.origin + delta_p;
1388     sp_node_adjust_handles(n);
1389     sp_node_update_handles(n, false);
1392 /**
1393  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1394  * on how far they are from the dragged node n.
1395  */
1396 static void
1397 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1399     g_assert (n);
1400     g_assert (nodepath);
1401     g_assert (n->subpath->nodepath == nodepath);
1403     double pressure = n->knot->pressure;
1404     if (pressure == 0)
1405         pressure = 0.5; // default
1406     pressure = CLAMP (pressure, 0.2, 0.8);
1408     // map pressure to alpha = 1/5 ... 5
1409     double alpha = 1 - 2 * fabs(pressure - 0.5);
1410     if (pressure > 0.5)
1411         alpha = 1/alpha;
1413     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1415     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1416         // Only one subpath has selected nodes:
1417         // use linear mode, where the distance from n to node being dragged is calculated along the path
1419         double n_sel_range = 0, p_sel_range = 0;
1420         guint n_nodes = 0, p_nodes = 0;
1421         guint n_sel_nodes = 0, p_sel_nodes = 0;
1423         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1424         {
1425             double n_range = 0, p_range = 0;
1426             bool n_going = true, p_going = true;
1427             Inkscape::NodePath::Node *n_node = n;
1428             Inkscape::NodePath::Node *p_node = n;
1429             do {
1430                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1431                 if (n_node && n_going)
1432                     n_node = n_node->n.other;
1433                 if (n_node == NULL) {
1434                     n_going = false;
1435                 } else {
1436                     n_nodes ++;
1437                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1438                     if (n_node->selected) {
1439                         n_sel_nodes ++;
1440                         n_sel_range = n_range;
1441                     }
1442                     if (n_node == p_node) {
1443                         n_going = false;
1444                         p_going = false;
1445                     }
1446                 }
1447                 if (p_node && p_going)
1448                     p_node = p_node->p.other;
1449                 if (p_node == NULL) {
1450                     p_going = false;
1451                 } else {
1452                     p_nodes ++;
1453                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1454                     if (p_node->selected) {
1455                         p_sel_nodes ++;
1456                         p_sel_range = p_range;
1457                     }
1458                     if (p_node == n_node) {
1459                         n_going = false;
1460                         p_going = false;
1461                     }
1462                 }
1463             } while (n_going || p_going);
1464         }
1466         // Second pass: actually move nodes in this subpath
1467         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1468         {
1469             double n_range = 0, p_range = 0;
1470             bool n_going = true, p_going = true;
1471             Inkscape::NodePath::Node *n_node = n;
1472             Inkscape::NodePath::Node *p_node = n;
1473             do {
1474                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1475                 if (n_node && n_going)
1476                     n_node = n_node->n.other;
1477                 if (n_node == NULL) {
1478                     n_going = false;
1479                 } else {
1480                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1481                     if (n_node->selected) {
1482                         sp_nodepath_move_node_and_handles (n_node,
1483                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1484                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1485                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1486                     }
1487                     if (n_node == p_node) {
1488                         n_going = false;
1489                         p_going = false;
1490                     }
1491                 }
1492                 if (p_node && p_going)
1493                     p_node = p_node->p.other;
1494                 if (p_node == NULL) {
1495                     p_going = false;
1496                 } else {
1497                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1498                     if (p_node->selected) {
1499                         sp_nodepath_move_node_and_handles (p_node,
1500                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1501                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1502                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1503                     }
1504                     if (p_node == n_node) {
1505                         n_going = false;
1506                         p_going = false;
1507                     }
1508                 }
1509             } while (n_going || p_going);
1510         }
1512     } else {
1513         // Multiple subpaths have selected nodes:
1514         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1515         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1516         // fix the pear-like shape when sculpting e.g. a ring
1518         // First pass: calculate range
1519         gdouble direct_range = 0;
1520         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1521             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1522             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1523                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1524                 if (node->selected) {
1525                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1526                 }
1527             }
1528         }
1530         // Second pass: actually move nodes
1531         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1532             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1533             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1534                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1535                 if (node->selected) {
1536                     if (direct_range > 1e-6) {
1537                         sp_nodepath_move_node_and_handles (node,
1538                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1539                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1540                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1541                     } else {
1542                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1543                     }
1545                 }
1546             }
1547         }
1548     }
1550     // do not update repr here so that node dragging is acceptably fast
1551     update_object(nodepath);
1555 /**
1556  * Move node selection to point, adjust its and neighbouring handles,
1557  * handle possible snapping, and commit the change with possible undo.
1558  */
1559 void
1560 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1562     if (!nodepath) return;
1564     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1566     if (dx == 0) {
1567         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1568     } else if (dy == 0) {
1569         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1570     } else {
1571         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1572     }
1575 /**
1576  * Move node selection off screen and commit the change.
1577  */
1578 void
1579 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1581     // borrowed from sp_selection_move_screen in selection-chemistry.c
1582     // we find out the current zoom factor and divide deltas by it
1583     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1585     gdouble zoom = desktop->current_zoom();
1586     gdouble zdx = dx / zoom;
1587     gdouble zdy = dy / zoom;
1589     if (!nodepath) return;
1591     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1593     if (dx == 0) {
1594         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1595     } else if (dy == 0) {
1596         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1597     } else {
1598         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1599     }
1602 /**
1603  * Move selected nodes to the absolute position given
1604  */
1605 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis)
1607     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1608         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1609         Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]);
1610         sp_node_moveto(n, npos);
1611     }
1613     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1616 /**
1617  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1618  */
1619 boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1621     boost::optional<Geom::Coord> no_coord;
1622     g_return_val_if_fail(nodepath->selected, no_coord);
1624     // determine coordinate of first selected node
1625     GList *nsel = nodepath->selected;
1626     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1627     NR::Coord coord = n->pos[axis];
1628     bool coincide = true;
1630     // compare it to the coordinates of all the other selected nodes
1631     for (GList *l = nsel->next; l != NULL; l = l->next) {
1632         n = (Inkscape::NodePath::Node *) l->data;
1633         if (n->pos[axis] != coord) {
1634             coincide = false;
1635         }
1636     }
1637     if (coincide) {
1638         return coord;
1639     } else {
1640         Geom::Rect bbox = sp_node_selected_bbox(nodepath);
1641         // currently we return the coordinate of the bounding box midpoint because I don't know how
1642         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1643         return bbox.midpoint()[axis];
1644     }
1647 /** If they don't yet exist, creates knot and line for the given side of the node */
1648 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1650     if (!side->knot) {
1651         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"));
1653         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1654         side->knot->setSize (7);
1655         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1656         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1657         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1658         sp_knot_update_ctrl(side->knot);
1660         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1661         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1662         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1663         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1664         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1665         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1666     }
1668     if (!side->line) {
1669         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1670                                         SP_TYPE_CTRLLINE, NULL);
1671     }
1674 /**
1675  * Ensure the given handle of the node is visible/invisible, update its screen position
1676  */
1677 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1679     g_assert(node != NULL);
1681    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1682     NRPathcode code = sp_node_path_code_from_side(node, side);
1684     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1686     if (show_handle) {
1687         if (!side->knot) { // No handle knot at all
1688             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1689             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1690             side->knot->pos = side->pos;
1691             if (side->knot->item)
1692                 SP_CTRL(side->knot->item)->moveto(side->pos);
1693             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1694             sp_knot_show(side->knot);
1695         } else {
1696             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1697                 if (fire_move_signals) {
1698                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1699                 } else {
1700                     sp_knot_moveto(side->knot, side->pos);
1701                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1702                 }
1703             }
1704             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1705                 sp_knot_show(side->knot);
1706             }
1707         }
1708         sp_canvas_item_show(side->line);
1709     } else {
1710         if (side->knot) {
1711             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1712                 sp_knot_hide(side->knot);
1713             }
1714         }
1715         if (side->line) {
1716             sp_canvas_item_hide(side->line);
1717         }
1718     }
1721 /**
1722  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1723  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1724  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1725  * updated; otherwise, just move the knots silently (used in batch moves).
1726  */
1727 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1729     g_assert(node != NULL);
1731     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1732         sp_knot_show(node->knot);
1733     }
1735     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1736         if (fire_move_signals)
1737             sp_knot_set_position(node->knot, node->pos, 0);
1738         else
1739             sp_knot_moveto(node->knot, node->pos);
1740     }
1742     gboolean show_handles = node->selected;
1743     if (node->p.other != NULL) {
1744         if (node->p.other->selected) show_handles = TRUE;
1745     }
1746     if (node->n.other != NULL) {
1747         if (node->n.other->selected) show_handles = TRUE;
1748     }
1750     if (node->subpath->nodepath->show_handles == false)
1751         show_handles = FALSE;
1753     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1754     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1757 /**
1758  * Call sp_node_update_handles() for all nodes on subpath.
1759  */
1760 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1762     g_assert(subpath != NULL);
1764     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1765         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1766     }
1769 /**
1770  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1771  */
1772 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1774     g_assert(nodepath != NULL);
1776     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1777         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1778     }
1781 void
1782 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1784     if (nodepath == NULL) return;
1786     nodepath->show_handles = show;
1787     sp_nodepath_update_handles(nodepath);
1790 /**
1791  * Adds all selected nodes in nodepath to list.
1792  */
1793 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1795     StlConv<Node *>::list(l, selected);
1796 /// \todo this adds a copying, rework when the selection becomes a stl list
1799 /**
1800  * Align selected nodes on the specified axis.
1801  */
1802 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1804     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1805         return;
1806     }
1808     if ( !nodepath->selected->next ) { // only one node selected
1809         return;
1810     }
1811    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1812     NR::Point dest(pNode->pos);
1813     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1814         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1815         if (pNode) {
1816             dest[axis] = pNode->pos[axis];
1817             sp_node_moveto(pNode, dest);
1818         }
1819     }
1821     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1824 /// Helper struct.
1825 struct NodeSort
1827    Inkscape::NodePath::Node *_node;
1828     NR::Coord _coord;
1829     /// \todo use vectorof pointers instead of calling copy ctor
1830     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1831         _node(node), _coord(node->pos[axis])
1832     {}
1834 };
1836 static bool operator<(NodeSort const &a, NodeSort const &b)
1838     return (a._coord < b._coord);
1841 /**
1842  * Distribute selected nodes on the specified axis.
1843  */
1844 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1846     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1847         return;
1848     }
1850     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1851         return;
1852     }
1854    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1855     std::vector<NodeSort> sorted;
1856     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1857         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1858         if (pNode) {
1859             NodeSort n(pNode, axis);
1860             sorted.push_back(n);
1861             //dest[axis] = pNode->pos[axis];
1862             //sp_node_moveto(pNode, dest);
1863         }
1864     }
1865     std::sort(sorted.begin(), sorted.end());
1866     unsigned int len = sorted.size();
1867     //overall bboxes span
1868     float dist = (sorted.back()._coord -
1869                   sorted.front()._coord);
1870     //new distance between each bbox
1871     float step = (dist) / (len - 1);
1872     float pos = sorted.front()._coord;
1873     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1874           it < sorted.end();
1875           it ++ )
1876     {
1877         NR::Point dest((*it)._node->pos);
1878         dest[axis] = pos;
1879         sp_node_moveto((*it)._node, dest);
1880         pos += step;
1881     }
1883     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1887 /**
1888  * Call sp_nodepath_line_add_node() for all selected segments.
1889  */
1890 void
1891 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1893     if (!nodepath) {
1894         return;
1895     }
1897     GList *nl = NULL;
1899     int n_added = 0;
1901     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1902        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1903         g_assert(t->selected);
1904         if (t->p.other && t->p.other->selected) {
1905             nl = g_list_prepend(nl, t);
1906         }
1907     }
1909     while (nl) {
1910        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1911        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1912        sp_nodepath_node_select(n, TRUE, FALSE);
1913        n_added ++;
1914        nl = g_list_remove(nl, t);
1915     }
1917     /** \todo fixme: adjust ? */
1918     sp_nodepath_update_handles(nodepath);
1920     if (n_added > 1) {
1921         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1922     } else if (n_added > 0) {
1923         sp_nodepath_update_repr(nodepath, _("Add node"));
1924     }
1926     sp_nodepath_update_statusbar(nodepath);
1929 /**
1930  * Select segment nearest to point
1931  */
1932 void
1933 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1935     if (!nodepath) {
1936         return;
1937     }
1939     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
1940     Geom::PathVector const &pathv = curve->get_pathvector();
1941     Geom::PathVectorPosition pvpos = Geom::nearestPoint(pathv, p);
1943     // calculate index for nodepath's representation.
1944     unsigned int segment_index = floor(pvpos.t) + 1;
1945     for (unsigned int i = 0; i < pvpos.path_nr; ++i) {
1946         segment_index += pathv[i].size() + 1;
1947         if (pathv[i].closed()) {
1948             segment_index += 1;
1949         }
1950     }
1952     curve->unref();
1954     //find segment to segment
1955     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(segment_index);
1957     //fixme: this can return NULL, so check before proceeding.
1958     g_return_if_fail(e != NULL);
1960     gboolean force = FALSE;
1961     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1962         force = TRUE;
1963     }
1964     sp_nodepath_node_select(e, (gboolean) toggle, force);
1965     if (e->p.other)
1966         sp_nodepath_node_select(e->p.other, TRUE, force);
1968     sp_nodepath_update_handles(nodepath);
1970     sp_nodepath_update_statusbar(nodepath);
1973 /**
1974  * Add a node nearest to point
1975  */
1976 void
1977 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1979     if (!nodepath) {
1980         return;
1981     }
1983     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
1984     Geom::PathVector const &pathv = curve->get_pathvector();
1985     Geom::PathVectorPosition pvpos = Geom::nearestPoint(pathv, p);
1987     // calculate index for nodepath's representation.
1988     double int_part;
1989     double t = std::modf(pvpos.t, &int_part);
1990     unsigned int segment_index = (unsigned int)int_part + 1;
1991     for (unsigned int i = 0; i < pvpos.path_nr; ++i) {
1992         segment_index += pathv[i].size() + 1;
1993         if (pathv[i].closed()) {
1994             segment_index += 1;
1995         }
1996     }
1998     curve->unref();
2000     //find segment to split
2001     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(segment_index);
2003     //don't know why but t seems to flip for lines
2004     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2005         t = 1.0 - t;
2006     }
2008     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2009     sp_nodepath_node_select(n, FALSE, TRUE);
2011     /* fixme: adjust ? */
2012     sp_nodepath_update_handles(nodepath);
2014     sp_nodepath_update_repr(nodepath, _("Add node"));
2016     sp_nodepath_update_statusbar(nodepath);
2019 /*
2020  * Adjusts a segment so that t moves by a certain delta for dragging
2021  * converts lines to curves
2022  *
2023  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2024  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2025  */
2026 void
2027 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
2029     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
2031     //fixme: e and e->p can be NULL, so check for those before proceeding
2032     g_return_if_fail(e != NULL);
2033     g_return_if_fail(&e->p != NULL);
2035     /* feel good is an arbitrary parameter that distributes the delta between handles
2036      * if t of the drag point is less than 1/6 distance form the endpoint only
2037      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2038      */
2039     double feel_good;
2040     if (t <= 1.0 / 6.0)
2041         feel_good = 0;
2042     else if (t <= 0.5)
2043         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2044     else if (t <= 5.0 / 6.0)
2045         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2046     else
2047         feel_good = 1;
2049     //if we're dragging a line convert it to a curve
2050     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2051         sp_nodepath_set_line_type(e, NR_CURVETO);
2052     }
2054     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2055     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2056     e->p.other->n.pos += offsetcoord0;
2057     e->p.pos += offsetcoord1;
2059     // adjust handles of adjacent nodes where necessary
2060     sp_node_adjust_handle(e,1);
2061     sp_node_adjust_handle(e->p.other,-1);
2063     sp_nodepath_update_handles(e->subpath->nodepath);
2065     update_object(e->subpath->nodepath);
2067     sp_nodepath_update_statusbar(e->subpath->nodepath);
2071 /**
2072  * Call sp_nodepath_break() for all selected segments.
2073  */
2074 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2076     if (!nodepath) return;
2078     GList *tempin = g_list_copy(nodepath->selected);
2079     GList *temp = NULL;
2080     for (GList *l = tempin; l != NULL; l = l->next) {
2081        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2082        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2083         if (nn == NULL) continue; // no break, no new node
2084         temp = g_list_prepend(temp, nn);
2085     }
2086     g_list_free(tempin);
2088     if (temp) {
2089         sp_nodepath_deselect(nodepath);
2090     }
2091     for (GList *l = temp; l != NULL; l = l->next) {
2092         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2093     }
2095     sp_nodepath_update_handles(nodepath);
2097     sp_nodepath_update_repr(nodepath, _("Break path"));
2100 /**
2101  * Duplicate the selected node(s).
2102  */
2103 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2105     if (!nodepath) {
2106         return;
2107     }
2109     GList *temp = NULL;
2110     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2111        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2112        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2113         if (nn == NULL) continue; // could not duplicate
2114         temp = g_list_prepend(temp, nn);
2115     }
2117     if (temp) {
2118         sp_nodepath_deselect(nodepath);
2119     }
2120     for (GList *l = temp; l != NULL; l = l->next) {
2121         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2122     }
2124     sp_nodepath_update_handles(nodepath);
2126     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2129 /**
2130  *  Internal function to join two nodes by merging them into one.
2131  */
2132 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2134     /* a and b are endpoints */
2136     // if one of the two nodes is mouseovered, fix its position
2137     NR::Point c;
2138     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2139         c = a->pos;
2140     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2141         c = b->pos;
2142     } else {
2143         // otherwise, move joined node to the midpoint
2144         c = (a->pos + b->pos) / 2;
2145     }
2147     if (a->subpath == b->subpath) {
2148        Inkscape::NodePath::SubPath *sp = a->subpath;
2149         sp_nodepath_subpath_close(sp);
2150         sp_node_moveto (sp->first, c);
2152         sp_nodepath_update_handles(sp->nodepath);
2153         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2154         return;
2155     }
2157     /* a and b are separate subpaths */
2158     Inkscape::NodePath::SubPath *sa = a->subpath;
2159     Inkscape::NodePath::SubPath *sb = b->subpath;
2160     NR::Point p;
2161     Inkscape::NodePath::Node *n;
2162     NRPathcode code;
2163     if (a == sa->first) {
2164         // we will now reverse sa, so that a is its last node, not first, and drop that node
2165         p = sa->first->n.pos;
2166         code = (NRPathcode)sa->first->n.other->code;
2167         // create new subpath
2168        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2169        // create a first moveto node on it
2170         n = sa->last;
2171         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2172         n = n->p.other;
2173         if (n == sa->first) n = NULL;
2174         while (n) {
2175             // copy the rest of the nodes from sa to t, going backwards
2176             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2177             n = n->p.other;
2178             if (n == sa->first) n = NULL;
2179         }
2180         // replace sa with t
2181         sp_nodepath_subpath_destroy(sa);
2182         sa = t;
2183     } else if (a == sa->last) {
2184         // a is already last, just drop it
2185         p = sa->last->p.pos;
2186         code = (NRPathcode)sa->last->code;
2187         sp_nodepath_node_destroy(sa->last);
2188     } else {
2189         code = NR_END;
2190         g_assert_not_reached();
2191     }
2193     if (b == sb->first) {
2194         // copy all nodes from b to a, forward 
2195         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2196         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2197             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2198         }
2199     } else if (b == sb->last) {
2200         // copy all nodes from b to a, backward 
2201         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2202         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2203             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2204         }
2205     } else {
2206         g_assert_not_reached();
2207     }
2208     /* and now destroy sb */
2210     sp_nodepath_subpath_destroy(sb);
2212     sp_nodepath_update_handles(sa->nodepath);
2214     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2216     sp_nodepath_update_statusbar(nodepath);
2219 /**
2220  *  Internal function to join two nodes by adding a segment between them.
2221  */
2222 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2224     if (a->subpath == b->subpath) {
2225        Inkscape::NodePath::SubPath *sp = a->subpath;
2227         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2228         sp->closed = TRUE;
2230         sp->first->p.other = sp->last;
2231         sp->last->n.other  = sp->first;
2233         sp_node_handle_mirror_p_to_n(sp->last);
2234         sp_node_handle_mirror_n_to_p(sp->first);
2236         sp->first->code = sp->last->code;
2237         sp->first       = sp->last;
2239         sp_nodepath_update_handles(sp->nodepath);
2241         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2243         return;
2244     }
2246     /* a and b are separate subpaths */
2247     Inkscape::NodePath::SubPath *sa = a->subpath;
2248     Inkscape::NodePath::SubPath *sb = b->subpath;
2250     Inkscape::NodePath::Node *n;
2251     NR::Point p;
2252     NRPathcode code;
2253     if (a == sa->first) {
2254         code = (NRPathcode) sa->first->n.other->code;
2255        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2256         n = sa->last;
2257         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2258         for (n = n->p.other; n != NULL; n = n->p.other) {
2259             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2260         }
2261         sp_nodepath_subpath_destroy(sa);
2262         sa = t;
2263     } else if (a == sa->last) {
2264         code = (NRPathcode)sa->last->code;
2265     } else {
2266         code = NR_END;
2267         g_assert_not_reached();
2268     }
2270     if (b == sb->first) {
2271         n = sb->first;
2272         sp_node_handle_mirror_p_to_n(sa->last);
2273         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2274         sp_node_handle_mirror_n_to_p(sa->last);
2275         for (n = n->n.other; n != NULL; n = n->n.other) {
2276             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2277         }
2278     } else if (b == sb->last) {
2279         n = sb->last;
2280         sp_node_handle_mirror_p_to_n(sa->last);
2281         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2282         sp_node_handle_mirror_n_to_p(sa->last);
2283         for (n = n->p.other; n != NULL; n = n->p.other) {
2284             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2285         }
2286     } else {
2287         g_assert_not_reached();
2288     }
2289     /* and now destroy sb */
2291     sp_nodepath_subpath_destroy(sb);
2293     sp_nodepath_update_handles(sa->nodepath);
2295     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2298 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2300 /**
2301  * Internal function to handle joining two nodes.
2302  */
2303 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2305     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2307     if (g_list_length(nodepath->selected) != 2) {
2308         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2309         return;
2310     }
2312     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2313     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2315     g_assert(a != b);
2316     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2317         // someone tried to join an orphan node (i.e. a single-node subpath).
2318         // this is not worth an error message, just fail silently.
2319         return;
2320     }
2322     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2323         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2324         return;
2325     }
2327     switch(mode) {
2328         case NODE_JOIN_ENDPOINTS:
2329             do_node_selected_join(nodepath, a, b);
2330             break;
2331         case NODE_JOIN_SEGMENT:
2332             do_node_selected_join_segment(nodepath, a, b);
2333             break;
2334     }
2337 /**
2338  *  Join two nodes by merging them into one.
2339  */
2340 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2342     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2345 /**
2346  *  Join two nodes by adding a segment between them.
2347  */
2348 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2350     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2353 /**
2354  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2355  */
2356 void sp_node_delete_preserve(GList *nodes_to_delete)
2358     GSList *nodepaths = NULL;
2360     while (nodes_to_delete) {
2361         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2362         Inkscape::NodePath::SubPath *sp = node->subpath;
2363         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2364         Inkscape::NodePath::Node *sample_cursor = NULL;
2365         Inkscape::NodePath::Node *sample_end = NULL;
2366         Inkscape::NodePath::Node *delete_cursor = node;
2367         bool just_delete = false;
2369         //find the start of this contiguous selection
2370         //move left to the first node that is not selected
2371         //or the start of the non-closed path
2372         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2373             delete_cursor = curr;
2374         }
2376         //just delete at the beginning of an open path
2377         if (!delete_cursor->p.other) {
2378             sample_cursor = delete_cursor;
2379             just_delete = true;
2380         } else {
2381             sample_cursor = delete_cursor->p.other;
2382         }
2384         //calculate points for each segment
2385         int rate = 5;
2386         float period = 1.0 / rate;
2387         std::vector<NR::Point> data;
2388         if (!just_delete) {
2389             data.push_back(sample_cursor->pos);
2390             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2391                 //just delete at the end of an open path
2392                 if (!sp->closed && curr == sp->last) {
2393                     just_delete = true;
2394                     break;
2395                 }
2397                 //sample points on the contiguous selected segment
2398                 NR::Point *bez;
2399                 bez = new NR::Point [4];
2400                 bez[0] = curr->pos;
2401                 bez[1] = curr->n.pos;
2402                 bez[2] = curr->n.other->p.pos;
2403                 bez[3] = curr->n.other->pos;
2404                 for (int i=1; i<rate; i++) {
2405                     gdouble t = i * period;
2406                     NR::Point p = bezier_pt(3, bez, t);
2407                     data.push_back(p);
2408                 }
2409                 data.push_back(curr->n.other->pos);
2411                 sample_end = curr->n.other;
2412                 //break if we've come full circle or hit the end of the selection
2413                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2414                     break;
2415                 }
2416             }
2417         }
2419         if (!just_delete) {
2420             //calculate the best fitting single segment and adjust the endpoints
2421             NR::Point *adata;
2422             adata = new NR::Point [data.size()];
2423             copy(data.begin(), data.end(), adata);
2425             NR::Point *bez;
2426             bez = new NR::Point [4];
2427             //would decreasing error create a better fitting approximation?
2428             gdouble error = 1.0;
2429             gint ret;
2430             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2432             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2433             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2434             //the resulting nodes behave as expected.
2435             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2436                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2437             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2438                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2440             //adjust endpoints
2441             sample_cursor->n.pos = bez[1];
2442             sample_end->p.pos = bez[2];
2443         }
2445         //destroy this contiguous selection
2446         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2447             Inkscape::NodePath::Node *temp = delete_cursor;
2448             if (delete_cursor->n.other == delete_cursor) {
2449                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2450                 delete_cursor = NULL;
2451             } else {
2452                 delete_cursor = delete_cursor->n.other;
2453             }
2454             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2455             sp_nodepath_node_destroy(temp);
2456         }
2458         sp_nodepath_update_handles(nodepath);
2460         if (!g_slist_find(nodepaths, nodepath))
2461             nodepaths = g_slist_prepend (nodepaths, nodepath);
2462     }
2464     for (GSList *i = nodepaths; i; i = i->next) {
2465         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2466         // different nodepaths will give us one undo event per nodepath
2467         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2469         // if the entire nodepath is removed, delete the selected object.
2470         if (nodepath->subpaths == NULL ||
2471             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2472             //at least 2
2473             sp_nodepath_get_node_count(nodepath) < 2) {
2474             SPDocument *document = sp_desktop_document (nodepath->desktop);
2475             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2476             //delete this nodepath's object, not the entire selection! (though at this time, this
2477             //does not matter)
2478             sp_selection_delete();
2479             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2480                               _("Delete nodes"));
2481         } else {
2482             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2483             sp_nodepath_update_statusbar(nodepath);
2484         }
2485     }
2487     g_slist_free (nodepaths);
2490 /**
2491  * Delete one or more selected nodes.
2492  */
2493 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2495     if (!nodepath) return;
2496     if (!nodepath->selected) return;
2498     /** \todo fixme: do it the right way */
2499     while (nodepath->selected) {
2500        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2501         sp_nodepath_node_destroy(node);
2502     }
2505     //clean up the nodepath (such as for trivial subpaths)
2506     sp_nodepath_cleanup(nodepath);
2508     sp_nodepath_update_handles(nodepath);
2510     // if the entire nodepath is removed, delete the selected object.
2511     if (nodepath->subpaths == NULL ||
2512         sp_nodepath_get_node_count(nodepath) < 2) {
2513         SPDocument *document = sp_desktop_document (nodepath->desktop);
2514         sp_selection_delete();
2515         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2516                           _("Delete nodes"));
2517         return;
2518     }
2520     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2522     sp_nodepath_update_statusbar(nodepath);
2525 /**
2526  * Delete one or more segments between two selected nodes.
2527  * This is the code for 'split'.
2528  */
2529 void
2530 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2532    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2533    Inkscape::NodePath::Node *curr, *next;     //Iterators
2535     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2537     if (g_list_length(nodepath->selected) != 2) {
2538         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2539                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2540         return;
2541     }
2543     //Selected nodes, not inclusive
2544    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2545    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2547     if ( ( a==b)                       ||  //same node
2548          (a->subpath  != b->subpath )  ||  //not the same path
2549          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2550          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2551     {
2552         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2553                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2554         return;
2555     }
2557     //###########################################
2558     //# BEGIN EDITS
2559     //###########################################
2560     //##################################
2561     //# CLOSED PATH
2562     //##################################
2563     if (a->subpath->closed) {
2566         gboolean reversed = FALSE;
2568         //Since we can go in a circle, we need to find the shorter distance.
2569         //  a->b or b->a
2570         start = end = NULL;
2571         int distance    = 0;
2572         int minDistance = 0;
2573         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2574             if (curr==b) {
2575                 //printf("a to b:%d\n", distance);
2576                 start = a;//go from a to b
2577                 end   = b;
2578                 minDistance = distance;
2579                 //printf("A to B :\n");
2580                 break;
2581             }
2582             distance++;
2583         }
2585         //try again, the other direction
2586         distance = 0;
2587         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2588             if (curr==a) {
2589                 //printf("b to a:%d\n", distance);
2590                 if (distance < minDistance) {
2591                     start    = b;  //we go from b to a
2592                     end      = a;
2593                     reversed = TRUE;
2594                     //printf("B to A\n");
2595                 }
2596                 break;
2597             }
2598             distance++;
2599         }
2602         //Copy everything from 'end' to 'start' to a new subpath
2603        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2604         for (curr=end ; curr ; curr=curr->n.other) {
2605             NRPathcode code = (NRPathcode) curr->code;
2606             if (curr == end)
2607                 code = NR_MOVETO;
2608             sp_nodepath_node_new(t, NULL,
2609                                  (Inkscape::NodePath::NodeType)curr->type, code,
2610                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2611             if (curr == start)
2612                 break;
2613         }
2614         sp_nodepath_subpath_destroy(a->subpath);
2617     }
2621     //##################################
2622     //# OPEN PATH
2623     //##################################
2624     else {
2626         //We need to get the direction of the list between A and B
2627         //Can we walk from a to b?
2628         start = end = NULL;
2629         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2630             if (curr==b) {
2631                 start = a;  //did it!  we go from a to b
2632                 end   = b;
2633                 //printf("A to B\n");
2634                 break;
2635             }
2636         }
2637         if (!start) {//didn't work?  let's try the other direction
2638             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2639                 if (curr==a) {
2640                     start = b;  //did it!  we go from b to a
2641                     end   = a;
2642                     //printf("B to A\n");
2643                     break;
2644                 }
2645             }
2646         }
2647         if (!start) {
2648             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2649                                                      _("Cannot find path between nodes."));
2650             return;
2651         }
2655         //Copy everything after 'end' to a new subpath
2656        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2657         for (curr=end ; curr ; curr=curr->n.other) {
2658             NRPathcode code = (NRPathcode) curr->code;
2659             if (curr == end)
2660                 code = NR_MOVETO;
2661             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2662                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2663         }
2665         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2666         for (curr = start->n.other ; curr  ; curr=next) {
2667             next = curr->n.other;
2668             sp_nodepath_node_destroy(curr);
2669         }
2671     }
2672     //###########################################
2673     //# END EDITS
2674     //###########################################
2676     //clean up the nodepath (such as for trivial subpaths)
2677     sp_nodepath_cleanup(nodepath);
2679     sp_nodepath_update_handles(nodepath);
2681     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2683     sp_nodepath_update_statusbar(nodepath);
2686 /**
2687  * Call sp_nodepath_set_line() for all selected segments.
2688  */
2689 void
2690 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2692     if (nodepath == NULL) return;
2694     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2695        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2696         g_assert(n->selected);
2697         if (n->p.other && n->p.other->selected) {
2698             sp_nodepath_set_line_type(n, code);
2699         }
2700     }
2702     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2705 /**
2706  * Call sp_nodepath_convert_node_type() for all selected nodes.
2707  */
2708 void
2709 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2711     if (nodepath == NULL) return;
2713     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2715     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2716         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2717     }
2719     sp_nodepath_update_repr(nodepath, _("Change node type"));
2722 /**
2723  * Change select status of node, update its own and neighbour handles.
2724  */
2725 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2727     node->selected = selected;
2729     if (selected) {
2730         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2731         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2732         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2733         sp_knot_update_ctrl(node->knot);
2734     } else {
2735         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2736         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2737         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2738         sp_knot_update_ctrl(node->knot);
2739     }
2741     sp_node_update_handles(node);
2742     if (node->n.other) sp_node_update_handles(node->n.other);
2743     if (node->p.other) sp_node_update_handles(node->p.other);
2746 /**
2747 \brief Select a node
2748 \param node     The node to select
2749 \param incremental   If true, add to selection, otherwise deselect others
2750 \param override   If true, always select this node, otherwise toggle selected status
2751 */
2752 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2754     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2756     if (incremental) {
2757         if (override) {
2758             if (!g_list_find(nodepath->selected, node)) {
2759                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2760             }
2761             sp_node_set_selected(node, TRUE);
2762         } else { // toggle
2763             if (node->selected) {
2764                 g_assert(g_list_find(nodepath->selected, node));
2765                 nodepath->selected = g_list_remove(nodepath->selected, node);
2766             } else {
2767                 g_assert(!g_list_find(nodepath->selected, node));
2768                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2769             }
2770             sp_node_set_selected(node, !node->selected);
2771         }
2772     } else {
2773         sp_nodepath_deselect(nodepath);
2774         nodepath->selected = g_list_prepend(nodepath->selected, node);
2775         sp_node_set_selected(node, TRUE);
2776     }
2778     sp_nodepath_update_statusbar(nodepath);
2782 /**
2783 \brief Deselect all nodes in the nodepath
2784 */
2785 void
2786 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2788     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2790     while (nodepath->selected) {
2791         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2792         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2793     }
2794     sp_nodepath_update_statusbar(nodepath);
2797 /**
2798 \brief Select or invert selection of all nodes in the nodepath
2799 */
2800 void
2801 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2803     if (!nodepath) return;
2805     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2806        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2807         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2808            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2809            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2810         }
2811     }
2814 /**
2815  * If nothing selected, does the same as sp_nodepath_select_all();
2816  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2817  * (i.e., similar to "select all in layer", with the "selected" subpaths
2818  * being treated as "layers" in the path).
2819  */
2820 void
2821 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2823     if (!nodepath) return;
2825     if (g_list_length (nodepath->selected) == 0) {
2826         sp_nodepath_select_all (nodepath, invert);
2827         return;
2828     }
2830     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2831     GSList *subpaths = NULL;
2833     for (GList *l = copy; l != NULL; l = l->next) {
2834         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2835         Inkscape::NodePath::SubPath *subpath = n->subpath;
2836         if (!g_slist_find (subpaths, subpath))
2837             subpaths = g_slist_prepend (subpaths, subpath);
2838     }
2840     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2841         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2842         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2843             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2844             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2845         }
2846     }
2848     g_slist_free (subpaths);
2849     g_list_free (copy);
2852 /**
2853  * \brief Select the node after the last selected; if none is selected,
2854  * select the first within path.
2855  */
2856 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2858     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2860    Inkscape::NodePath::Node *last = NULL;
2861     if (nodepath->selected) {
2862         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2863            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2864             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2865             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2866                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2867                 if (node->selected) {
2868                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2869                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2870                             if (spl->next) { // there's a next subpath
2871                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2872                                 last = subpath_next->first;
2873                             } else if (spl->prev) { // there's a previous subpath
2874                                 last = NULL; // to be set later to the first node of first subpath
2875                             } else {
2876                                 last = node->n.other;
2877                             }
2878                         } else {
2879                             last = node->n.other;
2880                         }
2881                     } else {
2882                         if (node->n.other) {
2883                             last = node->n.other;
2884                         } else {
2885                             if (spl->next) { // there's a next subpath
2886                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2887                                 last = subpath_next->first;
2888                             } else if (spl->prev) { // there's a previous subpath
2889                                 last = NULL; // to be set later to the first node of first subpath
2890                             } else {
2891                                 last = (Inkscape::NodePath::Node *) subpath->first;
2892                             }
2893                         }
2894                     }
2895                 }
2896             }
2897         }
2898         sp_nodepath_deselect(nodepath);
2899     }
2901     if (last) { // there's at least one more node after selected
2902         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2903     } else { // no more nodes, select the first one in first subpath
2904        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2905         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2906     }
2909 /**
2910  * \brief Select the node before the first selected; if none is selected,
2911  * select the last within path
2912  */
2913 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2915     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2917    Inkscape::NodePath::Node *last = NULL;
2918     if (nodepath->selected) {
2919         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2920            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2921             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2922                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2923                 if (node->selected) {
2924                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2925                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2926                             if (spl->prev) { // there's a prev subpath
2927                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2928                                 last = subpath_prev->last;
2929                             } else if (spl->next) { // there's a next subpath
2930                                 last = NULL; // to be set later to the last node of last subpath
2931                             } else {
2932                                 last = node->p.other;
2933                             }
2934                         } else {
2935                             last = node->p.other;
2936                         }
2937                     } else {
2938                         if (node->p.other) {
2939                             last = node->p.other;
2940                         } else {
2941                             if (spl->prev) { // there's a prev subpath
2942                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2943                                 last = subpath_prev->last;
2944                             } else if (spl->next) { // there's a next subpath
2945                                 last = NULL; // to be set later to the last node of last subpath
2946                             } else {
2947                                 last = (Inkscape::NodePath::Node *) subpath->last;
2948                             }
2949                         }
2950                     }
2951                 }
2952             }
2953         }
2954         sp_nodepath_deselect(nodepath);
2955     }
2957     if (last) { // there's at least one more node before selected
2958         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2959     } else { // no more nodes, select the last one in last subpath
2960         GList *spl = g_list_last(nodepath->subpaths);
2961        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2962         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2963     }
2966 /**
2967  * \brief Select all nodes that are within the rectangle.
2968  */
2969 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2971     if (!incremental) {
2972         sp_nodepath_deselect(nodepath);
2973     }
2975     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2976        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2977         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2978            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2980             if (b.contains(node->pos)) {
2981                 sp_nodepath_node_select(node, TRUE, TRUE);
2982             }
2983         }
2984     }
2988 void
2989 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2991     g_assert (n);
2992     g_assert (nodepath);
2993     g_assert (n->subpath->nodepath == nodepath);
2995     if (g_list_length (nodepath->selected) == 0) {
2996         if (grow > 0) {
2997             sp_nodepath_node_select(n, TRUE, TRUE);
2998         }
2999         return;
3000     }
3002     if (g_list_length (nodepath->selected) == 1) {
3003         if (grow < 0) {
3004             sp_nodepath_deselect (nodepath);
3005             return;
3006         }
3007     }
3009         double n_sel_range = 0, p_sel_range = 0;
3010             Inkscape::NodePath::Node *farthest_n_node = n;
3011             Inkscape::NodePath::Node *farthest_p_node = n;
3013         // Calculate ranges
3014         {
3015             double n_range = 0, p_range = 0;
3016             bool n_going = true, p_going = true;
3017             Inkscape::NodePath::Node *n_node = n;
3018             Inkscape::NodePath::Node *p_node = n;
3019             do {
3020                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3021                 if (n_node && n_going)
3022                     n_node = n_node->n.other;
3023                 if (n_node == NULL) {
3024                     n_going = false;
3025                 } else {
3026                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3027                     if (n_node->selected) {
3028                         n_sel_range = n_range;
3029                         farthest_n_node = n_node;
3030                     }
3031                     if (n_node == p_node) {
3032                         n_going = false;
3033                         p_going = false;
3034                     }
3035                 }
3036                 if (p_node && p_going)
3037                     p_node = p_node->p.other;
3038                 if (p_node == NULL) {
3039                     p_going = false;
3040                 } else {
3041                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3042                     if (p_node->selected) {
3043                         p_sel_range = p_range;
3044                         farthest_p_node = p_node;
3045                     }
3046                     if (p_node == n_node) {
3047                         n_going = false;
3048                         p_going = false;
3049                     }
3050                 }
3051             } while (n_going || p_going);
3052         }
3054     if (grow > 0) {
3055         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3056                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3057         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3058                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3059         }
3060     } else {
3061         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3062                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3063         } else if (farthest_p_node && farthest_p_node->selected) {
3064                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3065         }
3066     }
3069 void
3070 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3072     g_assert (n);
3073     g_assert (nodepath);
3074     g_assert (n->subpath->nodepath == nodepath);
3076     if (g_list_length (nodepath->selected) == 0) {
3077         if (grow > 0) {
3078             sp_nodepath_node_select(n, TRUE, TRUE);
3079         }
3080         return;
3081     }
3083     if (g_list_length (nodepath->selected) == 1) {
3084         if (grow < 0) {
3085             sp_nodepath_deselect (nodepath);
3086             return;
3087         }
3088     }
3090     Inkscape::NodePath::Node *farthest_selected = NULL;
3091     double farthest_dist = 0;
3093     Inkscape::NodePath::Node *closest_unselected = NULL;
3094     double closest_dist = NR_HUGE;
3096     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3097        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3098         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3099            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3100            if (node == n)
3101                continue;
3102            if (node->selected) {
3103                if (NR::L2(node->pos - n->pos) > farthest_dist) {
3104                    farthest_dist = NR::L2(node->pos - n->pos);
3105                    farthest_selected = node;
3106                }
3107            } else {
3108                if (NR::L2(node->pos - n->pos) < closest_dist) {
3109                    closest_dist = NR::L2(node->pos - n->pos);
3110                    closest_unselected = node;
3111                }
3112            }
3113         }
3114     }
3116     if (grow > 0) {
3117         if (closest_unselected) {
3118             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3119         }
3120     } else {
3121         if (farthest_selected) {
3122             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3123         }
3124     }
3128 /**
3129 \brief  Saves all nodes' and handles' current positions in their origin members
3130 */
3131 void
3132 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3134     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3135        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3136         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3137            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3138            n->origin = n->pos;
3139            n->p.origin = n->p.pos;
3140            n->n.origin = n->n.pos;
3141         }
3142     }
3145 /**
3146 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3147 */
3148 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3150     if (!nodepath->selected) {
3151         return NULL;
3152     }
3154     GList *r = NULL;
3155     guint i = 0;
3156     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3157        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3158         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3159            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3160             i++;
3161             if (node->selected) {
3162                 r = g_list_append(r, GINT_TO_POINTER(i));
3163             }
3164         }
3165     }
3166     return r;
3169 /**
3170 \brief  Restores selection by selecting nodes whose positions are in the list
3171 */
3172 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3174     sp_nodepath_deselect(nodepath);
3176     guint i = 0;
3177     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3178        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3179         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3180            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3181             i++;
3182             if (g_list_find(r, GINT_TO_POINTER(i))) {
3183                 sp_nodepath_node_select(node, TRUE, TRUE);
3184             }
3185         }
3186     }
3190 /**
3191 \brief Adjusts handle according to node type and line code.
3192 */
3193 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3195     g_assert(node);
3197    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3198    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3200    // nothing to do if we are an end node
3201     if (me->other == NULL) return;
3202     if (other->other == NULL) return;
3204     // nothing to do if we are a cusp node
3205     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3207     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3208     NRPathcode mecode;
3209     if (which_adjust == 1) {
3210         mecode = (NRPathcode)me->other->code;
3211     } else {
3212         mecode = (NRPathcode)node->code;
3213     }
3214     if (mecode == NR_LINETO) return;
3216     if (sp_node_side_is_line(node, other)) {
3217         // other is a line, and we are either smooth or symm
3218        Inkscape::NodePath::Node *othernode = other->other;
3219         double len = NR::L2(me->pos - node->pos);
3220         NR::Point delta = node->pos - othernode->pos;
3221         double linelen = NR::L2(delta);
3222         if (linelen < 1e-18)
3223             return;
3224         me->pos = node->pos + (len / linelen)*delta;
3225         return;
3226     }
3228     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3229         // symmetrize 
3230         me->pos = 2 * node->pos - other->pos;
3231         return;
3232     } else {
3233         // smoothify
3234         double len = NR::L2(me->pos - node->pos);
3235         NR::Point delta = other->pos - node->pos;
3236         double otherlen = NR::L2(delta);
3237         if (otherlen < 1e-18) return;
3238         me->pos = node->pos - (len / otherlen) * delta;
3239     }
3242 /**
3243  \brief Adjusts both handles according to node type and line code
3244  */
3245 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3247     g_assert(node);
3249     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3251     /* we are either smooth or symm */
3253     if (node->p.other == NULL) return;
3254     if (node->n.other == NULL) return;
3256     if (sp_node_side_is_line(node, &node->p)) {
3257         sp_node_adjust_handle(node, 1);
3258         return;
3259     }
3261     if (sp_node_side_is_line(node, &node->n)) {
3262         sp_node_adjust_handle(node, -1);
3263         return;
3264     }
3266     /* both are curves */
3267     NR::Point const delta( node->n.pos - node->p.pos );
3269     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3270         node->p.pos = node->pos - delta / 2;
3271         node->n.pos = node->pos + delta / 2;
3272         return;
3273     }
3275     /* We are smooth */
3276     double plen = NR::L2(node->p.pos - node->pos);
3277     if (plen < 1e-18) return;
3278     double nlen = NR::L2(node->n.pos - node->pos);
3279     if (nlen < 1e-18) return;
3280     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3281     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3284 /**
3285  * Node event callback.
3286  */
3287 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3289     gboolean ret = FALSE;
3290     switch (event->type) {
3291         case GDK_ENTER_NOTIFY:
3292             Inkscape::NodePath::Path::active_node = n;
3293             break;
3294         case GDK_LEAVE_NOTIFY:
3295             Inkscape::NodePath::Path::active_node = NULL;
3296             break;
3297         case GDK_SCROLL:
3298             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3299                 switch (event->scroll.direction) {
3300                     case GDK_SCROLL_UP:
3301                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3302                         break;
3303                     case GDK_SCROLL_DOWN:
3304                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3305                         break;
3306                     default:
3307                         break;
3308                 }
3309                 ret = TRUE;
3310             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3311                 switch (event->scroll.direction) {
3312                     case GDK_SCROLL_UP:
3313                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3314                         break;
3315                     case GDK_SCROLL_DOWN:
3316                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3317                         break;
3318                     default:
3319                         break;
3320                 }
3321                 ret = TRUE;
3322             }
3323             break;
3324         case GDK_KEY_PRESS:
3325             switch (get_group0_keyval (&event->key)) {
3326                 case GDK_space:
3327                     if (event->key.state & GDK_BUTTON1_MASK) {
3328                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3329                         stamp_repr(nodepath);
3330                         ret = TRUE;
3331                     }
3332                     break;
3333                 case GDK_Page_Up:
3334                     if (event->key.state & GDK_CONTROL_MASK) {
3335                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3336                     } else {
3337                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3338                     }
3339                     break;
3340                 case GDK_Page_Down:
3341                     if (event->key.state & GDK_CONTROL_MASK) {
3342                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3343                     } else {
3344                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3345                     }
3346                     break;
3347                 default:
3348                     break;
3349             }
3350             break;
3351         default:
3352             break;
3353     }
3355     return ret;
3358 /**
3359  * Handle keypress on node; directly called.
3360  */
3361 gboolean node_key(GdkEvent *event)
3363     Inkscape::NodePath::Path *np;
3365     // there is no way to verify nodes so set active_node to nil when deleting!!
3366     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3368     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3369         gint ret = FALSE;
3370         switch (get_group0_keyval (&event->key)) {
3371             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3372             case GDK_BackSpace:
3373                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3374                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3375                 sp_nodepath_update_repr(np, _("Delete node"));
3376                 Inkscape::NodePath::Path::active_node = NULL;
3377                 ret = TRUE;
3378                 break;
3379             case GDK_c:
3380                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3381                 ret = TRUE;
3382                 break;
3383             case GDK_s:
3384                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3385                 ret = TRUE;
3386                 break;
3387             case GDK_y:
3388                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3389                 ret = TRUE;
3390                 break;
3391             case GDK_b:
3392                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3393                 ret = TRUE;
3394                 break;
3395         }
3396         return ret;
3397     }
3398     return FALSE;
3401 /**
3402  * Mouseclick on node callback.
3403  */
3404 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3406    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3408     if (state & GDK_CONTROL_MASK) {
3409         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3411         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3412             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3413                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3414             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3415                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3416             } else {
3417                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3418             }
3419             sp_nodepath_update_repr(nodepath, _("Change node type"));
3420             sp_nodepath_update_statusbar(nodepath);
3422         } else { //ctrl+alt+click: delete node
3423             GList *node_to_delete = NULL;
3424             node_to_delete = g_list_append(node_to_delete, n);
3425             sp_node_delete_preserve(node_to_delete);
3426         }
3428     } else {
3429         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3430     }
3433 /**
3434  * Mouse grabbed node callback.
3435  */
3436 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3438    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3440     if (!n->selected) {
3441         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3442     }
3444     n->is_dragging = true;
3445     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3447     sp_nodepath_remember_origins (n->subpath->nodepath);
3450 /**
3451  * Mouse ungrabbed node callback.
3452  */
3453 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3455    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3457    n->dragging_out = NULL;
3458    n->is_dragging = false;
3459    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3461    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3464 /**
3465  * The point on a line, given by its angle, closest to the given point.
3466  * \param p  A point.
3467  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3468  * \param closest  Pointer to the point struct where the result is stored.
3469  * \todo FIXME: use dot product perhaps?
3470  */
3471 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3473     if (a == HUGE_VAL) { // vertical
3474         *closest = NR::Point(0, (*p)[NR::Y]);
3475     } else {
3476         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3477         (*closest)[NR::Y] = a * (*closest)[NR::X];
3478     }
3481 /**
3482  * Distance from the point to a line given by its angle.
3483  * \param p  A point.
3484  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3485  */
3486 static double point_line_distance(NR::Point *p, double a)
3488     NR::Point c;
3489     point_line_closest(p, a, &c);
3490     return sqrt(((*p)[NR::X] - c[NR::X])*((*p)[NR::X] - c[NR::X]) + ((*p)[NR::Y] - c[NR::Y])*((*p)[NR::Y] - c[NR::Y]));
3493 /**
3494  * Callback for node "request" signal.
3495  * \todo fixme: This goes to "moved" event? (lauris)
3496  */
3497 static gboolean
3498 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3500     double yn, xn, yp, xp;
3501     double an, ap, na, pa;
3502     double d_an, d_ap, d_na, d_pa;
3503     gboolean collinear = FALSE;
3504     NR::Point c;
3505     NR::Point pr;
3507     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3509     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3511     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3512     if ( (!n->subpath->nodepath->straight_path) &&
3513          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3514            || n->dragging_out ) )
3515     {
3516        NR::Point mouse = (*p);
3518        if (!n->dragging_out) {
3519            // This is the first drag-out event; find out which handle to drag out
3520            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3521            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3523            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3524                return FALSE;
3526            Inkscape::NodePath::NodeSide *opposite;
3527            if (appr_p > appr_n) { // closer to p
3528                n->dragging_out = &n->p;
3529                opposite = &n->n;
3530                n->code = NR_CURVETO;
3531            } else if (appr_p < appr_n) { // closer to n
3532                n->dragging_out = &n->n;
3533                opposite = &n->p;
3534                n->n.other->code = NR_CURVETO;
3535            } else { // p and n nodes are the same
3536                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3537                    n->dragging_out = &n->p;
3538                    opposite = &n->n;
3539                    n->code = NR_CURVETO;
3540                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3541                    n->dragging_out = &n->n;
3542                    opposite = &n->p;
3543                    n->n.other->code = NR_CURVETO;
3544                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3545                    double appr_other_n = (n->n.other ? NR::L2(n->n.other->n.pos - n->pos) - NR::L2(n->n.other->n.pos - (*p)) : -HUGE_VAL);
3546                    double appr_other_p = (n->n.other ? NR::L2(n->n.other->p.pos - n->pos) - NR::L2(n->n.other->p.pos - (*p)) : -HUGE_VAL);
3547                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3548                        n->dragging_out = &n->n;
3549                        opposite = &n->p;
3550                        n->n.other->code = NR_CURVETO;
3551                    } else { // closer to other's n handle
3552                        n->dragging_out = &n->p;
3553                        opposite = &n->n;
3554                        n->code = NR_CURVETO;
3555                    }
3556                }
3557            }
3559            // if there's another handle, make sure the one we drag out starts parallel to it
3560            if (opposite->pos != n->pos) {
3561                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3562            }
3564            // knots might not be created yet!
3565            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3566            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3567        }
3569        // pass this on to the handle-moved callback
3570        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3571        sp_node_update_handles(n);
3572        return TRUE;
3573    }
3575     if (state & GDK_CONTROL_MASK) { // constrained motion
3577         // calculate relative distances of handles
3578         // n handle:
3579         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3580         xn = n->n.pos[NR::X] - n->pos[NR::X];
3581         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3582         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3583             if (n->n.other) { // if there is the next point
3584                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3585                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3586                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3587             }
3588         }
3589         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3590         if (yn < 0) { xn = -xn; yn = -yn; }
3592         // p handle:
3593         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3594         xp = n->p.pos[NR::X] - n->pos[NR::X];
3595         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3596         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3597             if (n->p.other) {
3598                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3599                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3600                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3601             }
3602         }
3603         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3604         if (yp < 0) { xp = -xp; yp = -yp; }
3606         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3607             // sliding on handles, only if at least one of the handles is non-vertical
3608             // (otherwise it's the same as ctrl+drag anyway)
3610             // calculate angles of the handles
3611             if (xn == 0) {
3612                 if (yn == 0) { // no handle, consider it the continuation of the other one
3613                     an = 0;
3614                     collinear = TRUE;
3615                 }
3616                 else an = 0; // vertical; set the angle to horizontal
3617             } else an = yn/xn;
3619             if (xp == 0) {
3620                 if (yp == 0) { // no handle, consider it the continuation of the other one
3621                     ap = an;
3622                 }
3623                 else ap = 0; // vertical; set the angle to horizontal
3624             } else  ap = yp/xp;
3626             if (collinear) an = ap;
3628             // angles of the perpendiculars; HUGE_VAL means vertical
3629             if (an == 0) na = HUGE_VAL; else na = -1/an;
3630             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3632             // mouse point relative to the node's original pos
3633             pr = (*p) - n->origin;
3635             // distances to the four lines (two handles and two perpendiculars)
3636             d_an = point_line_distance(&pr, an);
3637             d_na = point_line_distance(&pr, na);
3638             d_ap = point_line_distance(&pr, ap);
3639             d_pa = point_line_distance(&pr, pa);
3641             // find out which line is the closest, save its closest point in c
3642             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3643                 point_line_closest(&pr, an, &c);
3644             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3645                 point_line_closest(&pr, ap, &c);
3646             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3647                 point_line_closest(&pr, na, &c);
3648             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3649                 point_line_closest(&pr, pa, &c);
3650             }
3652             // move the node to the closest point
3653             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3654                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3655                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y], 
3656                                             true);
3658         } else {  // constraining to hor/vert
3660             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3661                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3662                                                 (*p)[NR::X] - n->pos[NR::X], 
3663                                                 n->origin[NR::Y] - n->pos[NR::Y],
3664                                                 true, 
3665                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::X]));
3666             } else { // snap to vert
3667                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3668                                                 n->origin[NR::X] - n->pos[NR::X],
3669                                                 (*p)[NR::Y] - n->pos[NR::Y],
3670                                                 true,
3671                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::Y]));
3672             }
3673         }
3674     } else { // move freely
3675         if (n->is_dragging) {
3676             if (state & GDK_MOD1_MASK) { // sculpt
3677                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3678             } else {
3679                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3680                                             (*p)[NR::X] - n->pos[NR::X],
3681                                             (*p)[NR::Y] - n->pos[NR::Y],
3682                                             (state & GDK_SHIFT_MASK) == 0);
3683             }
3684         }
3685     }
3687     n->subpath->nodepath->desktop->scroll_to_point(p);
3689     return TRUE;
3692 /**
3693  * Node handle clicked callback.
3694  */
3695 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3697    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3699     if (state & GDK_CONTROL_MASK) { // "delete" handle
3700         if (n->p.knot == knot) {
3701             n->p.pos = n->pos;
3702         } else if (n->n.knot == knot) {
3703             n->n.pos = n->pos;
3704         }
3705         sp_node_update_handles(n);
3706         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3707         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3708         sp_nodepath_update_statusbar(nodepath);
3710     } else { // just select or add to selection, depending in Shift
3711         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3712     }
3715 /**
3716  * Node handle grabbed callback.
3717  */
3718 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3720    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3722     if (!n->selected) {
3723         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3724     }
3726     // remember the origin point of the handle
3727     if (n->p.knot == knot) {
3728         n->p.origin_radial = n->p.pos - n->pos;
3729     } else if (n->n.knot == knot) {
3730         n->n.origin_radial = n->n.pos - n->pos;
3731     } else {
3732         g_assert_not_reached();
3733     }
3735     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3738 /**
3739  * Node handle ungrabbed callback.
3740  */
3741 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3743    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3745     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3746     if (n->p.knot == knot) {
3747         n->p.origin_radial.a = 0;
3748         sp_knot_set_position(knot, n->p.pos, state);
3749     } else if (n->n.knot == knot) {
3750         n->n.origin_radial.a = 0;
3751         sp_knot_set_position(knot, n->n.pos, state);
3752     } else {
3753         g_assert_not_reached();
3754     }
3756     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3759 /**
3760  * Node handle "request" signal callback.
3761  */
3762 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3764     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3766     Inkscape::NodePath::NodeSide *me, *opposite;
3767     gint which;
3768     if (n->p.knot == knot) {
3769         me = &n->p;
3770         opposite = &n->n;
3771         which = -1;
3772     } else if (n->n.knot == knot) {
3773         me = &n->n;
3774         opposite = &n->p;
3775         which = 1;
3776     } else {
3777         me = opposite = NULL;
3778         which = 0;
3779         g_assert_not_reached();
3780     }
3782     SPDesktop *desktop = n->subpath->nodepath->desktop;
3783     SnapManager &m = desktop->namedview->snap_manager;
3784     m.setup(desktop, n->subpath->nodepath->item);
3785     Inkscape::SnappedPoint s;
3786     
3787     if ((state & GDK_SHIFT_MASK) != 0) {
3788         // We will not try to snap when the shift-key is pressed
3789         // so remove the old snap indicator and don't wait for it to time-out  
3790         desktop->snapindicator->remove_snappoint();     
3791     }
3793     Inkscape::NodePath::Node *othernode = opposite->other;
3794     if (othernode) {
3795         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3796             /* We are smooth node adjacent with line */
3797             NR::Point const delta = *p - n->pos;
3798             NR::Coord const len = NR::L2(delta);
3799             Inkscape::NodePath::Node *othernode = opposite->other;
3800             NR::Point const ndelta = n->pos - othernode->pos;
3801             NR::Coord const linelen = NR::L2(ndelta);
3802             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3803                 NR::Coord const scal = dot(delta, ndelta) / linelen;
3804                 (*p) = n->pos + (scal / linelen) * ndelta;
3805             }
3806             if ((state & GDK_SHIFT_MASK) == 0) {
3807                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta));
3808             }
3809         } else {
3810                 if ((state & GDK_SHIFT_MASK) == 0) {
3811                         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3812                 }
3813         }
3814     } else {
3815         if ((state & GDK_SHIFT_MASK) == 0) {
3816                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3817         }
3818     }
3819     
3820     s.getPoint(*p);
3821     
3822     sp_node_adjust_handle(n, -which);
3824     return FALSE;
3827 /**
3828  * Node handle moved callback.
3829  */
3830 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3832    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3834    Inkscape::NodePath::NodeSide *me;
3835    Inkscape::NodePath::NodeSide *other;
3836     if (n->p.knot == knot) {
3837         me = &n->p;
3838         other = &n->n;
3839     } else if (n->n.knot == knot) {
3840         me = &n->n;
3841         other = &n->p;
3842     } else {
3843         me = NULL;
3844         other = NULL;
3845         g_assert_not_reached();
3846     }
3848     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3849     Radial rme(me->pos - n->pos);
3850     Radial rother(other->pos - n->pos);
3851     Radial rnew(*p - n->pos);
3853     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3854         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3855         /* 0 interpreted as "no snapping". */
3857         // 1. Snap to the closest PI/snaps angle, starting from zero.
3858         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3860         // 2. Snap to the original angle, its opposite and perpendiculars
3861         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3862             /* The closest PI/2 angle, starting from original angle */
3863             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3865             // Snap to the closest.
3866             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3867                        ? a_snapped
3868                        : a_ortho );
3869         }
3871         // 3. Snap to the angle of the opposite line, if any
3872         Inkscape::NodePath::Node *othernode = other->other;
3873         if (othernode) {
3874             NR::Point other_to_snap(0,0);
3875             if (sp_node_side_is_line(n, other)) {
3876                 other_to_snap = othernode->pos - n->pos;
3877             } else {
3878                 other_to_snap = other->pos - n->pos;
3879             }
3880             if (NR::L2(other_to_snap) > 1e-3) {
3881                 Radial rother_to_snap(other_to_snap);
3882                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
3883                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
3885                 // Snap to the closest.
3886                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
3887                        ? a_snapped
3888                        : a_oppo );
3889             }
3890         }
3892         rnew.a = a_snapped;
3893     }
3895     if (state & GDK_MOD1_MASK) {
3896         // lock handle length
3897         rnew.r = me->origin_radial.r;
3898     }
3900     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3901         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3902         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3903         rother.a += rnew.a - rme.a;
3904         other->pos = NR::Point(rother) + n->pos;
3905         if (other->knot) {
3906             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3907             sp_knot_moveto(other->knot, other->pos);
3908         }
3909     }
3911     me->pos = NR::Point(rnew) + n->pos;
3912     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3914     // move knot, but without emitting the signal:
3915     // we cannot emit a "moved" signal because we're now processing it
3916     sp_knot_moveto(me->knot, me->pos);
3918     update_object(n->subpath->nodepath);
3920     /* status text */
3921     SPDesktop *desktop = n->subpath->nodepath->desktop;
3922     if (!desktop) return;
3923     SPEventContext *ec = desktop->event_context;
3924     if (!ec) return;
3925     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3926     if (!mc) return;
3928     double degrees = 180 / M_PI * rnew.a;
3929     if (degrees > 180) degrees -= 360;
3930     if (degrees < -180) degrees += 360;
3931     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3932         degrees = angle_to_compass (degrees);
3934     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3936     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
3937          _("<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);
3939     g_string_free(length, TRUE);
3942 /**
3943  * Node handle event callback.
3944  */
3945 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3947     gboolean ret = FALSE;
3948     switch (event->type) {
3949         case GDK_KEY_PRESS:
3950             switch (get_group0_keyval (&event->key)) {
3951                 case GDK_space:
3952                     if (event->key.state & GDK_BUTTON1_MASK) {
3953                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3954                         stamp_repr(nodepath);
3955                         ret = TRUE;
3956                     }
3957                     break;
3958                 default:
3959                     break;
3960             }
3961             break;
3962         case GDK_ENTER_NOTIFY:
3963             // we use an experimentally determined threshold that seems to work fine
3964             if (NR::L2(n->pos - knot->pos) < 0.75)
3965                 Inkscape::NodePath::Path::active_node = n;
3966             break;
3967         case GDK_LEAVE_NOTIFY:
3968             // we use an experimentally determined threshold that seems to work fine
3969             if (NR::L2(n->pos - knot->pos) < 0.75)
3970                 Inkscape::NodePath::Path::active_node = NULL;
3971             break;
3972         default:
3973             break;
3974     }
3976     return ret;
3979 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3980                                  Radial &rme, Radial &rother, gboolean const both)
3982     rme.a += angle;
3983     if ( both
3984          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3985          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3986     {
3987         rother.a += angle;
3988     }
3991 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3992                                         Radial &rme, Radial &rother, gboolean const both)
3994     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3996     gdouble r;
3997     if ( both
3998          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3999          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4000     {
4001         r = MAX(rme.r, rother.r);
4002     } else {
4003         r = rme.r;
4004     }
4006     gdouble const weird_angle = atan2(norm_angle, r);
4007 /* Bulia says norm_angle is just the visible distance that the
4008  * object's end must travel on the screen.  Left as 'angle' for want of
4009  * a better name.*/
4011     rme.a += weird_angle;
4012     if ( both
4013          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4014          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4015     {
4016         rother.a += weird_angle;
4017     }
4020 /**
4021  * Rotate one node.
4022  */
4023 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4025     Inkscape::NodePath::NodeSide *me, *other;
4026     bool both = false;
4028     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4029     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4031     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4032         me = &(n->p);
4033         other = &(n->n);
4034     } else if (!n->p.other) {
4035         me = &(n->n);
4036         other = &(n->p);
4037     } else {
4038         if (which > 0) { // right handle
4039             if (xn > xp) {
4040                 me = &(n->n);
4041                 other = &(n->p);
4042             } else {
4043                 me = &(n->p);
4044                 other = &(n->n);
4045             }
4046         } else if (which < 0){ // left handle
4047             if (xn <= xp) {
4048                 me = &(n->n);
4049                 other = &(n->p);
4050             } else {
4051                 me = &(n->p);
4052                 other = &(n->n);
4053             }
4054         } else { // both handles
4055             me = &(n->n);
4056             other = &(n->p);
4057             both = true;
4058         }
4059     }
4061     Radial rme(me->pos - n->pos);
4062     Radial rother(other->pos - n->pos);
4064     if (screen) {
4065         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4066     } else {
4067         node_rotate_one_internal (*n, angle, rme, rother, both);
4068     }
4070     me->pos = n->pos + NR::Point(rme);
4072     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4073         other->pos =  n->pos + NR::Point(rother);
4074     }
4076     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4077     // so here we just move all the knots without emitting move signals, for speed
4078     sp_node_update_handles(n, false);
4081 /**
4082  * Rotate selected nodes.
4083  */
4084 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4086     if (!nodepath || !nodepath->selected) return;
4088     if (g_list_length(nodepath->selected) == 1) {
4089        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4090         node_rotate_one (n, angle, which, screen);
4091     } else {
4092        // rotate as an object:
4094         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4095         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4096         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4097             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4098             box.expandTo (n->pos); // contain all selected nodes
4099         }
4101         gdouble rot;
4102         if (screen) {
4103             gdouble const zoom = nodepath->desktop->current_zoom();
4104             gdouble const zmove = angle / zoom;
4105             gdouble const r = NR::L2(box.max() - box.midpoint());
4106             rot = atan2(zmove, r);
4107         } else {
4108             rot = angle;
4109         }
4111         NR::Point rot_center;
4112         if (Inkscape::NodePath::Path::active_node == NULL)
4113             rot_center = box.midpoint();
4114         else
4115             rot_center = Inkscape::NodePath::Path::active_node->pos;
4117         NR::Matrix t =
4118             NR::Matrix (NR::translate(-rot_center)) *
4119             NR::Matrix (NR::rotate(rot)) *
4120             NR::Matrix (NR::translate(rot_center));
4122         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4123             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4124             n->pos *= t;
4125             n->n.pos *= t;
4126             n->p.pos *= t;
4127             sp_node_update_handles(n, false);
4128         }
4129     }
4131     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4134 /**
4135  * Scale one node.
4136  */
4137 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4139     bool both = false;
4140     Inkscape::NodePath::NodeSide *me, *other;
4142     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4143     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4145     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4146         me = &(n->p);
4147         other = &(n->n);
4148         n->code = NR_CURVETO;
4149     } else if (!n->p.other) {
4150         me = &(n->n);
4151         other = &(n->p);
4152         if (n->n.other)
4153             n->n.other->code = NR_CURVETO;
4154     } else {
4155         if (which > 0) { // right handle
4156             if (xn > xp) {
4157                 me = &(n->n);
4158                 other = &(n->p);
4159                 if (n->n.other)
4160                     n->n.other->code = NR_CURVETO;
4161             } else {
4162                 me = &(n->p);
4163                 other = &(n->n);
4164                 n->code = NR_CURVETO;
4165             }
4166         } else if (which < 0){ // left handle
4167             if (xn <= xp) {
4168                 me = &(n->n);
4169                 other = &(n->p);
4170                 if (n->n.other)
4171                     n->n.other->code = NR_CURVETO;
4172             } else {
4173                 me = &(n->p);
4174                 other = &(n->n);
4175                 n->code = NR_CURVETO;
4176             }
4177         } else { // both handles
4178             me = &(n->n);
4179             other = &(n->p);
4180             both = true;
4181             n->code = NR_CURVETO;
4182             if (n->n.other)
4183                 n->n.other->code = NR_CURVETO;
4184         }
4185     }
4187     Radial rme(me->pos - n->pos);
4188     Radial rother(other->pos - n->pos);
4190     rme.r += grow;
4191     if (rme.r < 0) rme.r = 0;
4192     if (rme.a == HUGE_VAL) {
4193         if (me->other) { // if direction is unknown, initialize it towards the next node
4194             Radial rme_next(me->other->pos - n->pos);
4195             rme.a = rme_next.a;
4196         } else { // if there's no next, initialize to 0
4197             rme.a = 0;
4198         }
4199     }
4200     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4201         rother.r += grow;
4202         if (rother.r < 0) rother.r = 0;
4203         if (rother.a == HUGE_VAL) {
4204             rother.a = rme.a + M_PI;
4205         }
4206     }
4208     me->pos = n->pos + NR::Point(rme);
4210     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4211         other->pos = n->pos + NR::Point(rother);
4212     }
4214     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4215     // so here we just move all the knots without emitting move signals, for speed
4216     sp_node_update_handles(n, false);
4219 /**
4220  * Scale selected nodes.
4221  */
4222 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4224     if (!nodepath || !nodepath->selected) return;
4226     if (g_list_length(nodepath->selected) == 1) {
4227         // scale handles of the single selected node
4228         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4229         node_scale_one (n, grow, which);
4230     } else {
4231         // scale nodes as an "object":
4233         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4234         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4235         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4236             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4237             box.expandTo (n->pos); // contain all selected nodes
4238         }
4240         double scale = (box.maxExtent() + grow)/box.maxExtent();
4242         NR::Point scale_center;
4243         if (Inkscape::NodePath::Path::active_node == NULL)
4244             scale_center = box.midpoint();
4245         else
4246             scale_center = Inkscape::NodePath::Path::active_node->pos;
4248         NR::Matrix t =
4249             NR::Matrix (NR::translate(-scale_center)) *
4250             NR::Matrix (NR::scale(scale, scale)) *
4251             NR::Matrix (NR::translate(scale_center));
4253         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4254             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4255             n->pos *= t;
4256             n->n.pos *= t;
4257             n->p.pos *= t;
4258             sp_node_update_handles(n, false);
4259         }
4260     }
4262     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4265 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4267     if (!nodepath) return;
4268     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4271 /**
4272  * Flip selected nodes horizontally/vertically.
4273  */
4274 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, boost::optional<NR::Point> center)
4276     if (!nodepath || !nodepath->selected) return;
4278     if (g_list_length(nodepath->selected) == 1 && !center) {
4279         // flip handles of the single selected node
4280         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4281         double temp = n->p.pos[axis];
4282         n->p.pos[axis] = n->n.pos[axis];
4283         n->n.pos[axis] = temp;
4284         sp_node_update_handles(n, false);
4285     } else {
4286         // scale nodes as an "object":
4288         Geom::Rect box = sp_node_selected_bbox (nodepath);
4289         if (!center) {
4290             center = from_2geom(box.midpoint());
4291         }
4292         NR::Matrix t =
4293             NR::Matrix (NR::translate(- *center)) *
4294             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
4295             NR::Matrix (NR::translate(*center));
4297         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4298             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4299             n->pos *= t;
4300             n->n.pos *= t;
4301             n->p.pos *= t;
4302             sp_node_update_handles(n, false);
4303         }
4304     }
4306     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4309 Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4311     g_assert (nodepath->selected);
4313     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4314     Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4315     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4316         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4317         box.expandTo (n->pos); // contain all selected nodes
4318     }
4319     return box;
4322 //-----------------------------------------------
4323 /**
4324  * Return new subpath under given nodepath.
4325  */
4326 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4328     g_assert(nodepath);
4329     g_assert(nodepath->desktop);
4331    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4333     s->nodepath = nodepath;
4334     s->closed = FALSE;
4335     s->nodes = NULL;
4336     s->first = NULL;
4337     s->last = NULL;
4339     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4340     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4341     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4343     return s;
4346 /**
4347  * Destroy nodes in subpath, then subpath itself.
4348  */
4349 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4351     g_assert(subpath);
4352     g_assert(subpath->nodepath);
4353     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4355     while (subpath->nodes) {
4356         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4357     }
4359     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4361     g_free(subpath);
4364 /**
4365  * Link head to tail in subpath.
4366  */
4367 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4369     g_assert(!sp->closed);
4370     g_assert(sp->last != sp->first);
4371     g_assert(sp->first->code == NR_MOVETO);
4373     sp->closed = TRUE;
4375     //Link the head to the tail
4376     sp->first->p.other = sp->last;
4377     sp->last->n.other  = sp->first;
4378     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4379     sp->first          = sp->last;
4381     //Remove the extra end node
4382     sp_nodepath_node_destroy(sp->last->n.other);
4385 /**
4386  * Open closed (loopy) subpath at node.
4387  */
4388 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4390     g_assert(sp->closed);
4391     g_assert(n->subpath == sp);
4392     g_assert(sp->first == sp->last);
4394     /* We create new startpoint, current node will become last one */
4396    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4397                                                 &n->pos, &n->pos, &n->n.pos);
4400     sp->closed        = FALSE;
4402     //Unlink to make a head and tail
4403     sp->first         = new_path;
4404     sp->last          = n;
4405     n->n.other        = NULL;
4406     new_path->p.other = NULL;
4409 /**
4410  * Return new node in subpath with given properties.
4411  * \param pos Position of node.
4412  * \param ppos Handle position in previous direction
4413  * \param npos Handle position in previous direction
4414  */
4415 Inkscape::NodePath::Node *
4416 sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp, Inkscape::NodePath::Node *next, Inkscape::NodePath::NodeType type, NRPathcode code, NR::Point *ppos, NR::Point *pos, NR::Point *npos)
4418     g_assert(sp);
4419     g_assert(sp->nodepath);
4420     g_assert(sp->nodepath->desktop);
4422     if (nodechunk == NULL)
4423         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4425     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4427     n->subpath  = sp;
4429     if (type != Inkscape::NodePath::NODE_NONE) {
4430         // use the type from sodipodi:nodetypes
4431         n->type = type;
4432     } else {
4433         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4434             // points are (almost) collinear
4435             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4436                 // endnode, or a node with a retracted handle
4437                 n->type = Inkscape::NodePath::NODE_CUSP;
4438             } else {
4439                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4440             }
4441         } else {
4442             n->type = Inkscape::NodePath::NODE_CUSP;
4443         }
4444     }
4446     n->code     = code;
4447     n->selected = FALSE;
4448     n->pos      = *pos;
4449     n->p.pos    = *ppos;
4450     n->n.pos    = *npos;
4452     n->dragging_out = NULL;
4454     Inkscape::NodePath::Node *prev;
4455     if (next) {
4456         //g_assert(g_list_find(sp->nodes, next));
4457         prev = next->p.other;
4458     } else {
4459         prev = sp->last;
4460     }
4462     if (prev)
4463         prev->n.other = n;
4464     else
4465         sp->first = n;
4467     if (next)
4468         next->p.other = n;
4469     else
4470         sp->last = n;
4472     n->p.other = prev;
4473     n->n.other = next;
4475     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"));
4476     sp_knot_set_position(n->knot, *pos, 0);
4478     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4479     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4480     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4481     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4482     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4483     sp_knot_update_ctrl(n->knot);
4485     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4486     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4487     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4488     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4489     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4490     sp_knot_show(n->knot);
4492     // We only create handle knots and lines on demand
4493     n->p.knot = NULL;
4494     n->p.line = NULL;
4495     n->n.knot = NULL;
4496     n->n.line = NULL;
4498     sp->nodes = g_list_prepend(sp->nodes, n);
4500     return n;
4503 /**
4504  * Destroy node and its knots, link neighbors in subpath.
4505  */
4506 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4508     g_assert(node);
4509     g_assert(node->subpath);
4510     g_assert(SP_IS_KNOT(node->knot));
4512    Inkscape::NodePath::SubPath *sp = node->subpath;
4514     if (node->selected) { // first, deselect
4515         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4516         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4517     }
4519     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4521     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4522     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4523     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4524     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4525     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4526     g_object_unref(G_OBJECT(node->knot));
4528     if (node->p.knot) {
4529         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4530         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4531         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4532         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4533         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4534         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4535         g_object_unref(G_OBJECT(node->p.knot));
4536         node->p.knot = NULL;
4537     }
4539     if (node->n.knot) {
4540         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4541         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4542         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4543         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4544         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4545         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4546         g_object_unref(G_OBJECT(node->n.knot));
4547         node->n.knot = NULL;
4548     }
4550     if (node->p.line)
4551         gtk_object_destroy(GTK_OBJECT(node->p.line));
4552     if (node->n.line)
4553         gtk_object_destroy(GTK_OBJECT(node->n.line));
4555     if (sp->nodes) { // there are others nodes on the subpath
4556         if (sp->closed) {
4557             if (sp->first == node) {
4558                 g_assert(sp->last == node);
4559                 sp->first = node->n.other;
4560                 sp->last = sp->first;
4561             }
4562             node->p.other->n.other = node->n.other;
4563             node->n.other->p.other = node->p.other;
4564         } else {
4565             if (sp->first == node) {
4566                 sp->first = node->n.other;
4567                 sp->first->code = NR_MOVETO;
4568             }
4569             if (sp->last == node) sp->last = node->p.other;
4570             if (node->p.other) node->p.other->n.other = node->n.other;
4571             if (node->n.other) node->n.other->p.other = node->p.other;
4572         }
4573     } else { // this was the last node on subpath
4574         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4575     }
4577     g_mem_chunk_free(nodechunk, node);
4580 /**
4581  * Returns one of the node's two sides.
4582  * \param which Indicates which side.
4583  * \return Pointer to previous node side if which==-1, next if which==1.
4584  */
4585 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4587     g_assert(node);
4589     switch (which) {
4590         case -1:
4591             return &node->p;
4592         case 1:
4593             return &node->n;
4594         default:
4595             break;
4596     }
4598     g_assert_not_reached();
4600     return NULL;
4603 /**
4604  * Return the other side of the node, given one of its sides.
4605  */
4606 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4608     g_assert(node);
4610     if (me == &node->p) return &node->n;
4611     if (me == &node->n) return &node->p;
4613     g_assert_not_reached();
4615     return NULL;
4618 /**
4619  * Return NRPathcode on the given side of the node.
4620  */
4621 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4623     g_assert(node);
4625     if (me == &node->p) {
4626         if (node->p.other) return (NRPathcode)node->code;
4627         return NR_MOVETO;
4628     }
4630     if (me == &node->n) {
4631         if (node->n.other) return (NRPathcode)node->n.other->code;
4632         return NR_MOVETO;
4633     }
4635     g_assert_not_reached();
4637     return NR_END;
4640 /**
4641  * Return node with the given index
4642  */
4643 Inkscape::NodePath::Node *
4644 sp_nodepath_get_node_by_index(int index)
4646     Inkscape::NodePath::Node *e = NULL;
4648     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4649     if (!nodepath) {
4650         return e;
4651     }
4653     //find segment
4654     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4656         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4657         int n = g_list_length(sp->nodes);
4658         if (sp->closed) {
4659             n++;
4660         }
4662         //if the piece belongs to this subpath grab it
4663         //otherwise move onto the next subpath
4664         if (index < n) {
4665             e = sp->first;
4666             for (int i = 0; i < index; ++i) {
4667                 e = e->n.other;
4668             }
4669             break;
4670         } else {
4671             if (sp->closed) {
4672                 index -= (n+1);
4673             } else {
4674                 index -= n;
4675             }
4676         }
4677     }
4679     return e;
4682 /**
4683  * Returns plain text meaning of node type.
4684  */
4685 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4687     unsigned retracted = 0;
4688     bool endnode = false;
4690     for (int which = -1; which <= 1; which += 2) {
4691         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4692         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4693             retracted ++;
4694         if (!side->other)
4695             endnode = true;
4696     }
4698     if (retracted == 0) {
4699         if (endnode) {
4700                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4701                 return _("end node");
4702         } else {
4703             switch (node->type) {
4704                 case Inkscape::NodePath::NODE_CUSP:
4705                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4706                     return _("cusp");
4707                 case Inkscape::NodePath::NODE_SMOOTH:
4708                     // TRANSLATORS: "smooth" is an adjective here
4709                     return _("smooth");
4710                 case Inkscape::NodePath::NODE_SYMM:
4711                     return _("symmetric");
4712             }
4713         }
4714     } else if (retracted == 1) {
4715         if (endnode) {
4716             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4717             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4718         } else {
4719             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4720         }
4721     } else {
4722         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4723     }
4725     return NULL;
4728 /**
4729  * Handles content of statusbar as long as node tool is active.
4730  */
4731 void
4732 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4734     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");
4735     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4737     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4738     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4739     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4740     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4742     SPDesktop *desktop = NULL;
4743     if (nodepath) {
4744         desktop = nodepath->desktop;
4745     } else {
4746         desktop = SP_ACTIVE_DESKTOP;
4747     }
4749     SPEventContext *ec = desktop->event_context;
4750     if (!ec) return;
4751     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4752     if (!mc) return;
4754     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4756     if (selected_nodes == 0) {
4757         Inkscape::Selection *sel = desktop->selection;
4758         if (!sel || sel->isEmpty()) {
4759             mc->setF(Inkscape::NORMAL_MESSAGE,
4760                      _("Select a single object to edit its nodes or handles."));
4761         } else {
4762             if (nodepath) {
4763             mc->setF(Inkscape::NORMAL_MESSAGE,
4764                      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.",
4765                               "<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.",
4766                               total_nodes),
4767                      total_nodes);
4768             } else {
4769                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4770                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4771                 } else {
4772                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4773                 }
4774             }
4775         }
4776     } else if (nodepath && selected_nodes == 1) {
4777         mc->setF(Inkscape::NORMAL_MESSAGE,
4778                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4779                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4780                           total_nodes),
4781                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4782     } else {
4783         if (selected_subpaths > 1) {
4784             mc->setF(Inkscape::NORMAL_MESSAGE,
4785                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4786                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4787                               total_nodes),
4788                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4789         } else {
4790             mc->setF(Inkscape::NORMAL_MESSAGE,
4791                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4792                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4793                               total_nodes),
4794                      selected_nodes, total_nodes, when_selected);
4795         }
4796     }
4799 /*
4800  * returns a *copy* of the curve of that object.
4801  */
4802 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4803     if (!object)
4804         return NULL;
4806     SPCurve *curve = NULL;
4807     if (SP_IS_PATH(object)) {
4808         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4809         curve = curve_new->copy();
4810     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4811         const gchar *svgd = object->repr->attribute(key);
4812         if (svgd) {
4813             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4814             SPCurve *curve_new = new SPCurve(pv);
4815             if (curve_new) {
4816                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4817             }
4818         }
4819     }
4821     return curve;
4824 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4825     if (!np || !np->object || !curve)
4826         return;
4828     if (SP_IS_PATH(np->object)) {
4829         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4830             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4831         } else {
4832             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4833         }
4834     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4835         Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( LIVEPATHEFFECT(np->object)->lpe->getParameter(np->repr_key) );
4836         if (pathparam) {
4837             pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
4838             np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4839         }
4840     }
4843 /**
4844 SPCanvasItem *
4845 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
4846     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
4848 **/
4850 /**
4851 SPCanvasItem *
4852 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) {
4853     SPCurve *flash_curve = curve->copy();
4854     Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity();
4855     flash_curve->transform(i2d);
4856     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4857     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4858     // unless we also flash the nodes...
4859     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4860     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4861     sp_canvas_item_show(canvasitem);
4862     flash_curve->unref();
4863     return canvasitem;
4866 SPCanvasItem *
4867 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) {
4868     return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path),
4869                                            prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff));
4871 **/
4873 SPCanvasItem *
4874 sp_nodepath_helperpath_from_path(SPDesktop *desktop, SPPath *path) {
4875     SPCurve *flash_curve = sp_path_get_curve_for_edit(path)->copy();
4876     Geom::Matrix i2d = sp_item_i2d_affine(SP_ITEM(path));
4877     flash_curve->transform(i2d);
4878     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4879     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4880     // unless we also flash the nodes...
4881     guint32 color = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
4882     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4883     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4884     sp_canvas_item_show(canvasitem);
4885     flash_curve->unref();
4886     return canvasitem;
4889 // TODO: Merge this with sp_nodepath_make_helper_item()!
4890 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4891     np->show_helperpath = show;
4893     if (show) {
4894         SPCurve *helper_curve = np->curve->copy();
4895         helper_curve->transform(to_2geom(np->i2d));
4896         if (!np->helper_path) {
4897             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
4899             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4900             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);
4901             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4902             sp_canvas_item_move_to_z(np->helper_path, 0);
4903             sp_canvas_item_show(np->helper_path);
4904         } else {
4905             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4906         }
4907         helper_curve->unref();
4908     } else {
4909         if (np->helper_path) {
4910             GtkObject *temp = np->helper_path;
4911             np->helper_path = NULL;
4912             gtk_object_destroy(temp);
4913         }
4914     }
4917 /* sp_nodepath_make_straight_path:
4918  *   Prevents user from curving the path by dragging a segment or activating handles etc.
4919  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
4920  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
4921  */
4922 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4923     np->straight_path = true;
4924     np->show_handles = false;
4925     g_message("add code to make the path straight.");
4926     // do sp_nodepath_convert_node_type on all nodes?
4927     // coding tip: search for this text : "Make selected segments lines"
4930 /*
4931   Local Variables:
4932   mode:c++
4933   c-file-style:"stroustrup"
4934   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4935   indent-tabs-mode:nil
4936   fill-column:99
4937   End:
4938 */
4939 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :