Code

Hopefully fix for the recently experienced crashes when building with -O2. I don...
[inkscape.git] / src / nodepath.cpp
1 #define __SP_NODEPATH_C__
3 /** \file
4  * Path handler in node edit mode
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *
10  * Portions of this code are in public domain; node sculpting functions written by bulia byak are under GNU GPL
11  */
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
17 #include <gdk/gdkkeysyms.h>
18 #include "display/canvas-bpath.h"
19 #include "display/curve.h"
20 #include "display/sp-ctrlline.h"
21 #include "display/sodipodi-ctrl.h"
22 #include "display/sp-canvas-util.h"
23 #include <glibmm/i18n.h>
24 #include <2geom/pathvector.h>
25 #include <2geom/sbasis-to-bezier.h>
26 #include <2geom/bezier-curve.h>
27 #include <2geom/hvlinesegment.h>
28 #include "helper/units.h"
29 #include "helper/geom.h"
30 #include "knot.h"
31 #include "inkscape.h"
32 #include "document.h"
33 #include "sp-namedview.h"
34 #include "desktop.h"
35 #include "desktop-handles.h"
36 #include "snap.h"
37 #include "message-stack.h"
38 #include "message-context.h"
39 #include "node-context.h"
40 #include "lpe-tool-context.h"
41 #include "shape-editor.h"
42 #include "selection-chemistry.h"
43 #include "selection.h"
44 #include "xml/repr.h"
45 #include "prefs-utils.h"
46 #include "sp-metrics.h"
47 #include "sp-path.h"
48 #include "libnr/nr-matrix-ops.h"
49 #include "svg/svg.h"
50 #include "verbs.h"
51 #include "display/bezier-utils.h"
52 #include <vector>
53 #include <algorithm>
54 #include <cstring>
55 #include <cmath>
56 #include <string>
57 #include "live_effects/lpeobject.h"
58 #include "live_effects/lpeobject-reference.h"
59 #include "live_effects/effect.h"
60 #include "live_effects/parameter/parameter.h"
61 #include "live_effects/parameter/path.h"
62 #include "util/mathfns.h"
63 #include "display/snap-indicator.h"
64 #include "snapped-point.h"
66 class NR::Matrix;
68 /// \todo
69 /// evil evil evil. FIXME: conflict of two different Path classes!
70 /// There is a conflict in the namespace between two classes named Path.
71 /// #include "sp-flowtext.h"
72 /// #include "sp-flowregion.h"
74 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
75 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
76 GType sp_flowregion_get_type (void);
77 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
78 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
79 GType sp_flowtext_get_type (void);
80 // end evil workaround
82 #include "helper/stlport.h"
85 /// \todo fixme: Implement these via preferences */
87 #define NODE_FILL          0xbfbfbf00
88 #define NODE_STROKE        0x000000ff
89 #define NODE_FILL_HI       0xff000000
90 #define NODE_STROKE_HI     0x000000ff
91 #define NODE_FILL_SEL      0x0000ffff
92 #define NODE_STROKE_SEL    0x000000ff
93 #define NODE_FILL_SEL_HI   0xff000000
94 #define NODE_STROKE_SEL_HI 0x000000ff
95 #define KNOT_FILL          0xffffffff
96 #define KNOT_STROKE        0x000000ff
97 #define KNOT_FILL_HI       0xff000000
98 #define KNOT_STROKE_HI     0x000000ff
100 static GMemChunk *nodechunk = NULL;
102 /* Creation from object */
104 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t);
105 static Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length);
107 /* Object updating */
109 static void stamp_repr(Inkscape::NodePath::Path *np);
110 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
111 static gchar *create_typestr(Inkscape::NodePath::Path *np);
113 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
115 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
117 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
119 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
121 /* Adjust handle placement, if the node or the other handle is moved */
122 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
123 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
125 /* Node event callbacks */
126 static void node_clicked(SPKnot *knot, guint state, gpointer data);
127 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
128 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
129 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
131 /* Handle event callbacks */
132 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
133 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
134 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
135 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
136 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
137 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
139 /* Constructors and destructors */
141 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
142 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
143 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
144 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
145 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
146                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
147 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
149 /* Helpers */
151 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
152 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
153 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
155 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
156 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
158 // active_node indicates mouseover node
159 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
161 static SPCanvasItem *
162 sp_nodepath_make_helper_item(Inkscape::NodePath::Path *np, /*SPDesktop *desktop, */const SPCurve *curve, bool show = false) {
163     SPCurve *helper_curve = curve->copy();
164     helper_curve->transform(np->i2d);
165     SPCanvasItem *helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
166     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
167     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helper_path), 0, SP_WIND_RULE_NONZERO);
168     sp_canvas_item_move_to_z(helper_path, 0);
169     if (show) {
170         sp_canvas_item_show(helper_path);
171     }
172     helper_curve->unref();
173     return helper_path;
176 static SPCanvasItem *
177 canvasitem_from_pathvec(Inkscape::NodePath::Path *np, Geom::PathVector const &pathv, bool show) {
178     SPCurve *helper_curve = new SPCurve(pathv);
179     return sp_nodepath_make_helper_item(np, helper_curve, show);
182 static void
183 sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) {
184     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> >* helper_path_vec;
185     if (!SP_IS_LPE_ITEM(np->item)) {
186         g_print ("Only LPEItems can have helperpaths!\n");
187         return;
188     }
190     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
191     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
192     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
193         Inkscape::LivePathEffect::LPEObjectReference *lperef = (*i);
194         Inkscape::LivePathEffect::Effect *lpe = lperef->lpeobject->lpe;
195         // create new canvas items from the effect's helper paths
196         std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
197         for (std::vector<Geom::PathVector>::iterator j = hpaths.begin(); j != hpaths.end(); ++j) {
198             (*np->helper_path_vec)[lpe].push_back(canvasitem_from_pathvec(np, *j, true));
199         }
200     }
203 void
204 sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np) {
205     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> >* helper_path_vec;
206     if (!SP_IS_LPE_ITEM(np->item)) {
207         g_print ("Only LPEItems can have helperpaths!\n");
208         return;
209     }
211     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
212     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
213     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
214         Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->lpe;
215         /* update canvas items from the effect's helper paths; note that this code relies on the
216          * fact that getHelperPaths() will always return the same number of helperpaths in the same
217          * order as during their creation in sp_nodepath_create_helperpaths
218          */
219         std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
220         for (unsigned int j = 0; j < hpaths.size(); ++j) {
221             SPCurve *curve = new SPCurve(hpaths[j]);
222             curve->transform(np->i2d);
223             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(((*np->helper_path_vec)[lpe])[j]), curve);
224             curve = curve->unref();
225         }
226     }
229 static void
230 sp_nodepath_destroy_helperpaths(Inkscape::NodePath::Path *np) {
231     for (HelperPathList::iterator i = np->helper_path_vec->begin(); i != np->helper_path_vec->end(); ++i) {
232         for (std::vector<SPCanvasItem *>::iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) {
233             GtkObject *temp = *j;
234             *j = NULL;
235             gtk_object_destroy(temp);
236         }
237     }
241 /**
242  * \brief Creates new nodepath from item
243  */
244 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
246     Inkscape::XML::Node *repr = object->repr;
248     /** \todo
249      * FIXME: remove this. We don't want to edit paths inside flowtext.
250      * Instead we will build our flowtext with cloned paths, so that the
251      * real paths are outside the flowtext and thus editable as usual.
252      */
253     if (SP_IS_FLOWTEXT(object)) {
254         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
255             if SP_IS_FLOWREGION(child) {
256                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
257                 if (grandchild && SP_IS_PATH(grandchild)) {
258                     object = SP_ITEM(grandchild);
259                     break;
260                 }
261             }
262         }
263     }
265     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
267     if (curve == NULL)
268         return NULL;
270     if (curve->get_segment_count() < 1) {
271         curve->unref();
272         return NULL; // prevent crash for one-node paths
273     }
275     //Create new nodepath
276     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
277     if (!np) {
278         curve->unref();
279         return NULL;
280     }
282     // Set defaults
283     np->desktop     = desktop;
284     np->object      = object;
285     np->subpaths    = NULL;
286     np->selected    = NULL;
287     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
288     np->local_change = 0;
289     np->show_handles = show_handles;
290     np->helper_path = NULL;
291     np->helper_path_vec = new HelperPathList;
292     np->helperpath_rgba = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
293     np->helperpath_width = 1.0;
294     np->curve = curve->copy();
295     np->show_helperpath = (prefs_get_int_attribute ("tools.nodes", "show_helperpath",  0) == 1);
296     if (SP_IS_LPE_ITEM(object)) {
297         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
298         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
299             np->show_helperpath = true;
300         }            
301     }
302     np->straight_path = false;
303     if (IS_LIVEPATHEFFECT(object) && item) {
304         np->item = item;
305     } else {
306         np->item = SP_ITEM(object);
307     }
309     // we need to update item's transform from the repr here,
310     // because they may be out of sync when we respond
311     // to a change in repr by regenerating nodepath     --bb
312     sp_object_read_attr(SP_OBJECT(np->item), "transform");
314     np->i2d  = sp_item_i2d_affine(np->item);
315     np->d2i  = np->i2d.inverse();
317     np->repr = repr;
318     if (repr_key_in) { // apparantly the object is an LPEObject
319         np->repr_key = g_strdup(repr_key_in);
320         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
321         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
322         if (lpeparam) {
323             lpeparam->param_setup_nodepath(np);
324         }
325     } else {
326         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
327         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
328             np->repr_key = g_strdup("inkscape:original-d");
330             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
331             if (lpe) {
332                 lpe->setup_nodepath(np);
333             }
334         } else {
335             np->repr_key = g_strdup("d");
336         }
337     }
339     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
340      * So for example a closed rectangle has a nodetypestring of length 5.
341      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
342     Geom::PathVector pathv_sanitized = pathv_to_linear_and_cubic_beziers(np->curve->get_pathvector());
343     np->curve->set_pathvector(pathv_sanitized);
344     guint length = np->curve->get_segment_count();
345     for (Geom::PathVector::const_iterator pit = pathv_sanitized.begin(); pit != pathv_sanitized.end(); ++pit) {
346         length += pit->empty() ? 0 : 1;
347     }
349     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
350     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
352     // create the subpath(s) from the bpath
353     subpaths_from_pathvector(np, pathv_sanitized, typestr);
355     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
356     np->subpaths = g_list_reverse(np->subpaths);
358     delete[] typestr;
359     curve->unref();
361     // Draw helper curve
362     if (np->show_helperpath) {
363         np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true);
364     }
366     sp_nodepath_create_helperpaths(np);
368     return np;
371 /**
372  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
373  */
374 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
376     if (!np)  //soft fail, like delete
377         return;
379     while (np->subpaths) {
380         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
381     }
383     //Inform the ShapeEditor that made me, if any, that I am gone.
384     if (np->shape_editor)
385         np->shape_editor->nodepath_destroyed();
387     g_assert(!np->selected);
389     if (np->helper_path) {
390         GtkObject *temp = np->helper_path;
391         np->helper_path = NULL;
392         gtk_object_destroy(temp);
393     }
394     if (np->curve) {
395         np->curve->unref();
396         np->curve = NULL;
397     }
399     if (np->repr_key) {
400         g_free(np->repr_key);
401         np->repr_key = NULL;
402     }
403     if (np->repr_nodetypes_key) {
404         g_free(np->repr_nodetypes_key);
405         np->repr_nodetypes_key = NULL;
406     }
408     sp_nodepath_destroy_helperpaths(np);
409     delete np->helper_path_vec;
410     np->helper_path_vec = NULL;
412     np->desktop = NULL;
414     g_free(np);
417 /**
418  *  Return the node count of a given NodeSubPath.
419  */
420 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
422     if (!subpath)
423         return 0;
424     gint nodeCount = g_list_length(subpath->nodes);
425     return nodeCount;
428 /**
429  *  Return the node count of a given NodePath.
430  */
431 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
433     if (!np)
434         return 0;
435     gint nodeCount = 0;
436     for (GList *item = np->subpaths ; item ; item=item->next) {
437        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
438         nodeCount += g_list_length(subpath->nodes);
439     }
440     return nodeCount;
443 /**
444  *  Return the subpath count of a given NodePath.
445  */
446 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
448     if (!np)
449         return 0;
450     return g_list_length (np->subpaths);
453 /**
454  *  Return the selected node count of a given NodePath.
455  */
456 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
458     if (!np)
459         return 0;
460     return g_list_length (np->selected);
463 /**
464  *  Return the number of subpaths where nodes are selected in a given NodePath.
465  */
466 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
468     if (!np)
469         return 0;
470     if (!np->selected)
471         return 0;
472     if (!np->selected->next)
473         return 1;
474     gint count = 0;
475     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
476         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
477         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
478             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
479             if (node->selected) {
480                 count ++;
481                 break;
482             }
483         }
484     }
485     return count;
488 /**
489  * Clean up a nodepath after editing.
490  *
491  * Currently we are deleting trivial subpaths.
492  */
493 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
495     GList *badSubPaths = NULL;
497     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
498     for (GList *l = nodepath->subpaths; l ; l=l->next) {
499        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
500        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
501             badSubPaths = g_list_append(badSubPaths, sp);
502     }
504     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
505     //also removes the subpath from nodepath->subpaths
506     for (GList *l = badSubPaths; l ; l=l->next) {
507        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
508         sp_nodepath_subpath_destroy(sp);
509     }
511     g_list_free(badSubPaths);
514 /**
515  * Create new nodepaths from pathvector, make it subpaths of np.
516  * \param t The node type array.
517  */
518 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
520     guint i = 0;  // index into node type array
521     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
522         if (pit->empty())
523             continue;  // don't add single knot paths
525         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
527         NR::Point ppos = pit->initialPoint() * (Geom::Matrix)np->i2d;
528         NRPathcode pcode = NR_MOVETO;
530         /* 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)*/
531         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
532             if( dynamic_cast<Geom::LineSegment const*>(&*cit) ||
533                 dynamic_cast<Geom::HLineSegment const*>(&*cit) ||
534                 dynamic_cast<Geom::VLineSegment const*>(&*cit) )
535             {
536                 NR::Point pos = cit->initialPoint() * (Geom::Matrix)np->i2d;
537                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
539                 ppos = cit->finalPoint() * (Geom::Matrix)np->i2d;
540                 pcode = NR_LINETO;
541             }
542             else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
543                 std::vector<Geom::Point> points = cubic_bezier->points();
544                 NR::Point pos = points[0] * (Geom::Matrix)np->i2d;
545                 NR::Point npos = points[1] * (Geom::Matrix)np->i2d;
546                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
548                 ppos = points[2] * (Geom::Matrix)np->i2d;
549                 pcode = NR_CURVETO;
550             }
551         }
553         if (pit->closed()) {
554             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
555             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
556              * If the length is zero, don't add it to the nodepath. */
557             Geom::Curve const &closing_seg = pit->back_closed();
558             if ( ! closing_seg.isDegenerate() ) {
559                 NR::Point pos = closing_seg.finalPoint() * (Geom::Matrix)np->i2d;
560                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
561             }
563             sp_nodepath_subpath_close(sp);
564         }
565     }
568 /**
569  * Convert from sodipodi:nodetypes to new style type array.
570  */
571 static
572 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
574     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
576     guint pos = 0;
578     if (types) {
579         for (guint i = 0; types[i] && ( i < length ); i++) {
580             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
581             if (types[i] != '\0') {
582                 switch (types[i]) {
583                     case 's':
584                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
585                         break;
586                     case 'z':
587                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
588                         break;
589                     case 'c':
590                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
591                         break;
592                     default:
593                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
594                         break;
595                 }
596             }
597         }
598     }
600     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
602     return typestr;
605 /**
606  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
607  * updated but repr is not (for speed). Used during curve and node drag.
608  */
609 static void update_object(Inkscape::NodePath::Path *np)
611     g_assert(np);
613     np->curve->unref();
614     np->curve = create_curve(np);
616     sp_nodepath_set_curve(np, np->curve);
618     if (np->show_helperpath) {
619         SPCurve * helper_curve = np->curve->copy();
620         helper_curve->transform(np->i2d);
621         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
622         helper_curve->unref();
623     }
625     // updating helperpaths of LPEItems is now done in sp_lpe_item_update();
626     //sp_nodepath_update_helperpaths(np);
628     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
629     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
630     np->shape_editor->update_knotholder();
633 /**
634  * Update XML path node with data from path object.
635  */
636 static void update_repr_internal(Inkscape::NodePath::Path *np)
638     g_assert(np);
640     Inkscape::XML::Node *repr = np->object->repr;
642     np->curve->unref();
643     np->curve = create_curve(np);
645     gchar *typestr = create_typestr(np);
646     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
648     // determine if path has an effect applied and write to correct "d" attribute.
649     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
650         np->local_change++;
651         repr->setAttribute(np->repr_key, svgpath);
652     }
654     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
655         np->local_change++;
656         repr->setAttribute(np->repr_nodetypes_key, typestr);
657     }
659     g_free(svgpath);
660     g_free(typestr);
662     if (np->show_helperpath) {
663         SPCurve * helper_curve = np->curve->copy();
664         helper_curve->transform(np->i2d);
665         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
666         helper_curve->unref();
667     }
669     // TODO: do we need this call here? after all, update_object() should have been called just before
670     //sp_nodepath_update_helperpaths(np);
673 /**
674  * Update XML path node with data from path object, commit changes forever.
675  */
676 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
678     //fixme: np can be NULL, so check before proceeding
679     g_return_if_fail(np != NULL);
681     update_repr_internal(np);
682     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
684     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
685                      annotation);
688 /**
689  * Update XML path node with data from path object, commit changes with undo.
690  */
691 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
693     update_repr_internal(np);
694     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
695                            annotation);
698 /**
699  * Make duplicate of path, replace corresponding XML node in tree, commit.
700  */
701 static void stamp_repr(Inkscape::NodePath::Path *np)
703     g_assert(np);
705     Inkscape::XML::Node *old_repr = np->object->repr;
706     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
708     // remember the position of the item
709     gint pos = old_repr->position();
710     // remember parent
711     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
713     SPCurve *curve = create_curve(np);
714     gchar *typestr = create_typestr(np);
716     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
718     new_repr->setAttribute(np->repr_key, svgpath);
719     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
721     // add the new repr to the parent
722     parent->appendChild(new_repr);
723     // move to the saved position
724     new_repr->setPosition(pos > 0 ? pos : 0);
726     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
727                      _("Stamp"));
729     Inkscape::GC::release(new_repr);
730     g_free(svgpath);
731     g_free(typestr);
732     curve->unref();
735 /**
736  * Create curve from path.
737  */
738 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
740     SPCurve *curve = new SPCurve();
742     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
743        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
744         curve->moveto(sp->first->pos * np->d2i);
745        Inkscape::NodePath::Node *n = sp->first->n.other;
746         while (n) {
747             NR::Point const end_pt = n->pos * np->d2i;
748             switch (n->code) {
749                 case NR_LINETO:
750                     curve->lineto(end_pt);
751                     break;
752                 case NR_CURVETO:
753                     curve->curveto(n->p.other->n.pos * np->d2i,
754                                      n->p.pos * np->d2i,
755                                      end_pt);
756                     break;
757                 default:
758                     g_assert_not_reached();
759                     break;
760             }
761             if (n != sp->last) {
762                 n = n->n.other;
763             } else {
764                 n = NULL;
765             }
766         }
767         if (sp->closed) {
768             curve->closepath();
769         }
770     }
772     return curve;
775 /**
776  * Convert path type string to sodipodi:nodetypes style.
777  */
778 static gchar *create_typestr(Inkscape::NodePath::Path *np)
780     gchar *typestr = g_new(gchar, 32);
781     gint len = 32;
782     gint pos = 0;
784     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
785        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
787         if (pos >= len) {
788             typestr = g_renew(gchar, typestr, len + 32);
789             len += 32;
790         }
792         typestr[pos++] = 'c';
794        Inkscape::NodePath::Node *n;
795         n = sp->first->n.other;
796         while (n) {
797             gchar code;
799             switch (n->type) {
800                 case Inkscape::NodePath::NODE_CUSP:
801                     code = 'c';
802                     break;
803                 case Inkscape::NodePath::NODE_SMOOTH:
804                     code = 's';
805                     break;
806                 case Inkscape::NodePath::NODE_SYMM:
807                     code = 'z';
808                     break;
809                 default:
810                     g_assert_not_reached();
811                     code = '\0';
812                     break;
813             }
815             if (pos >= len) {
816                 typestr = g_renew(gchar, typestr, len + 32);
817                 len += 32;
818             }
820             typestr[pos++] = code;
822             if (n != sp->last) {
823                 n = n->n.other;
824             } else {
825                 n = NULL;
826             }
827         }
828     }
830     if (pos >= len) {
831         typestr = g_renew(gchar, typestr, len + 1);
832         len += 1;
833     }
835     typestr[pos++] = '\0';
837     return typestr;
840 // Returns different message contexts depending on the current context. This function should only
841 // be called when ec is either a SPNodeContext or SPLPEToolContext, thus we return NULL in all
842 // other cases.
843 static Inkscape::MessageContext *
844 get_message_context(SPEventContext *ec)
846     Inkscape::MessageContext *mc;
847     if (SP_IS_NODE_CONTEXT(ec)) {
848         mc = SP_NODE_CONTEXT(ec)->_node_message_context;
849     } else if (SP_IS_LPETOOL_CONTEXT(ec)) {
850         mc = SP_LPETOOL_CONTEXT(ec)->_lpetool_message_context;
851     } else {
852         g_warning ("Nodepath should only be present in Node tool or Geometric tool.");
853         return NULL;
854     }
857 /**
858  \brief Fills node and handle positions for three nodes, splitting line
859   marked by end at distance t.
860  */
861 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
863     g_assert(new_path != NULL);
864     g_assert(end      != NULL);
866     g_assert(end->p.other == new_path);
867    Inkscape::NodePath::Node *start = new_path->p.other;
868     g_assert(start);
870     if (end->code == NR_LINETO) {
871         new_path->type =Inkscape::NodePath::NODE_CUSP;
872         new_path->code = NR_LINETO;
873         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
874     } else {
875         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
876         new_path->code = NR_CURVETO;
877         gdouble s      = 1 - t;
878         for (int dim = 0; dim < 2; dim++) {
879             NR::Coord const f000 = start->pos[dim];
880             NR::Coord const f001 = start->n.pos[dim];
881             NR::Coord const f011 = end->p.pos[dim];
882             NR::Coord const f111 = end->pos[dim];
883             NR::Coord const f00t = s * f000 + t * f001;
884             NR::Coord const f01t = s * f001 + t * f011;
885             NR::Coord const f11t = s * f011 + t * f111;
886             NR::Coord const f0tt = s * f00t + t * f01t;
887             NR::Coord const f1tt = s * f01t + t * f11t;
888             NR::Coord const fttt = s * f0tt + t * f1tt;
889             start->n.pos[dim]    = f00t;
890             new_path->p.pos[dim] = f0tt;
891             new_path->pos[dim]   = fttt;
892             new_path->n.pos[dim] = f1tt;
893             end->p.pos[dim]      = f11t;
894         }
895     }
898 /**
899  * Adds new node on direct line between two nodes, activates handles of all
900  * three nodes.
901  */
902 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
904     g_assert(end);
905     g_assert(end->subpath);
906     g_assert(g_list_find(end->subpath->nodes, end));
908    Inkscape::NodePath::Node *start = end->p.other;
909     g_assert( start->n.other == end );
910    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
911                                                end,
912                                                (NRPathcode)end->code == NR_LINETO?
913                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
914                                                (NRPathcode)end->code,
915                                                &start->pos, &start->pos, &start->n.pos);
916     sp_nodepath_line_midpoint(newnode, end, t);
918     sp_node_adjust_handles(start);
919     sp_node_update_handles(start);
920     sp_node_update_handles(newnode);
921     sp_node_adjust_handles(end);
922     sp_node_update_handles(end);
924     return newnode;
927 /**
928 \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
929 */
930 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
932     g_assert(node);
933     g_assert(node->subpath);
934     g_assert(g_list_find(node->subpath->nodes, node));
936    Inkscape::NodePath::SubPath *sp = node->subpath;
937     Inkscape::NodePath::Path *np    = sp->nodepath;
939     if (sp->closed) {
940         sp_nodepath_subpath_open(sp, node);
941         return sp->first;
942     } else {
943         // no break for end nodes
944         if (node == sp->first) return NULL;
945         if (node == sp->last ) return NULL;
947         // create a new subpath
948        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
950         // duplicate the break node as start of the new subpath
951         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
953         // attach rest of curve to new node
954         g_assert(node->n.other);
955         newnode->n.other = node->n.other; node->n.other = NULL;
956         newnode->n.other->p.other = newnode;
957         newsubpath->last = sp->last;
958         sp->last = node;
959         node = newnode;
960         while (node->n.other) {
961             node = node->n.other;
962             node->subpath = newsubpath;
963             sp->nodes = g_list_remove(sp->nodes, node);
964             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
965         }
968         return newnode;
969     }
972 /**
973  * Duplicate node and connect to neighbours.
974  */
975 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
977     g_assert(node);
978     g_assert(node->subpath);
979     g_assert(g_list_find(node->subpath->nodes, node));
981    Inkscape::NodePath::SubPath *sp = node->subpath;
983     NRPathcode code = (NRPathcode) node->code;
984     if (code == NR_MOVETO) { // if node is the endnode,
985         node->code = NR_LINETO; // new one is inserted before it, so change that to line
986     }
988     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
990     if (!node->n.other || !node->p.other) // if node is an endnode, select it
991         return node;
992     else
993         return newnode; // otherwise select the newly created node
996 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
998     node->p.pos = (node->pos + (node->pos - node->n.pos));
1001 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
1003     node->n.pos = (node->pos + (node->pos - node->p.pos));
1006 /**
1007  * Change line type at node, with side effects on neighbours.
1008  */
1009 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
1011     g_assert(end);
1012     g_assert(end->subpath);
1013     g_assert(end->p.other);
1015     if (end->code == static_cast< guint > ( code ) )
1016         return;
1018    Inkscape::NodePath::Node *start = end->p.other;
1020     end->code = code;
1022     if (code == NR_LINETO) {
1023         if (start->code == NR_LINETO) {
1024             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
1025         }
1026         if (end->n.other) {
1027             if (end->n.other->code == NR_LINETO) {
1028                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
1029             }
1030         }
1031     } else {
1032         NR::Point delta = end->pos - start->pos;
1033         start->n.pos = start->pos + delta / 3;
1034         end->p.pos = end->pos - delta / 3;
1035         sp_node_adjust_handle(start, 1);
1036         sp_node_adjust_handle(end, -1);
1037     }
1039     sp_node_update_handles(start);
1040     sp_node_update_handles(end);
1043 /**
1044  * Change node type, and its handles accordingly.
1045  */
1046 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1048     g_assert(node);
1049     g_assert(node->subpath);
1051     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1052         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1053             type =Inkscape::NodePath::NODE_CUSP;
1054         }
1055     }
1057     node->type = type;
1059     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1060         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1061         node->knot->setSize (node->selected? 11 : 9);
1062         sp_knot_update_ctrl(node->knot);
1063     } else {
1064         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1065         node->knot->setSize (node->selected? 9 : 7);
1066         sp_knot_update_ctrl(node->knot);
1067     }
1069     // if one of handles is mouseovered, preserve its position
1070     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1071         sp_node_adjust_handle(node, 1);
1072     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1073         sp_node_adjust_handle(node, -1);
1074     } else {
1075         sp_node_adjust_handles(node);
1076     }
1078     sp_node_update_handles(node);
1080     sp_nodepath_update_statusbar(node->subpath->nodepath);
1082     return node;
1085 bool
1086 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1088         Inkscape::NodePath::Node *othernode = side->other;
1089         if (!othernode)
1090             return false;
1091         NRPathcode const code = sp_node_path_code_from_side(node, side);
1092         if (code == NR_LINETO)
1093             return true;
1094         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1095         if (&node->p == side) {
1096             other_to_me = &othernode->n;
1097         } else if (&node->n == side) {
1098             other_to_me = &othernode->p;
1099         } 
1100         if (!other_to_me)
1101             return false;
1102         bool is_line = 
1103              (NR::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1104               NR::L2(node->pos - side->pos) < 1e-6);
1105         return is_line;
1108 /**
1109  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1110  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1111  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1112  * If already cusp and set to cusp, retracts handles.
1113 */
1114 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1116     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1118 /* 
1119   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1120  
1121         if (two_handles) {
1122             // do nothing, adjust_handles called via set_node_type will line them up
1123         } else if (one_handle) {
1124             if (opposite_to_handle_is_line) {
1125                 if (lined_up) {
1126                     // already half-smooth; pull opposite handle too making it fully smooth
1127                 } else {
1128                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1129                 }
1130             } else {
1131                 // pull opposite handle in line with the existing one
1132             }
1133         } else if (no_handles) {
1134             if (both_segments_are_lines OR both_segments_are_curves) {
1135                 //pull both handles
1136             } else {
1137                 // pull the handle opposite to line segment, making node half-smooth
1138             }
1139         }
1140 */
1141         bool p_has_handle = (NR::L2(node->pos  - node->p.pos) > 1e-6);
1142         bool n_has_handle = (NR::L2(node->pos  - node->n.pos) > 1e-6);
1143         bool p_is_line = sp_node_side_is_line(node, &node->p);
1144         bool n_is_line = sp_node_side_is_line(node, &node->n);
1146         if (p_has_handle && n_has_handle) {
1147             // do nothing, adjust_handles will line them up
1148         } else if (p_has_handle || n_has_handle) {
1149             if (p_has_handle && n_is_line) {
1150                 Radial line (node->n.other->pos - node->pos);
1151                 Radial handle (node->pos - node->p.pos);
1152                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1153                     // already half-smooth; pull opposite handle too making it fully smooth
1154                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1155                 } else {
1156                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1157                 }
1158             } else if (n_has_handle && p_is_line) {
1159                 Radial line (node->p.other->pos - node->pos);
1160                 Radial handle (node->pos - node->n.pos);
1161                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1162                     // already half-smooth; pull opposite handle too making it fully smooth
1163                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1164                 } else {
1165                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1166                 }
1167             } else if (p_has_handle && node->n.other) {
1168                 // pull n handle
1169                 node->n.other->code = NR_CURVETO;
1170                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1171                     NR::L2(node->p.pos - node->pos) :
1172                     NR::L2(node->n.other->pos - node->pos) / 3;
1173                 node->n.pos = node->pos - (len / NR::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1174             } else if (n_has_handle && node->p.other) {
1175                 // pull p handle
1176                 node->code = NR_CURVETO;
1177                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1178                     NR::L2(node->n.pos - node->pos) :
1179                     NR::L2(node->p.other->pos - node->pos) / 3;
1180                 node->p.pos = node->pos - (len / NR::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1181             }
1182         } else if (!p_has_handle && !n_has_handle) {
1183             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1184                 // no handles, but both segments are either lnes or curves:
1185                 //pull both handles
1187                 // convert both to curves:
1188                 node->code = NR_CURVETO;
1189                 node->n.other->code = NR_CURVETO;
1191                 NR::Point leg_prev = node->pos - node->p.other->pos;
1192                 NR::Point leg_next = node->pos - node->n.other->pos;
1194                 double norm_leg_prev = L2(leg_prev);
1195                 double norm_leg_next = L2(leg_next);
1197                 NR::Point delta;
1198                 if (norm_leg_next > 0.0) {
1199                     delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1200                     (&delta)->normalize();
1201                 }
1203                 if (type == Inkscape::NodePath::NODE_SYMM) {
1204                     double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1205                     node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1206                     node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1207                 } else {
1208                     // length of handle is proportional to distance to adjacent node
1209                     node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1210                     node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1211                 }
1213             } else {
1214                 // pull the handle opposite to line segment, making it half-smooth
1215                 if (p_is_line && node->n.other) {
1216                     if (type != Inkscape::NodePath::NODE_SYMM) {
1217                         // pull n handle
1218                         node->n.other->code = NR_CURVETO;
1219                         double len =  NR::L2(node->n.other->pos - node->pos) / 3;
1220                         node->n.pos = node->pos + (len / NR::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1221                     }
1222                 } else if (n_is_line && node->p.other) {
1223                     if (type != Inkscape::NodePath::NODE_SYMM) {
1224                         // pull p handle
1225                         node->code = NR_CURVETO;
1226                         double len =  NR::L2(node->p.other->pos - node->pos) / 3;
1227                         node->p.pos = node->pos + (len / NR::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1228                     }
1229                 }
1230             }
1231         }
1232     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1233         // cusping a cusp: retract nodes
1234         node->p.pos = node->pos;
1235         node->n.pos = node->pos;
1236     }
1238     sp_nodepath_set_node_type (node, type);
1241 /**
1242  * Move node to point, and adjust its and neighbouring handles.
1243  */
1244 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1246     NR::Point delta = p - node->pos;
1247     node->pos = p;
1249     node->p.pos += delta;
1250     node->n.pos += delta;
1252     Inkscape::NodePath::Node *node_p = NULL;
1253     Inkscape::NodePath::Node *node_n = NULL;
1255     if (node->p.other) {
1256         if (node->code == NR_LINETO) {
1257             sp_node_adjust_handle(node, 1);
1258             sp_node_adjust_handle(node->p.other, -1);
1259             node_p = node->p.other;
1260         }
1261     }
1262     if (node->n.other) {
1263         if (node->n.other->code == NR_LINETO) {
1264             sp_node_adjust_handle(node, -1);
1265             sp_node_adjust_handle(node->n.other, 1);
1266             node_n = node->n.other;
1267         }
1268     }
1270     // this function is only called from batch movers that will update display at the end
1271     // themselves, so here we just move all the knots without emitting move signals, for speed
1272     sp_node_update_handles(node, false);
1273     if (node_n) {
1274         sp_node_update_handles(node_n, false);
1275     }
1276     if (node_p) {
1277         sp_node_update_handles(node_p, false);
1278     }
1281 /**
1282  * Call sp_node_moveto() for node selection and handle possible snapping.
1283  */
1284 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1285                                             bool const snap, bool constrained = false, 
1286                                             Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point())
1288     NR::Coord best = NR_HUGE;
1289     NR::Point delta(dx, dy);
1290     NR::Point best_pt = delta;
1291     Inkscape::SnappedPoint best_abs;
1292     
1293     if (snap) {    
1294         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1295          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1296          * must provide that information. */
1297           
1298         // Build a list of the unselected nodes to which the snapper should snap 
1299         std::vector<Geom::Point> unselected_nodes;
1300         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1301             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1302             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1303                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1304                 if (!node->selected) {
1305                     unselected_nodes.push_back(to_2geom(node->pos));
1306                 }    
1307             }
1308         }        
1309         
1310         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1311         
1312         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1313             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1314             m.setup(NULL, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1315             Inkscape::SnappedPoint s;
1316             if (constrained) {
1317                 Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1318                 dedicated_constraint.setPoint(n->pos);
1319                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(n->pos + delta), dedicated_constraint);
1320             } else {
1321                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(n->pos + delta));
1322             }            
1323             if (s.getSnapped() && (s.getDistance() < best)) {
1324                 best = s.getDistance();
1325                 best_abs = s;
1326                 best_pt = from_2geom(s.getPoint()) - n->pos;
1327             }
1328         }
1329                         
1330         if (best_abs.getSnapped()) {
1331             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1332         } else {
1333             nodepath->desktop->snapindicator->remove_snappoint();    
1334         }
1335     }
1337     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1338         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1339         sp_node_moveto(n, n->pos + best_pt);
1340     }
1342     // do not update repr here so that node dragging is acceptably fast
1343     update_object(nodepath);
1346 /**
1347 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1348 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1349 near x = 0.
1350  */
1351 double
1352 sculpt_profile (double x, double alpha, guint profile)
1354     if (x >= 1)
1355         return 0;
1356     if (x <= 0)
1357         return 1;
1359     switch (profile) {
1360         case SCULPT_PROFILE_LINEAR:
1361         return 1 - x;
1362         case SCULPT_PROFILE_BELL:
1363         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1364         case SCULPT_PROFILE_ELLIPTIC:
1365         return sqrt(1 - x*x);
1366     }
1368     return 1;
1371 double
1372 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1374     // extremely primitive for now, don't have time to look for the real one
1375     double lower = NR::L2(b - a);
1376     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1377     return (lower + upper)/2;
1380 void
1381 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1383     n->pos = n->origin + delta;
1384     n->n.pos = n->n.origin + delta_n;
1385     n->p.pos = n->p.origin + delta_p;
1386     sp_node_adjust_handles(n);
1387     sp_node_update_handles(n, false);
1390 /**
1391  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1392  * on how far they are from the dragged node n.
1393  */
1394 static void
1395 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1397     g_assert (n);
1398     g_assert (nodepath);
1399     g_assert (n->subpath->nodepath == nodepath);
1401     double pressure = n->knot->pressure;
1402     if (pressure == 0)
1403         pressure = 0.5; // default
1404     pressure = CLAMP (pressure, 0.2, 0.8);
1406     // map pressure to alpha = 1/5 ... 5
1407     double alpha = 1 - 2 * fabs(pressure - 0.5);
1408     if (pressure > 0.5)
1409         alpha = 1/alpha;
1411     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1413     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1414         // Only one subpath has selected nodes:
1415         // use linear mode, where the distance from n to node being dragged is calculated along the path
1417         double n_sel_range = 0, p_sel_range = 0;
1418         guint n_nodes = 0, p_nodes = 0;
1419         guint n_sel_nodes = 0, p_sel_nodes = 0;
1421         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1422         {
1423             double n_range = 0, p_range = 0;
1424             bool n_going = true, p_going = true;
1425             Inkscape::NodePath::Node *n_node = n;
1426             Inkscape::NodePath::Node *p_node = n;
1427             do {
1428                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1429                 if (n_node && n_going)
1430                     n_node = n_node->n.other;
1431                 if (n_node == NULL) {
1432                     n_going = false;
1433                 } else {
1434                     n_nodes ++;
1435                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1436                     if (n_node->selected) {
1437                         n_sel_nodes ++;
1438                         n_sel_range = n_range;
1439                     }
1440                     if (n_node == p_node) {
1441                         n_going = false;
1442                         p_going = false;
1443                     }
1444                 }
1445                 if (p_node && p_going)
1446                     p_node = p_node->p.other;
1447                 if (p_node == NULL) {
1448                     p_going = false;
1449                 } else {
1450                     p_nodes ++;
1451                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1452                     if (p_node->selected) {
1453                         p_sel_nodes ++;
1454                         p_sel_range = p_range;
1455                     }
1456                     if (p_node == n_node) {
1457                         n_going = false;
1458                         p_going = false;
1459                     }
1460                 }
1461             } while (n_going || p_going);
1462         }
1464         // Second pass: actually move nodes in this subpath
1465         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1466         {
1467             double n_range = 0, p_range = 0;
1468             bool n_going = true, p_going = true;
1469             Inkscape::NodePath::Node *n_node = n;
1470             Inkscape::NodePath::Node *p_node = n;
1471             do {
1472                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1473                 if (n_node && n_going)
1474                     n_node = n_node->n.other;
1475                 if (n_node == NULL) {
1476                     n_going = false;
1477                 } else {
1478                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1479                     if (n_node->selected) {
1480                         sp_nodepath_move_node_and_handles (n_node,
1481                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1482                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1483                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1484                     }
1485                     if (n_node == p_node) {
1486                         n_going = false;
1487                         p_going = false;
1488                     }
1489                 }
1490                 if (p_node && p_going)
1491                     p_node = p_node->p.other;
1492                 if (p_node == NULL) {
1493                     p_going = false;
1494                 } else {
1495                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1496                     if (p_node->selected) {
1497                         sp_nodepath_move_node_and_handles (p_node,
1498                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1499                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1500                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1501                     }
1502                     if (p_node == n_node) {
1503                         n_going = false;
1504                         p_going = false;
1505                     }
1506                 }
1507             } while (n_going || p_going);
1508         }
1510     } else {
1511         // Multiple subpaths have selected nodes:
1512         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1513         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1514         // fix the pear-like shape when sculpting e.g. a ring
1516         // First pass: calculate range
1517         gdouble direct_range = 0;
1518         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1519             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1520             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1521                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1522                 if (node->selected) {
1523                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1524                 }
1525             }
1526         }
1528         // Second pass: actually move nodes
1529         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1530             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1531             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1532                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1533                 if (node->selected) {
1534                     if (direct_range > 1e-6) {
1535                         sp_nodepath_move_node_and_handles (node,
1536                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1537                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1538                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1539                     } else {
1540                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1541                     }
1543                 }
1544             }
1545         }
1546     }
1548     // do not update repr here so that node dragging is acceptably fast
1549     update_object(nodepath);
1553 /**
1554  * Move node selection to point, adjust its and neighbouring handles,
1555  * handle possible snapping, and commit the change with possible undo.
1556  */
1557 void
1558 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1560     if (!nodepath) return;
1562     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1564     if (dx == 0) {
1565         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1566     } else if (dy == 0) {
1567         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1568     } else {
1569         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1570     }
1573 /**
1574  * Move node selection off screen and commit the change.
1575  */
1576 void
1577 sp_node_selected_move_screen(SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1579     // borrowed from sp_selection_move_screen in selection-chemistry.c
1580     // we find out the current zoom factor and divide deltas by it
1582     gdouble zoom = desktop->current_zoom();
1583     gdouble zdx = dx / zoom;
1584     gdouble zdy = dy / zoom;
1586     if (!nodepath) return;
1588     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1590     if (dx == 0) {
1591         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1592     } else if (dy == 0) {
1593         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1594     } else {
1595         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1596     }
1599 /**
1600  * Move selected nodes to the absolute position given
1601  */
1602 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis)
1604     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1605         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1606         Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]);
1607         sp_node_moveto(n, npos);
1608     }
1610     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1613 /**
1614  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1615  */
1616 boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1618     boost::optional<Geom::Coord> no_coord;
1619     g_return_val_if_fail(nodepath->selected, no_coord);
1621     // determine coordinate of first selected node
1622     GList *nsel = nodepath->selected;
1623     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1624     NR::Coord coord = n->pos[axis];
1625     bool coincide = true;
1627     // compare it to the coordinates of all the other selected nodes
1628     for (GList *l = nsel->next; l != NULL; l = l->next) {
1629         n = (Inkscape::NodePath::Node *) l->data;
1630         if (n->pos[axis] != coord) {
1631             coincide = false;
1632         }
1633     }
1634     if (coincide) {
1635         return coord;
1636     } else {
1637         Geom::Rect bbox = sp_node_selected_bbox(nodepath);
1638         // currently we return the coordinate of the bounding box midpoint because I don't know how
1639         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1640         return bbox.midpoint()[axis];
1641     }
1644 /** If they don't yet exist, creates knot and line for the given side of the node */
1645 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1647     if (!side->knot) {
1648         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"));
1650         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1651         side->knot->setSize (7);
1652         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1653         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1654         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1655         sp_knot_update_ctrl(side->knot);
1657         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1658         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1659         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1660         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1661         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1662         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1663     }
1665     if (!side->line) {
1666         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1667                                         SP_TYPE_CTRLLINE, NULL);
1668     }
1671 /**
1672  * Ensure the given handle of the node is visible/invisible, update its screen position
1673  */
1674 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1676     g_assert(node != NULL);
1678    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1679     NRPathcode code = sp_node_path_code_from_side(node, side);
1681     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1683     if (show_handle) {
1684         if (!side->knot) { // No handle knot at all
1685             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1686             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1687             side->knot->pos = side->pos;
1688             if (side->knot->item)
1689                 SP_CTRL(side->knot->item)->moveto(side->pos);
1690             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1691             sp_knot_show(side->knot);
1692         } else {
1693             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1694                 if (fire_move_signals) {
1695                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1696                 } else {
1697                     sp_knot_moveto(side->knot, side->pos);
1698                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1699                 }
1700             }
1701             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1702                 sp_knot_show(side->knot);
1703             }
1704         }
1705         sp_canvas_item_show(side->line);
1706     } else {
1707         if (side->knot) {
1708             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1709                 sp_knot_hide(side->knot);
1710             }
1711         }
1712         if (side->line) {
1713             sp_canvas_item_hide(side->line);
1714         }
1715     }
1718 /**
1719  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1720  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1721  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1722  * updated; otherwise, just move the knots silently (used in batch moves).
1723  */
1724 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1726     g_assert(node != NULL);
1728     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1729         sp_knot_show(node->knot);
1730     }
1732     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1733         if (fire_move_signals)
1734             sp_knot_set_position(node->knot, node->pos, 0);
1735         else
1736             sp_knot_moveto(node->knot, node->pos);
1737     }
1739     gboolean show_handles = node->selected;
1740     if (node->p.other != NULL) {
1741         if (node->p.other->selected) show_handles = TRUE;
1742     }
1743     if (node->n.other != NULL) {
1744         if (node->n.other->selected) show_handles = TRUE;
1745     }
1747     if (node->subpath->nodepath->show_handles == false)
1748         show_handles = FALSE;
1750     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1751     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1754 /**
1755  * Call sp_node_update_handles() for all nodes on subpath.
1756  */
1757 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1759     g_assert(subpath != NULL);
1761     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1762         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1763     }
1766 /**
1767  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1768  */
1769 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1771     g_assert(nodepath != NULL);
1773     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1774         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1775     }
1778 void
1779 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1781     if (nodepath == NULL) return;
1783     nodepath->show_handles = show;
1784     sp_nodepath_update_handles(nodepath);
1787 /**
1788  * Adds all selected nodes in nodepath to list.
1789  */
1790 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1792     StlConv<Node *>::list(l, selected);
1793 /// \todo this adds a copying, rework when the selection becomes a stl list
1796 /**
1797  * Align selected nodes on the specified axis.
1798  */
1799 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1801     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1802         return;
1803     }
1805     if ( !nodepath->selected->next ) { // only one node selected
1806         return;
1807     }
1808    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1809     NR::Point dest(pNode->pos);
1810     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1811         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1812         if (pNode) {
1813             dest[axis] = pNode->pos[axis];
1814             sp_node_moveto(pNode, dest);
1815         }
1816     }
1818     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1821 /// Helper struct.
1822 struct NodeSort
1824    Inkscape::NodePath::Node *_node;
1825     NR::Coord _coord;
1826     /// \todo use vectorof pointers instead of calling copy ctor
1827     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1828         _node(node), _coord(node->pos[axis])
1829     {}
1831 };
1833 static bool operator<(NodeSort const &a, NodeSort const &b)
1835     return (a._coord < b._coord);
1838 /**
1839  * Distribute selected nodes on the specified axis.
1840  */
1841 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1843     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1844         return;
1845     }
1847     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1848         return;
1849     }
1851    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1852     std::vector<NodeSort> sorted;
1853     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1854         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1855         if (pNode) {
1856             NodeSort n(pNode, axis);
1857             sorted.push_back(n);
1858             //dest[axis] = pNode->pos[axis];
1859             //sp_node_moveto(pNode, dest);
1860         }
1861     }
1862     std::sort(sorted.begin(), sorted.end());
1863     unsigned int len = sorted.size();
1864     //overall bboxes span
1865     float dist = (sorted.back()._coord -
1866                   sorted.front()._coord);
1867     //new distance between each bbox
1868     float step = (dist) / (len - 1);
1869     float pos = sorted.front()._coord;
1870     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1871           it < sorted.end();
1872           it ++ )
1873     {
1874         NR::Point dest((*it)._node->pos);
1875         dest[axis] = pos;
1876         sp_node_moveto((*it)._node, dest);
1877         pos += step;
1878     }
1880     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1884 /**
1885  * Call sp_nodepath_line_add_node() for all selected segments.
1886  */
1887 void
1888 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1890     if (!nodepath) {
1891         return;
1892     }
1894     GList *nl = NULL;
1896     int n_added = 0;
1898     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1899        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1900         g_assert(t->selected);
1901         if (t->p.other && t->p.other->selected) {
1902             nl = g_list_prepend(nl, t);
1903         }
1904     }
1906     while (nl) {
1907        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1908        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1909        sp_nodepath_node_select(n, TRUE, FALSE);
1910        n_added ++;
1911        nl = g_list_remove(nl, t);
1912     }
1914     /** \todo fixme: adjust ? */
1915     sp_nodepath_update_handles(nodepath);
1917     if (n_added > 1) {
1918         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1919     } else if (n_added > 0) {
1920         sp_nodepath_update_repr(nodepath, _("Add node"));
1921     }
1923     sp_nodepath_update_statusbar(nodepath);
1926 /**
1927  * Select segment nearest to point
1928  */
1929 void
1930 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1932     if (!nodepath) {
1933         return;
1934     }
1936     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
1937     Geom::PathVector const &pathv = curve->get_pathvector();
1938     Geom::PathVectorPosition pvpos = Geom::nearestPoint(pathv, p);
1940     // calculate index for nodepath's representation.
1941     unsigned int segment_index = floor(pvpos.t) + 1;
1942     for (unsigned int i = 0; i < pvpos.path_nr; ++i) {
1943         segment_index += pathv[i].size() + 1;
1944         if (pathv[i].closed()) {
1945             segment_index += 1;
1946         }
1947     }
1949     curve->unref();
1951     //find segment to segment
1952     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
1954     //fixme: this can return NULL, so check before proceeding.
1955     g_return_if_fail(e != NULL);
1957     gboolean force = FALSE;
1958     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1959         force = TRUE;
1960     }
1961     sp_nodepath_node_select(e, (gboolean) toggle, force);
1962     if (e->p.other)
1963         sp_nodepath_node_select(e->p.other, TRUE, force);
1965     sp_nodepath_update_handles(nodepath);
1967     sp_nodepath_update_statusbar(nodepath);
1970 /**
1971  * Add a node nearest to point
1972  */
1973 void
1974 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1976     if (!nodepath) {
1977         return;
1978     }
1980     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
1981     Geom::PathVector const &pathv = curve->get_pathvector();
1982     Geom::PathVectorPosition pvpos = Geom::nearestPoint(pathv, p);
1984     // calculate index for nodepath's representation.
1985     double int_part;
1986     double t = std::modf(pvpos.t, &int_part);
1987     unsigned int segment_index = (unsigned int)int_part + 1;
1988     for (unsigned int i = 0; i < pvpos.path_nr; ++i) {
1989         segment_index += pathv[i].size() + 1;
1990         if (pathv[i].closed()) {
1991             segment_index += 1;
1992         }
1993     }
1995     curve->unref();
1997     //find segment to split
1998     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2000     //don't know why but t seems to flip for lines
2001     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2002         t = 1.0 - t;
2003     }
2005     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2006     sp_nodepath_node_select(n, FALSE, TRUE);
2008     /* fixme: adjust ? */
2009     sp_nodepath_update_handles(nodepath);
2011     sp_nodepath_update_repr(nodepath, _("Add node"));
2013     sp_nodepath_update_statusbar(nodepath);
2016 /*
2017  * Adjusts a segment so that t moves by a certain delta for dragging
2018  * converts lines to curves
2019  *
2020  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2021  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2022  */
2023 void
2024 sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, NR::Point delta)
2026     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node);
2028     //fixme: e and e->p can be NULL, so check for those before proceeding
2029     g_return_if_fail(e != NULL);
2030     g_return_if_fail(&e->p != NULL);
2032     /* feel good is an arbitrary parameter that distributes the delta between handles
2033      * if t of the drag point is less than 1/6 distance form the endpoint only
2034      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2035      */
2036     double feel_good;
2037     if (t <= 1.0 / 6.0)
2038         feel_good = 0;
2039     else if (t <= 0.5)
2040         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2041     else if (t <= 5.0 / 6.0)
2042         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2043     else
2044         feel_good = 1;
2046     //if we're dragging a line convert it to a curve
2047     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2048         sp_nodepath_set_line_type(e, NR_CURVETO);
2049     }
2051     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2052     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2053     e->p.other->n.pos += offsetcoord0;
2054     e->p.pos += offsetcoord1;
2056     // adjust handles of adjacent nodes where necessary
2057     sp_node_adjust_handle(e,1);
2058     sp_node_adjust_handle(e->p.other,-1);
2060     sp_nodepath_update_handles(e->subpath->nodepath);
2062     update_object(e->subpath->nodepath);
2064     sp_nodepath_update_statusbar(e->subpath->nodepath);
2068 /**
2069  * Call sp_nodepath_break() for all selected segments.
2070  */
2071 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2073     if (!nodepath) return;
2075     GList *tempin = g_list_copy(nodepath->selected);
2076     GList *temp = NULL;
2077     for (GList *l = tempin; l != NULL; l = l->next) {
2078        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2079        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2080         if (nn == NULL) continue; // no break, no new node
2081         temp = g_list_prepend(temp, nn);
2082     }
2083     g_list_free(tempin);
2085     if (temp) {
2086         sp_nodepath_deselect(nodepath);
2087     }
2088     for (GList *l = temp; l != NULL; l = l->next) {
2089         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2090     }
2092     sp_nodepath_update_handles(nodepath);
2094     sp_nodepath_update_repr(nodepath, _("Break path"));
2097 /**
2098  * Duplicate the selected node(s).
2099  */
2100 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2102     if (!nodepath) {
2103         return;
2104     }
2106     GList *temp = NULL;
2107     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2108        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2109        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2110         if (nn == NULL) continue; // could not duplicate
2111         temp = g_list_prepend(temp, nn);
2112     }
2114     if (temp) {
2115         sp_nodepath_deselect(nodepath);
2116     }
2117     for (GList *l = temp; l != NULL; l = l->next) {
2118         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2119     }
2121     sp_nodepath_update_handles(nodepath);
2123     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2126 /**
2127  *  Internal function to join two nodes by merging them into one.
2128  */
2129 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2131     /* a and b are endpoints */
2133     // if one of the two nodes is mouseovered, fix its position
2134     NR::Point c;
2135     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2136         c = a->pos;
2137     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2138         c = b->pos;
2139     } else {
2140         // otherwise, move joined node to the midpoint
2141         c = (a->pos + b->pos) / 2;
2142     }
2144     if (a->subpath == b->subpath) {
2145        Inkscape::NodePath::SubPath *sp = a->subpath;
2146         sp_nodepath_subpath_close(sp);
2147         sp_node_moveto (sp->first, c);
2149         sp_nodepath_update_handles(sp->nodepath);
2150         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2151         return;
2152     }
2154     /* a and b are separate subpaths */
2155     Inkscape::NodePath::SubPath *sa = a->subpath;
2156     Inkscape::NodePath::SubPath *sb = b->subpath;
2157     NR::Point p;
2158     Inkscape::NodePath::Node *n;
2159     NRPathcode code;
2160     if (a == sa->first) {
2161         // we will now reverse sa, so that a is its last node, not first, and drop that node
2162         p = sa->first->n.pos;
2163         code = (NRPathcode)sa->first->n.other->code;
2164         // create new subpath
2165        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2166        // create a first moveto node on it
2167         n = sa->last;
2168         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2169         n = n->p.other;
2170         if (n == sa->first) n = NULL;
2171         while (n) {
2172             // copy the rest of the nodes from sa to t, going backwards
2173             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2174             n = n->p.other;
2175             if (n == sa->first) n = NULL;
2176         }
2177         // replace sa with t
2178         sp_nodepath_subpath_destroy(sa);
2179         sa = t;
2180     } else if (a == sa->last) {
2181         // a is already last, just drop it
2182         p = sa->last->p.pos;
2183         code = (NRPathcode)sa->last->code;
2184         sp_nodepath_node_destroy(sa->last);
2185     } else {
2186         code = NR_END;
2187         g_assert_not_reached();
2188     }
2190     if (b == sb->first) {
2191         // copy all nodes from b to a, forward 
2192         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2193         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2194             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2195         }
2196     } else if (b == sb->last) {
2197         // copy all nodes from b to a, backward 
2198         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2199         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2200             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2201         }
2202     } else {
2203         g_assert_not_reached();
2204     }
2205     /* and now destroy sb */
2207     sp_nodepath_subpath_destroy(sb);
2209     sp_nodepath_update_handles(sa->nodepath);
2211     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2213     sp_nodepath_update_statusbar(nodepath);
2216 /**
2217  *  Internal function to join two nodes by adding a segment between them.
2218  */
2219 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2221     if (a->subpath == b->subpath) {
2222        Inkscape::NodePath::SubPath *sp = a->subpath;
2224         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2225         sp->closed = TRUE;
2227         sp->first->p.other = sp->last;
2228         sp->last->n.other  = sp->first;
2230         sp_node_handle_mirror_p_to_n(sp->last);
2231         sp_node_handle_mirror_n_to_p(sp->first);
2233         sp->first->code = sp->last->code;
2234         sp->first       = sp->last;
2236         sp_nodepath_update_handles(sp->nodepath);
2238         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2240         return;
2241     }
2243     /* a and b are separate subpaths */
2244     Inkscape::NodePath::SubPath *sa = a->subpath;
2245     Inkscape::NodePath::SubPath *sb = b->subpath;
2247     Inkscape::NodePath::Node *n;
2248     NR::Point p;
2249     NRPathcode code;
2250     if (a == sa->first) {
2251         code = (NRPathcode) sa->first->n.other->code;
2252        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2253         n = sa->last;
2254         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2255         for (n = n->p.other; n != NULL; n = n->p.other) {
2256             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2257         }
2258         sp_nodepath_subpath_destroy(sa);
2259         sa = t;
2260     } else if (a == sa->last) {
2261         code = (NRPathcode)sa->last->code;
2262     } else {
2263         code = NR_END;
2264         g_assert_not_reached();
2265     }
2267     if (b == sb->first) {
2268         n = sb->first;
2269         sp_node_handle_mirror_p_to_n(sa->last);
2270         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2271         sp_node_handle_mirror_n_to_p(sa->last);
2272         for (n = n->n.other; n != NULL; n = n->n.other) {
2273             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2274         }
2275     } else if (b == sb->last) {
2276         n = sb->last;
2277         sp_node_handle_mirror_p_to_n(sa->last);
2278         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2279         sp_node_handle_mirror_n_to_p(sa->last);
2280         for (n = n->p.other; n != NULL; n = n->p.other) {
2281             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2282         }
2283     } else {
2284         g_assert_not_reached();
2285     }
2286     /* and now destroy sb */
2288     sp_nodepath_subpath_destroy(sb);
2290     sp_nodepath_update_handles(sa->nodepath);
2292     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2295 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2297 /**
2298  * Internal function to handle joining two nodes.
2299  */
2300 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2302     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2304     if (g_list_length(nodepath->selected) != 2) {
2305         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2306         return;
2307     }
2309     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2310     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2312     g_assert(a != b);
2313     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2314         // someone tried to join an orphan node (i.e. a single-node subpath).
2315         // this is not worth an error message, just fail silently.
2316         return;
2317     }
2319     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2320         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2321         return;
2322     }
2324     switch(mode) {
2325         case NODE_JOIN_ENDPOINTS:
2326             do_node_selected_join(nodepath, a, b);
2327             break;
2328         case NODE_JOIN_SEGMENT:
2329             do_node_selected_join_segment(nodepath, a, b);
2330             break;
2331     }
2334 /**
2335  *  Join two nodes by merging them into one.
2336  */
2337 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2339     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2342 /**
2343  *  Join two nodes by adding a segment between them.
2344  */
2345 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2347     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2350 /**
2351  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2352  */
2353 void sp_node_delete_preserve(GList *nodes_to_delete)
2355     GSList *nodepaths = NULL;
2357     while (nodes_to_delete) {
2358         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2359         Inkscape::NodePath::SubPath *sp = node->subpath;
2360         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2361         Inkscape::NodePath::Node *sample_cursor = NULL;
2362         Inkscape::NodePath::Node *sample_end = NULL;
2363         Inkscape::NodePath::Node *delete_cursor = node;
2364         bool just_delete = false;
2366         //find the start of this contiguous selection
2367         //move left to the first node that is not selected
2368         //or the start of the non-closed path
2369         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2370             delete_cursor = curr;
2371         }
2373         //just delete at the beginning of an open path
2374         if (!delete_cursor->p.other) {
2375             sample_cursor = delete_cursor;
2376             just_delete = true;
2377         } else {
2378             sample_cursor = delete_cursor->p.other;
2379         }
2381         //calculate points for each segment
2382         int rate = 5;
2383         float period = 1.0 / rate;
2384         std::vector<NR::Point> data;
2385         if (!just_delete) {
2386             data.push_back(sample_cursor->pos);
2387             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2388                 //just delete at the end of an open path
2389                 if (!sp->closed && curr == sp->last) {
2390                     just_delete = true;
2391                     break;
2392                 }
2394                 //sample points on the contiguous selected segment
2395                 NR::Point *bez;
2396                 bez = new NR::Point [4];
2397                 bez[0] = curr->pos;
2398                 bez[1] = curr->n.pos;
2399                 bez[2] = curr->n.other->p.pos;
2400                 bez[3] = curr->n.other->pos;
2401                 for (int i=1; i<rate; i++) {
2402                     gdouble t = i * period;
2403                     NR::Point p = bezier_pt(3, bez, t);
2404                     data.push_back(p);
2405                 }
2406                 data.push_back(curr->n.other->pos);
2408                 sample_end = curr->n.other;
2409                 //break if we've come full circle or hit the end of the selection
2410                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2411                     break;
2412                 }
2413             }
2414         }
2416         if (!just_delete) {
2417             //calculate the best fitting single segment and adjust the endpoints
2418             NR::Point *adata;
2419             adata = new NR::Point [data.size()];
2420             copy(data.begin(), data.end(), adata);
2422             NR::Point *bez;
2423             bez = new NR::Point [4];
2424             //would decreasing error create a better fitting approximation?
2425             gdouble error = 1.0;
2426             gint ret;
2427             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2429             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2430             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2431             //the resulting nodes behave as expected.
2432             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2433                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2434             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2435                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2437             //adjust endpoints
2438             sample_cursor->n.pos = bez[1];
2439             sample_end->p.pos = bez[2];
2440         }
2442         //destroy this contiguous selection
2443         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2444             Inkscape::NodePath::Node *temp = delete_cursor;
2445             if (delete_cursor->n.other == delete_cursor) {
2446                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2447                 delete_cursor = NULL;
2448             } else {
2449                 delete_cursor = delete_cursor->n.other;
2450             }
2451             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2452             sp_nodepath_node_destroy(temp);
2453         }
2455         sp_nodepath_update_handles(nodepath);
2457         if (!g_slist_find(nodepaths, nodepath))
2458             nodepaths = g_slist_prepend (nodepaths, nodepath);
2459     }
2461     for (GSList *i = nodepaths; i; i = i->next) {
2462         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2463         // different nodepaths will give us one undo event per nodepath
2464         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2466         // if the entire nodepath is removed, delete the selected object.
2467         if (nodepath->subpaths == NULL ||
2468             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2469             //at least 2
2470             sp_nodepath_get_node_count(nodepath) < 2) {
2471             SPDocument *document = sp_desktop_document (nodepath->desktop);
2472             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2473             //delete this nodepath's object, not the entire selection! (though at this time, this
2474             //does not matter)
2475             sp_selection_delete(nodepath->desktop);
2476             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2477                               _("Delete nodes"));
2478         } else {
2479             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2480             sp_nodepath_update_statusbar(nodepath);
2481         }
2482     }
2484     g_slist_free (nodepaths);
2487 /**
2488  * Delete one or more selected nodes.
2489  */
2490 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2492     if (!nodepath) return;
2493     if (!nodepath->selected) return;
2495     /** \todo fixme: do it the right way */
2496     while (nodepath->selected) {
2497        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2498         sp_nodepath_node_destroy(node);
2499     }
2502     //clean up the nodepath (such as for trivial subpaths)
2503     sp_nodepath_cleanup(nodepath);
2505     sp_nodepath_update_handles(nodepath);
2507     // if the entire nodepath is removed, delete the selected object.
2508     if (nodepath->subpaths == NULL ||
2509         sp_nodepath_get_node_count(nodepath) < 2) {
2510         SPDocument *document = sp_desktop_document (nodepath->desktop);
2511         sp_selection_delete(nodepath->desktop);
2512         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2513                           _("Delete nodes"));
2514         return;
2515     }
2517     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2519     sp_nodepath_update_statusbar(nodepath);
2522 /**
2523  * Delete one or more segments between two selected nodes.
2524  * This is the code for 'split'.
2525  */
2526 void
2527 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2529    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2530    Inkscape::NodePath::Node *curr, *next;     //Iterators
2532     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2534     if (g_list_length(nodepath->selected) != 2) {
2535         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2536                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2537         return;
2538     }
2540     //Selected nodes, not inclusive
2541    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2542    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2544     if ( ( a==b)                       ||  //same node
2545          (a->subpath  != b->subpath )  ||  //not the same path
2546          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2547          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2548     {
2549         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2550                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2551         return;
2552     }
2554     //###########################################
2555     //# BEGIN EDITS
2556     //###########################################
2557     //##################################
2558     //# CLOSED PATH
2559     //##################################
2560     if (a->subpath->closed) {
2563         gboolean reversed = FALSE;
2565         //Since we can go in a circle, we need to find the shorter distance.
2566         //  a->b or b->a
2567         start = end = NULL;
2568         int distance    = 0;
2569         int minDistance = 0;
2570         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2571             if (curr==b) {
2572                 //printf("a to b:%d\n", distance);
2573                 start = a;//go from a to b
2574                 end   = b;
2575                 minDistance = distance;
2576                 //printf("A to B :\n");
2577                 break;
2578             }
2579             distance++;
2580         }
2582         //try again, the other direction
2583         distance = 0;
2584         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2585             if (curr==a) {
2586                 //printf("b to a:%d\n", distance);
2587                 if (distance < minDistance) {
2588                     start    = b;  //we go from b to a
2589                     end      = a;
2590                     reversed = TRUE;
2591                     //printf("B to A\n");
2592                 }
2593                 break;
2594             }
2595             distance++;
2596         }
2599         //Copy everything from 'end' to 'start' to a new subpath
2600        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2601         for (curr=end ; curr ; curr=curr->n.other) {
2602             NRPathcode code = (NRPathcode) curr->code;
2603             if (curr == end)
2604                 code = NR_MOVETO;
2605             sp_nodepath_node_new(t, NULL,
2606                                  (Inkscape::NodePath::NodeType)curr->type, code,
2607                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2608             if (curr == start)
2609                 break;
2610         }
2611         sp_nodepath_subpath_destroy(a->subpath);
2614     }
2618     //##################################
2619     //# OPEN PATH
2620     //##################################
2621     else {
2623         //We need to get the direction of the list between A and B
2624         //Can we walk from a to b?
2625         start = end = NULL;
2626         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2627             if (curr==b) {
2628                 start = a;  //did it!  we go from a to b
2629                 end   = b;
2630                 //printf("A to B\n");
2631                 break;
2632             }
2633         }
2634         if (!start) {//didn't work?  let's try the other direction
2635             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2636                 if (curr==a) {
2637                     start = b;  //did it!  we go from b to a
2638                     end   = a;
2639                     //printf("B to A\n");
2640                     break;
2641                 }
2642             }
2643         }
2644         if (!start) {
2645             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2646                                                      _("Cannot find path between nodes."));
2647             return;
2648         }
2652         //Copy everything after 'end' to a new subpath
2653        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2654         for (curr=end ; curr ; curr=curr->n.other) {
2655             NRPathcode code = (NRPathcode) curr->code;
2656             if (curr == end)
2657                 code = NR_MOVETO;
2658             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2659                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2660         }
2662         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2663         for (curr = start->n.other ; curr  ; curr=next) {
2664             next = curr->n.other;
2665             sp_nodepath_node_destroy(curr);
2666         }
2668     }
2669     //###########################################
2670     //# END EDITS
2671     //###########################################
2673     //clean up the nodepath (such as for trivial subpaths)
2674     sp_nodepath_cleanup(nodepath);
2676     sp_nodepath_update_handles(nodepath);
2678     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2680     sp_nodepath_update_statusbar(nodepath);
2683 /**
2684  * Call sp_nodepath_set_line() for all selected segments.
2685  */
2686 void
2687 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2689     if (nodepath == NULL) return;
2691     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2692        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2693         g_assert(n->selected);
2694         if (n->p.other && n->p.other->selected) {
2695             sp_nodepath_set_line_type(n, code);
2696         }
2697     }
2699     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2702 /**
2703  * Call sp_nodepath_convert_node_type() for all selected nodes.
2704  */
2705 void
2706 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2708     if (nodepath == NULL) return;
2710     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2712     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2713         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2714     }
2716     sp_nodepath_update_repr(nodepath, _("Change node type"));
2719 /**
2720  * Change select status of node, update its own and neighbour handles.
2721  */
2722 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2724     node->selected = selected;
2726     if (selected) {
2727         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2728         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2729         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2730         sp_knot_update_ctrl(node->knot);
2731     } else {
2732         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2733         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2734         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2735         sp_knot_update_ctrl(node->knot);
2736     }
2738     sp_node_update_handles(node);
2739     if (node->n.other) sp_node_update_handles(node->n.other);
2740     if (node->p.other) sp_node_update_handles(node->p.other);
2743 /**
2744 \brief Select a node
2745 \param node     The node to select
2746 \param incremental   If true, add to selection, otherwise deselect others
2747 \param override   If true, always select this node, otherwise toggle selected status
2748 */
2749 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2751     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2753     if (incremental) {
2754         if (override) {
2755             if (!g_list_find(nodepath->selected, node)) {
2756                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2757             }
2758             sp_node_set_selected(node, TRUE);
2759         } else { // toggle
2760             if (node->selected) {
2761                 g_assert(g_list_find(nodepath->selected, node));
2762                 nodepath->selected = g_list_remove(nodepath->selected, node);
2763             } else {
2764                 g_assert(!g_list_find(nodepath->selected, node));
2765                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2766             }
2767             sp_node_set_selected(node, !node->selected);
2768         }
2769     } else {
2770         sp_nodepath_deselect(nodepath);
2771         nodepath->selected = g_list_prepend(nodepath->selected, node);
2772         sp_node_set_selected(node, TRUE);
2773     }
2775     sp_nodepath_update_statusbar(nodepath);
2779 /**
2780 \brief Deselect all nodes in the nodepath
2781 */
2782 void
2783 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2785     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2787     while (nodepath->selected) {
2788         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2789         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2790     }
2791     sp_nodepath_update_statusbar(nodepath);
2794 /**
2795 \brief Select or invert selection of all nodes in the nodepath
2796 */
2797 void
2798 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2800     if (!nodepath) return;
2802     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2803        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2804         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2805            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2806            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2807         }
2808     }
2811 /**
2812  * If nothing selected, does the same as sp_nodepath_select_all();
2813  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2814  * (i.e., similar to "select all in layer", with the "selected" subpaths
2815  * being treated as "layers" in the path).
2816  */
2817 void
2818 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2820     if (!nodepath) return;
2822     if (g_list_length (nodepath->selected) == 0) {
2823         sp_nodepath_select_all (nodepath, invert);
2824         return;
2825     }
2827     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2828     GSList *subpaths = NULL;
2830     for (GList *l = copy; l != NULL; l = l->next) {
2831         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2832         Inkscape::NodePath::SubPath *subpath = n->subpath;
2833         if (!g_slist_find (subpaths, subpath))
2834             subpaths = g_slist_prepend (subpaths, subpath);
2835     }
2837     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2838         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2839         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2840             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2841             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2842         }
2843     }
2845     g_slist_free (subpaths);
2846     g_list_free (copy);
2849 /**
2850  * \brief Select the node after the last selected; if none is selected,
2851  * select the first within path.
2852  */
2853 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2855     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2857    Inkscape::NodePath::Node *last = NULL;
2858     if (nodepath->selected) {
2859         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2860            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2861             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2862             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2863                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2864                 if (node->selected) {
2865                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2866                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2867                             if (spl->next) { // there's a next subpath
2868                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2869                                 last = subpath_next->first;
2870                             } else if (spl->prev) { // there's a previous subpath
2871                                 last = NULL; // to be set later to the first node of first subpath
2872                             } else {
2873                                 last = node->n.other;
2874                             }
2875                         } else {
2876                             last = node->n.other;
2877                         }
2878                     } else {
2879                         if (node->n.other) {
2880                             last = node->n.other;
2881                         } else {
2882                             if (spl->next) { // there's a next subpath
2883                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2884                                 last = subpath_next->first;
2885                             } else if (spl->prev) { // there's a previous subpath
2886                                 last = NULL; // to be set later to the first node of first subpath
2887                             } else {
2888                                 last = (Inkscape::NodePath::Node *) subpath->first;
2889                             }
2890                         }
2891                     }
2892                 }
2893             }
2894         }
2895         sp_nodepath_deselect(nodepath);
2896     }
2898     if (last) { // there's at least one more node after selected
2899         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2900     } else { // no more nodes, select the first one in first subpath
2901        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2902         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2903     }
2906 /**
2907  * \brief Select the node before the first selected; if none is selected,
2908  * select the last within path
2909  */
2910 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2912     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2914    Inkscape::NodePath::Node *last = NULL;
2915     if (nodepath->selected) {
2916         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2917            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2918             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2919                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2920                 if (node->selected) {
2921                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2922                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2923                             if (spl->prev) { // there's a prev subpath
2924                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2925                                 last = subpath_prev->last;
2926                             } else if (spl->next) { // there's a next subpath
2927                                 last = NULL; // to be set later to the last node of last subpath
2928                             } else {
2929                                 last = node->p.other;
2930                             }
2931                         } else {
2932                             last = node->p.other;
2933                         }
2934                     } else {
2935                         if (node->p.other) {
2936                             last = node->p.other;
2937                         } else {
2938                             if (spl->prev) { // there's a prev subpath
2939                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2940                                 last = subpath_prev->last;
2941                             } else if (spl->next) { // there's a next subpath
2942                                 last = NULL; // to be set later to the last node of last subpath
2943                             } else {
2944                                 last = (Inkscape::NodePath::Node *) subpath->last;
2945                             }
2946                         }
2947                     }
2948                 }
2949             }
2950         }
2951         sp_nodepath_deselect(nodepath);
2952     }
2954     if (last) { // there's at least one more node before selected
2955         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2956     } else { // no more nodes, select the last one in last subpath
2957         GList *spl = g_list_last(nodepath->subpaths);
2958        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2959         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2960     }
2963 /**
2964  * \brief Select all nodes that are within the rectangle.
2965  */
2966 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2968     if (!incremental) {
2969         sp_nodepath_deselect(nodepath);
2970     }
2972     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2973        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2974         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2975            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2977             if (b.contains(node->pos)) {
2978                 sp_nodepath_node_select(node, TRUE, TRUE);
2979             }
2980         }
2981     }
2985 void
2986 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2988     g_assert (n);
2989     g_assert (nodepath);
2990     g_assert (n->subpath->nodepath == nodepath);
2992     if (g_list_length (nodepath->selected) == 0) {
2993         if (grow > 0) {
2994             sp_nodepath_node_select(n, TRUE, TRUE);
2995         }
2996         return;
2997     }
2999     if (g_list_length (nodepath->selected) == 1) {
3000         if (grow < 0) {
3001             sp_nodepath_deselect (nodepath);
3002             return;
3003         }
3004     }
3006         double n_sel_range = 0, p_sel_range = 0;
3007             Inkscape::NodePath::Node *farthest_n_node = n;
3008             Inkscape::NodePath::Node *farthest_p_node = n;
3010         // Calculate ranges
3011         {
3012             double n_range = 0, p_range = 0;
3013             bool n_going = true, p_going = true;
3014             Inkscape::NodePath::Node *n_node = n;
3015             Inkscape::NodePath::Node *p_node = n;
3016             do {
3017                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3018                 if (n_node && n_going)
3019                     n_node = n_node->n.other;
3020                 if (n_node == NULL) {
3021                     n_going = false;
3022                 } else {
3023                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3024                     if (n_node->selected) {
3025                         n_sel_range = n_range;
3026                         farthest_n_node = n_node;
3027                     }
3028                     if (n_node == p_node) {
3029                         n_going = false;
3030                         p_going = false;
3031                     }
3032                 }
3033                 if (p_node && p_going)
3034                     p_node = p_node->p.other;
3035                 if (p_node == NULL) {
3036                     p_going = false;
3037                 } else {
3038                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3039                     if (p_node->selected) {
3040                         p_sel_range = p_range;
3041                         farthest_p_node = p_node;
3042                     }
3043                     if (p_node == n_node) {
3044                         n_going = false;
3045                         p_going = false;
3046                     }
3047                 }
3048             } while (n_going || p_going);
3049         }
3051     if (grow > 0) {
3052         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3053                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3054         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3055                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3056         }
3057     } else {
3058         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3059                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3060         } else if (farthest_p_node && farthest_p_node->selected) {
3061                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3062         }
3063     }
3066 void
3067 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3069     g_assert (n);
3070     g_assert (nodepath);
3071     g_assert (n->subpath->nodepath == nodepath);
3073     if (g_list_length (nodepath->selected) == 0) {
3074         if (grow > 0) {
3075             sp_nodepath_node_select(n, TRUE, TRUE);
3076         }
3077         return;
3078     }
3080     if (g_list_length (nodepath->selected) == 1) {
3081         if (grow < 0) {
3082             sp_nodepath_deselect (nodepath);
3083             return;
3084         }
3085     }
3087     Inkscape::NodePath::Node *farthest_selected = NULL;
3088     double farthest_dist = 0;
3090     Inkscape::NodePath::Node *closest_unselected = NULL;
3091     double closest_dist = NR_HUGE;
3093     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3094        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3095         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3096            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3097            if (node == n)
3098                continue;
3099            if (node->selected) {
3100                if (NR::L2(node->pos - n->pos) > farthest_dist) {
3101                    farthest_dist = NR::L2(node->pos - n->pos);
3102                    farthest_selected = node;
3103                }
3104            } else {
3105                if (NR::L2(node->pos - n->pos) < closest_dist) {
3106                    closest_dist = NR::L2(node->pos - n->pos);
3107                    closest_unselected = node;
3108                }
3109            }
3110         }
3111     }
3113     if (grow > 0) {
3114         if (closest_unselected) {
3115             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3116         }
3117     } else {
3118         if (farthest_selected) {
3119             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3120         }
3121     }
3125 /**
3126 \brief  Saves all nodes' and handles' current positions in their origin members
3127 */
3128 void
3129 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3131     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3132        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3133         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3134            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3135            n->origin = n->pos;
3136            n->p.origin = n->p.pos;
3137            n->n.origin = n->n.pos;
3138         }
3139     }
3142 /**
3143 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3144 */
3145 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3147     if (!nodepath->selected) {
3148         return NULL;
3149     }
3151     GList *r = NULL;
3152     guint i = 0;
3153     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3154        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3155         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3156            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3157             i++;
3158             if (node->selected) {
3159                 r = g_list_append(r, GINT_TO_POINTER(i));
3160             }
3161         }
3162     }
3163     return r;
3166 /**
3167 \brief  Restores selection by selecting nodes whose positions are in the list
3168 */
3169 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3171     sp_nodepath_deselect(nodepath);
3173     guint i = 0;
3174     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3175        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3176         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3177            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3178             i++;
3179             if (g_list_find(r, GINT_TO_POINTER(i))) {
3180                 sp_nodepath_node_select(node, TRUE, TRUE);
3181             }
3182         }
3183     }
3187 /**
3188 \brief Adjusts handle according to node type and line code.
3189 */
3190 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3192     g_assert(node);
3194    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3195    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3197    // nothing to do if we are an end node
3198     if (me->other == NULL) return;
3199     if (other->other == NULL) return;
3201     // nothing to do if we are a cusp node
3202     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3204     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3205     NRPathcode mecode;
3206     if (which_adjust == 1) {
3207         mecode = (NRPathcode)me->other->code;
3208     } else {
3209         mecode = (NRPathcode)node->code;
3210     }
3211     if (mecode == NR_LINETO) return;
3213     if (sp_node_side_is_line(node, other)) {
3214         // other is a line, and we are either smooth or symm
3215        Inkscape::NodePath::Node *othernode = other->other;
3216         double len = NR::L2(me->pos - node->pos);
3217         NR::Point delta = node->pos - othernode->pos;
3218         double linelen = NR::L2(delta);
3219         if (linelen < 1e-18)
3220             return;
3221         me->pos = node->pos + (len / linelen)*delta;
3222         return;
3223     }
3225     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3226         // symmetrize 
3227         me->pos = 2 * node->pos - other->pos;
3228         return;
3229     } else {
3230         // smoothify
3231         double len = NR::L2(me->pos - node->pos);
3232         NR::Point delta = other->pos - node->pos;
3233         double otherlen = NR::L2(delta);
3234         if (otherlen < 1e-18) return;
3235         me->pos = node->pos - (len / otherlen) * delta;
3236     }
3239 /**
3240  \brief Adjusts both handles according to node type and line code
3241  */
3242 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3244     g_assert(node);
3246     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3248     /* we are either smooth or symm */
3250     if (node->p.other == NULL) return;
3251     if (node->n.other == NULL) return;
3253     if (sp_node_side_is_line(node, &node->p)) {
3254         sp_node_adjust_handle(node, 1);
3255         return;
3256     }
3258     if (sp_node_side_is_line(node, &node->n)) {
3259         sp_node_adjust_handle(node, -1);
3260         return;
3261     }
3263     /* both are curves */
3264     NR::Point const delta( node->n.pos - node->p.pos );
3266     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3267         node->p.pos = node->pos - delta / 2;
3268         node->n.pos = node->pos + delta / 2;
3269         return;
3270     }
3272     /* We are smooth */
3273     double plen = NR::L2(node->p.pos - node->pos);
3274     if (plen < 1e-18) return;
3275     double nlen = NR::L2(node->n.pos - node->pos);
3276     if (nlen < 1e-18) return;
3277     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3278     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3281 /**
3282  * Node event callback.
3283  */
3284 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3286     gboolean ret = FALSE;
3287     switch (event->type) {
3288         case GDK_ENTER_NOTIFY:
3289             Inkscape::NodePath::Path::active_node = n;
3290             break;
3291         case GDK_LEAVE_NOTIFY:
3292             Inkscape::NodePath::Path::active_node = NULL;
3293             break;
3294         case GDK_SCROLL:
3295             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3296                 switch (event->scroll.direction) {
3297                     case GDK_SCROLL_UP:
3298                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3299                         break;
3300                     case GDK_SCROLL_DOWN:
3301                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3302                         break;
3303                     default:
3304                         break;
3305                 }
3306                 ret = TRUE;
3307             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3308                 switch (event->scroll.direction) {
3309                     case GDK_SCROLL_UP:
3310                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3311                         break;
3312                     case GDK_SCROLL_DOWN:
3313                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3314                         break;
3315                     default:
3316                         break;
3317                 }
3318                 ret = TRUE;
3319             }
3320             break;
3321         case GDK_KEY_PRESS:
3322             switch (get_group0_keyval (&event->key)) {
3323                 case GDK_space:
3324                     if (event->key.state & GDK_BUTTON1_MASK) {
3325                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3326                         stamp_repr(nodepath);
3327                         ret = TRUE;
3328                     }
3329                     break;
3330                 case GDK_Page_Up:
3331                     if (event->key.state & GDK_CONTROL_MASK) {
3332                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3333                     } else {
3334                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3335                     }
3336                     break;
3337                 case GDK_Page_Down:
3338                     if (event->key.state & GDK_CONTROL_MASK) {
3339                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3340                     } else {
3341                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3342                     }
3343                     break;
3344                 default:
3345                     break;
3346             }
3347             break;
3348         default:
3349             break;
3350     }
3352     return ret;
3355 /**
3356  * Handle keypress on node; directly called.
3357  */
3358 gboolean node_key(GdkEvent *event)
3360     Inkscape::NodePath::Path *np;
3362     // there is no way to verify nodes so set active_node to nil when deleting!!
3363     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3365     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3366         gint ret = FALSE;
3367         switch (get_group0_keyval (&event->key)) {
3368             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3369             case GDK_BackSpace:
3370                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3371                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3372                 sp_nodepath_update_repr(np, _("Delete node"));
3373                 Inkscape::NodePath::Path::active_node = NULL;
3374                 ret = TRUE;
3375                 break;
3376             case GDK_c:
3377                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3378                 ret = TRUE;
3379                 break;
3380             case GDK_s:
3381                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3382                 ret = TRUE;
3383                 break;
3384             case GDK_y:
3385                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3386                 ret = TRUE;
3387                 break;
3388             case GDK_b:
3389                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3390                 ret = TRUE;
3391                 break;
3392         }
3393         return ret;
3394     }
3395     return FALSE;
3398 /**
3399  * Mouseclick on node callback.
3400  */
3401 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3403    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3405     if (state & GDK_CONTROL_MASK) {
3406         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3408         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3409             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3410                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3411             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3412                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3413             } else {
3414                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3415             }
3416             sp_nodepath_update_repr(nodepath, _("Change node type"));
3417             sp_nodepath_update_statusbar(nodepath);
3419         } else { //ctrl+alt+click: delete node
3420             GList *node_to_delete = NULL;
3421             node_to_delete = g_list_append(node_to_delete, n);
3422             sp_node_delete_preserve(node_to_delete);
3423         }
3425     } else {
3426         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3427     }
3430 /**
3431  * Mouse grabbed node callback.
3432  */
3433 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3435    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3437     if (!n->selected) {
3438         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3439     }
3441     n->is_dragging = true;
3442     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3444     sp_nodepath_remember_origins (n->subpath->nodepath);
3447 /**
3448  * Mouse ungrabbed node callback.
3449  */
3450 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3452    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3454    n->dragging_out = NULL;
3455    n->is_dragging = false;
3456    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3458    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3461 /**
3462  * The point on a line, given by its angle, closest to the given point.
3463  * \param p  A point.
3464  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3465  * \param closest  Pointer to the point struct where the result is stored.
3466  * \todo FIXME: use dot product perhaps?
3467  */
3468 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3470     if (a == HUGE_VAL) { // vertical
3471         *closest = NR::Point(0, (*p)[NR::Y]);
3472     } else {
3473         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3474         (*closest)[NR::Y] = a * (*closest)[NR::X];
3475     }
3478 /**
3479  * Distance from the point to a line given by its angle.
3480  * \param p  A point.
3481  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3482  */
3483 static double point_line_distance(NR::Point *p, double a)
3485     NR::Point c;
3486     point_line_closest(p, a, &c);
3487     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]));
3490 /**
3491  * Callback for node "request" signal.
3492  * \todo fixme: This goes to "moved" event? (lauris)
3493  */
3494 static gboolean
3495 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3497     double yn, xn, yp, xp;
3498     double an, ap, na, pa;
3499     double d_an, d_ap, d_na, d_pa;
3500     gboolean collinear = FALSE;
3501     NR::Point c;
3502     NR::Point pr;
3504     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3506     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3508     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3509     if ( (!n->subpath->nodepath->straight_path) &&
3510          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3511            || n->dragging_out ) )
3512     {
3513        NR::Point mouse = (*p);
3515        if (!n->dragging_out) {
3516            // This is the first drag-out event; find out which handle to drag out
3517            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3518            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3520            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3521                return FALSE;
3523            Inkscape::NodePath::NodeSide *opposite;
3524            if (appr_p > appr_n) { // closer to p
3525                n->dragging_out = &n->p;
3526                opposite = &n->n;
3527                n->code = NR_CURVETO;
3528            } else if (appr_p < appr_n) { // closer to n
3529                n->dragging_out = &n->n;
3530                opposite = &n->p;
3531                n->n.other->code = NR_CURVETO;
3532            } else { // p and n nodes are the same
3533                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3534                    n->dragging_out = &n->p;
3535                    opposite = &n->n;
3536                    n->code = NR_CURVETO;
3537                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3538                    n->dragging_out = &n->n;
3539                    opposite = &n->p;
3540                    n->n.other->code = NR_CURVETO;
3541                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3542                    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);
3543                    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);
3544                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3545                        n->dragging_out = &n->n;
3546                        opposite = &n->p;
3547                        n->n.other->code = NR_CURVETO;
3548                    } else { // closer to other's n handle
3549                        n->dragging_out = &n->p;
3550                        opposite = &n->n;
3551                        n->code = NR_CURVETO;
3552                    }
3553                }
3554            }
3556            // if there's another handle, make sure the one we drag out starts parallel to it
3557            if (opposite->pos != n->pos) {
3558                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3559            }
3561            // knots might not be created yet!
3562            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3563            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3564        }
3566        // pass this on to the handle-moved callback
3567        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3568        sp_node_update_handles(n);
3569        return TRUE;
3570    }
3572     if (state & GDK_CONTROL_MASK) { // constrained motion
3574         // calculate relative distances of handles
3575         // n handle:
3576         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3577         xn = n->n.pos[NR::X] - n->pos[NR::X];
3578         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3579         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3580             if (n->n.other) { // if there is the next point
3581                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3582                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3583                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3584             }
3585         }
3586         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3587         if (yn < 0) { xn = -xn; yn = -yn; }
3589         // p handle:
3590         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3591         xp = n->p.pos[NR::X] - n->pos[NR::X];
3592         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3593         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3594             if (n->p.other) {
3595                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3596                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3597                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3598             }
3599         }
3600         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3601         if (yp < 0) { xp = -xp; yp = -yp; }
3603         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3604             // sliding on handles, only if at least one of the handles is non-vertical
3605             // (otherwise it's the same as ctrl+drag anyway)
3607             // calculate angles of the handles
3608             if (xn == 0) {
3609                 if (yn == 0) { // no handle, consider it the continuation of the other one
3610                     an = 0;
3611                     collinear = TRUE;
3612                 }
3613                 else an = 0; // vertical; set the angle to horizontal
3614             } else an = yn/xn;
3616             if (xp == 0) {
3617                 if (yp == 0) { // no handle, consider it the continuation of the other one
3618                     ap = an;
3619                 }
3620                 else ap = 0; // vertical; set the angle to horizontal
3621             } else  ap = yp/xp;
3623             if (collinear) an = ap;
3625             // angles of the perpendiculars; HUGE_VAL means vertical
3626             if (an == 0) na = HUGE_VAL; else na = -1/an;
3627             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3629             // mouse point relative to the node's original pos
3630             pr = (*p) - n->origin;
3632             // distances to the four lines (two handles and two perpendiculars)
3633             d_an = point_line_distance(&pr, an);
3634             d_na = point_line_distance(&pr, na);
3635             d_ap = point_line_distance(&pr, ap);
3636             d_pa = point_line_distance(&pr, pa);
3638             // find out which line is the closest, save its closest point in c
3639             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3640                 point_line_closest(&pr, an, &c);
3641             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3642                 point_line_closest(&pr, ap, &c);
3643             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3644                 point_line_closest(&pr, na, &c);
3645             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3646                 point_line_closest(&pr, pa, &c);
3647             }
3649             // move the node to the closest point
3650             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3651                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3652                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y], 
3653                                             true);
3655         } else {  // constraining to hor/vert
3657             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3658                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3659                                                 (*p)[NR::X] - n->pos[NR::X], 
3660                                                 n->origin[NR::Y] - n->pos[NR::Y],
3661                                                 true, 
3662                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::X]));
3663             } else { // snap to vert
3664                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3665                                                 n->origin[NR::X] - n->pos[NR::X],
3666                                                 (*p)[NR::Y] - n->pos[NR::Y],
3667                                                 true,
3668                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::Y]));
3669             }
3670         }
3671     } else { // move freely
3672         if (n->is_dragging) {
3673             if (state & GDK_MOD1_MASK) { // sculpt
3674                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3675             } else {
3676                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3677                                             (*p)[NR::X] - n->pos[NR::X],
3678                                             (*p)[NR::Y] - n->pos[NR::Y],
3679                                             (state & GDK_SHIFT_MASK) == 0);
3680             }
3681         }
3682     }
3684     n->subpath->nodepath->desktop->scroll_to_point(p);
3686     return TRUE;
3689 /**
3690  * Node handle clicked callback.
3691  */
3692 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3694    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3696     if (state & GDK_CONTROL_MASK) { // "delete" handle
3697         if (n->p.knot == knot) {
3698             n->p.pos = n->pos;
3699         } else if (n->n.knot == knot) {
3700             n->n.pos = n->pos;
3701         }
3702         sp_node_update_handles(n);
3703         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3704         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3705         sp_nodepath_update_statusbar(nodepath);
3707     } else { // just select or add to selection, depending in Shift
3708         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3709     }
3712 /**
3713  * Node handle grabbed callback.
3714  */
3715 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3717    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3719     if (!n->selected) {
3720         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3721     }
3723     // remember the origin point of the handle
3724     if (n->p.knot == knot) {
3725         n->p.origin_radial = n->p.pos - n->pos;
3726     } else if (n->n.knot == knot) {
3727         n->n.origin_radial = n->n.pos - n->pos;
3728     } else {
3729         g_assert_not_reached();
3730     }
3732     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3735 /**
3736  * Node handle ungrabbed callback.
3737  */
3738 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3740    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3742     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3743     if (n->p.knot == knot) {
3744         n->p.origin_radial.a = 0;
3745         sp_knot_set_position(knot, n->p.pos, state);
3746     } else if (n->n.knot == knot) {
3747         n->n.origin_radial.a = 0;
3748         sp_knot_set_position(knot, n->n.pos, state);
3749     } else {
3750         g_assert_not_reached();
3751     }
3753     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3756 /**
3757  * Node handle "request" signal callback.
3758  */
3759 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3761     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3763     Inkscape::NodePath::NodeSide *me, *opposite;
3764     gint which;
3765     if (n->p.knot == knot) {
3766         me = &n->p;
3767         opposite = &n->n;
3768         which = -1;
3769     } else if (n->n.knot == knot) {
3770         me = &n->n;
3771         opposite = &n->p;
3772         which = 1;
3773     } else {
3774         me = opposite = NULL;
3775         which = 0;
3776         g_assert_not_reached();
3777     }
3779     SPDesktop *desktop = n->subpath->nodepath->desktop;
3780     SnapManager &m = desktop->namedview->snap_manager;
3781     m.setup(desktop, n->subpath->nodepath->item);
3782     Inkscape::SnappedPoint s;
3783     
3784     if ((state & GDK_SHIFT_MASK) != 0) {
3785         // We will not try to snap when the shift-key is pressed
3786         // so remove the old snap indicator and don't wait for it to time-out  
3787         desktop->snapindicator->remove_snappoint();     
3788     }
3790     Inkscape::NodePath::Node *othernode = opposite->other;
3791     if (othernode) {
3792         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3793             /* We are smooth node adjacent with line */
3794             NR::Point const delta = *p - n->pos;
3795             NR::Coord const len = NR::L2(delta);
3796             Inkscape::NodePath::Node *othernode = opposite->other;
3797             NR::Point const ndelta = n->pos - othernode->pos;
3798             NR::Coord const linelen = NR::L2(ndelta);
3799             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3800                 NR::Coord const scal = dot(delta, ndelta) / linelen;
3801                 (*p) = n->pos + (scal / linelen) * ndelta;
3802             }
3803             if ((state & GDK_SHIFT_MASK) == 0) {
3804                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(*p), Inkscape::Snapper::ConstraintLine(*p, ndelta));
3805             }
3806         } else {
3807                 if ((state & GDK_SHIFT_MASK) == 0) {
3808                         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(*p));
3809                 }
3810         }
3811     } else {
3812         if ((state & GDK_SHIFT_MASK) == 0) {
3813                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(*p));
3814         }
3815     }
3816     
3817     Geom::Point pt2g = *p;
3818     s.getPoint(pt2g);
3819     *p = pt2g;
3820     
3821     sp_node_adjust_handle(n, -which);
3823     return FALSE;
3826 /**
3827  * Node handle moved callback.
3828  */
3829 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3831    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3833    Inkscape::NodePath::NodeSide *me;
3834    Inkscape::NodePath::NodeSide *other;
3835     if (n->p.knot == knot) {
3836         me = &n->p;
3837         other = &n->n;
3838     } else if (n->n.knot == knot) {
3839         me = &n->n;
3840         other = &n->p;
3841     } else {
3842         me = NULL;
3843         other = NULL;
3844         g_assert_not_reached();
3845     }
3847     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3848     Radial rme(me->pos - n->pos);
3849     Radial rother(other->pos - n->pos);
3850     Radial rnew(*p - n->pos);
3852     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3853         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3854         /* 0 interpreted as "no snapping". */
3856         // 1. Snap to the closest PI/snaps angle, starting from zero.
3857         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3859         // 2. Snap to the original angle, its opposite and perpendiculars
3860         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3861             /* The closest PI/2 angle, starting from original angle */
3862             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3864             // Snap to the closest.
3865             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3866                        ? a_snapped
3867                        : a_ortho );
3868         }
3870         // 3. Snap to the angle of the opposite line, if any
3871         Inkscape::NodePath::Node *othernode = other->other;
3872         if (othernode) {
3873             NR::Point other_to_snap(0,0);
3874             if (sp_node_side_is_line(n, other)) {
3875                 other_to_snap = othernode->pos - n->pos;
3876             } else {
3877                 other_to_snap = other->pos - n->pos;
3878             }
3879             if (NR::L2(other_to_snap) > 1e-3) {
3880                 Radial rother_to_snap(other_to_snap);
3881                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
3882                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
3884                 // Snap to the closest.
3885                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
3886                        ? a_snapped
3887                        : a_oppo );
3888             }
3889         }
3891         rnew.a = a_snapped;
3892     }
3894     if (state & GDK_MOD1_MASK) {
3895         // lock handle length
3896         rnew.r = me->origin_radial.r;
3897     }
3899     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3900         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3901         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3902         rother.a += rnew.a - rme.a;
3903         other->pos = NR::Point(rother) + n->pos;
3904         if (other->knot) {
3905             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3906             sp_knot_moveto(other->knot, other->pos);
3907         }
3908     }
3910     me->pos = NR::Point(rnew) + n->pos;
3911     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3913     // move knot, but without emitting the signal:
3914     // we cannot emit a "moved" signal because we're now processing it
3915     sp_knot_moveto(me->knot, me->pos);
3917     update_object(n->subpath->nodepath);
3919     /* status text */
3920     SPDesktop *desktop = n->subpath->nodepath->desktop;
3921     if (!desktop) return;
3922     SPEventContext *ec = desktop->event_context;
3923     if (!ec) return;
3925     // FIXME: this is an ad-hoc crash fix but we need to find a better way (which also works in LPEToolContext)
3926     //Inkscape::MessageContext *mc = get_message_context(ec);
3927     if (!SP_IS_NODE_CONTEXT(ec)) {
3928         return;
3929     }
3930     Inkscape::MessageContext *mc = SP_NODE_CONTEXT(ec)->_node_message_context;
3932     if (!mc) return;
3934     double degrees = 180 / M_PI * rnew.a;
3935     if (degrees > 180) degrees -= 360;
3936     if (degrees < -180) degrees += 360;
3937     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3938         degrees = angle_to_compass (degrees);
3940     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3942     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
3943          _("<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);
3945     g_string_free(length, TRUE);
3948 /**
3949  * Node handle event callback.
3950  */
3951 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3953     gboolean ret = FALSE;
3954     switch (event->type) {
3955         case GDK_KEY_PRESS:
3956             switch (get_group0_keyval (&event->key)) {
3957                 case GDK_space:
3958                     if (event->key.state & GDK_BUTTON1_MASK) {
3959                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3960                         stamp_repr(nodepath);
3961                         ret = TRUE;
3962                     }
3963                     break;
3964                 default:
3965                     break;
3966             }
3967             break;
3968         case GDK_ENTER_NOTIFY:
3969             // we use an experimentally determined threshold that seems to work fine
3970             if (NR::L2(n->pos - knot->pos) < 0.75)
3971                 Inkscape::NodePath::Path::active_node = n;
3972             break;
3973         case GDK_LEAVE_NOTIFY:
3974             // we use an experimentally determined threshold that seems to work fine
3975             if (NR::L2(n->pos - knot->pos) < 0.75)
3976                 Inkscape::NodePath::Path::active_node = NULL;
3977             break;
3978         default:
3979             break;
3980     }
3982     return ret;
3985 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3986                                  Radial &rme, Radial &rother, gboolean const both)
3988     rme.a += angle;
3989     if ( both
3990          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3991          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3992     {
3993         rother.a += angle;
3994     }
3997 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3998                                         Radial &rme, Radial &rother, gboolean const both)
4000     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
4002     gdouble r;
4003     if ( both
4004          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4005          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4006     {
4007         r = MAX(rme.r, rother.r);
4008     } else {
4009         r = rme.r;
4010     }
4012     gdouble const weird_angle = atan2(norm_angle, r);
4013 /* Bulia says norm_angle is just the visible distance that the
4014  * object's end must travel on the screen.  Left as 'angle' for want of
4015  * a better name.*/
4017     rme.a += weird_angle;
4018     if ( both
4019          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4020          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4021     {
4022         rother.a += weird_angle;
4023     }
4026 /**
4027  * Rotate one node.
4028  */
4029 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4031     Inkscape::NodePath::NodeSide *me, *other;
4032     bool both = false;
4034     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4035     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4037     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4038         me = &(n->p);
4039         other = &(n->n);
4040     } else if (!n->p.other) {
4041         me = &(n->n);
4042         other = &(n->p);
4043     } else {
4044         if (which > 0) { // right handle
4045             if (xn > xp) {
4046                 me = &(n->n);
4047                 other = &(n->p);
4048             } else {
4049                 me = &(n->p);
4050                 other = &(n->n);
4051             }
4052         } else if (which < 0){ // left handle
4053             if (xn <= xp) {
4054                 me = &(n->n);
4055                 other = &(n->p);
4056             } else {
4057                 me = &(n->p);
4058                 other = &(n->n);
4059             }
4060         } else { // both handles
4061             me = &(n->n);
4062             other = &(n->p);
4063             both = true;
4064         }
4065     }
4067     Radial rme(me->pos - n->pos);
4068     Radial rother(other->pos - n->pos);
4070     if (screen) {
4071         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4072     } else {
4073         node_rotate_one_internal (*n, angle, rme, rother, both);
4074     }
4076     me->pos = n->pos + NR::Point(rme);
4078     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4079         other->pos =  n->pos + NR::Point(rother);
4080     }
4082     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4083     // so here we just move all the knots without emitting move signals, for speed
4084     sp_node_update_handles(n, false);
4087 /**
4088  * Rotate selected nodes.
4089  */
4090 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4092     if (!nodepath || !nodepath->selected) return;
4094     if (g_list_length(nodepath->selected) == 1) {
4095        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4096         node_rotate_one (n, angle, which, screen);
4097     } else {
4098        // rotate as an object:
4100         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4101         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4102         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4103             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4104             box.expandTo (n->pos); // contain all selected nodes
4105         }
4107         gdouble rot;
4108         if (screen) {
4109             gdouble const zoom = nodepath->desktop->current_zoom();
4110             gdouble const zmove = angle / zoom;
4111             gdouble const r = NR::L2(box.max() - box.midpoint());
4112             rot = atan2(zmove, r);
4113         } else {
4114             rot = angle;
4115         }
4117         NR::Point rot_center;
4118         if (Inkscape::NodePath::Path::active_node == NULL)
4119             rot_center = box.midpoint();
4120         else
4121             rot_center = Inkscape::NodePath::Path::active_node->pos;
4123         NR::Matrix t =
4124             NR::Matrix (NR::translate(-rot_center)) *
4125             NR::Matrix (NR::rotate(rot)) *
4126             NR::Matrix (NR::translate(rot_center));
4128         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4129             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4130             n->pos *= t;
4131             n->n.pos *= t;
4132             n->p.pos *= t;
4133             sp_node_update_handles(n, false);
4134         }
4135     }
4137     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4140 /**
4141  * Scale one node.
4142  */
4143 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4145     bool both = false;
4146     Inkscape::NodePath::NodeSide *me, *other;
4148     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4149     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4151     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4152         me = &(n->p);
4153         other = &(n->n);
4154         n->code = NR_CURVETO;
4155     } else if (!n->p.other) {
4156         me = &(n->n);
4157         other = &(n->p);
4158         if (n->n.other)
4159             n->n.other->code = NR_CURVETO;
4160     } else {
4161         if (which > 0) { // right handle
4162             if (xn > xp) {
4163                 me = &(n->n);
4164                 other = &(n->p);
4165                 if (n->n.other)
4166                     n->n.other->code = NR_CURVETO;
4167             } else {
4168                 me = &(n->p);
4169                 other = &(n->n);
4170                 n->code = NR_CURVETO;
4171             }
4172         } else if (which < 0){ // left handle
4173             if (xn <= xp) {
4174                 me = &(n->n);
4175                 other = &(n->p);
4176                 if (n->n.other)
4177                     n->n.other->code = NR_CURVETO;
4178             } else {
4179                 me = &(n->p);
4180                 other = &(n->n);
4181                 n->code = NR_CURVETO;
4182             }
4183         } else { // both handles
4184             me = &(n->n);
4185             other = &(n->p);
4186             both = true;
4187             n->code = NR_CURVETO;
4188             if (n->n.other)
4189                 n->n.other->code = NR_CURVETO;
4190         }
4191     }
4193     Radial rme(me->pos - n->pos);
4194     Radial rother(other->pos - n->pos);
4196     rme.r += grow;
4197     if (rme.r < 0) rme.r = 0;
4198     if (rme.a == HUGE_VAL) {
4199         if (me->other) { // if direction is unknown, initialize it towards the next node
4200             Radial rme_next(me->other->pos - n->pos);
4201             rme.a = rme_next.a;
4202         } else { // if there's no next, initialize to 0
4203             rme.a = 0;
4204         }
4205     }
4206     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4207         rother.r += grow;
4208         if (rother.r < 0) rother.r = 0;
4209         if (rother.a == HUGE_VAL) {
4210             rother.a = rme.a + M_PI;
4211         }
4212     }
4214     me->pos = n->pos + NR::Point(rme);
4216     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4217         other->pos = n->pos + NR::Point(rother);
4218     }
4220     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4221     // so here we just move all the knots without emitting move signals, for speed
4222     sp_node_update_handles(n, false);
4225 /**
4226  * Scale selected nodes.
4227  */
4228 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4230     if (!nodepath || !nodepath->selected) return;
4232     if (g_list_length(nodepath->selected) == 1) {
4233         // scale handles of the single selected node
4234         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4235         node_scale_one (n, grow, which);
4236     } else {
4237         // scale nodes as an "object":
4239         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4240         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4241         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4242             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4243             box.expandTo (n->pos); // contain all selected nodes
4244         }
4246         double scale = (box.maxExtent() + grow)/box.maxExtent();
4248         NR::Point scale_center;
4249         if (Inkscape::NodePath::Path::active_node == NULL)
4250             scale_center = box.midpoint();
4251         else
4252             scale_center = Inkscape::NodePath::Path::active_node->pos;
4254         NR::Matrix t =
4255             NR::Matrix (NR::translate(-scale_center)) *
4256             NR::Matrix (NR::scale(scale, scale)) *
4257             NR::Matrix (NR::translate(scale_center));
4259         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4260             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4261             n->pos *= t;
4262             n->n.pos *= t;
4263             n->p.pos *= t;
4264             sp_node_update_handles(n, false);
4265         }
4266     }
4268     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4271 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4273     if (!nodepath) return;
4274     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4277 /**
4278  * Flip selected nodes horizontally/vertically.
4279  */
4280 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, boost::optional<NR::Point> center)
4282     if (!nodepath || !nodepath->selected) return;
4284     if (g_list_length(nodepath->selected) == 1 && !center) {
4285         // flip handles of the single selected node
4286         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4287         double temp = n->p.pos[axis];
4288         n->p.pos[axis] = n->n.pos[axis];
4289         n->n.pos[axis] = temp;
4290         sp_node_update_handles(n, false);
4291     } else {
4292         // scale nodes as an "object":
4294         Geom::Rect box = sp_node_selected_bbox (nodepath);
4295         if (!center) {
4296             center = box.midpoint();
4297         }
4298         NR::Matrix t =
4299             NR::Matrix (NR::translate(- *center)) *
4300             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
4301             NR::Matrix (NR::translate(*center));
4303         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4304             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4305             n->pos *= t;
4306             n->n.pos *= t;
4307             n->p.pos *= t;
4308             sp_node_update_handles(n, false);
4309         }
4310     }
4312     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4315 Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4317     g_assert (nodepath->selected);
4319     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4320     Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4321     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4322         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4323         box.expandTo (n->pos); // contain all selected nodes
4324     }
4325     return box;
4328 //-----------------------------------------------
4329 /**
4330  * Return new subpath under given nodepath.
4331  */
4332 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4334     g_assert(nodepath);
4335     g_assert(nodepath->desktop);
4337    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4339     s->nodepath = nodepath;
4340     s->closed = FALSE;
4341     s->nodes = NULL;
4342     s->first = NULL;
4343     s->last = NULL;
4345     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4346     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4347     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4349     return s;
4352 /**
4353  * Destroy nodes in subpath, then subpath itself.
4354  */
4355 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4357     g_assert(subpath);
4358     g_assert(subpath->nodepath);
4359     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4361     while (subpath->nodes) {
4362         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4363     }
4365     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4367     g_free(subpath);
4370 /**
4371  * Link head to tail in subpath.
4372  */
4373 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4375     g_assert(!sp->closed);
4376     g_assert(sp->last != sp->first);
4377     g_assert(sp->first->code == NR_MOVETO);
4379     sp->closed = TRUE;
4381     //Link the head to the tail
4382     sp->first->p.other = sp->last;
4383     sp->last->n.other  = sp->first;
4384     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4385     sp->first          = sp->last;
4387     //Remove the extra end node
4388     sp_nodepath_node_destroy(sp->last->n.other);
4391 /**
4392  * Open closed (loopy) subpath at node.
4393  */
4394 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4396     g_assert(sp->closed);
4397     g_assert(n->subpath == sp);
4398     g_assert(sp->first == sp->last);
4400     /* We create new startpoint, current node will become last one */
4402    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4403                                                 &n->pos, &n->pos, &n->n.pos);
4406     sp->closed        = FALSE;
4408     //Unlink to make a head and tail
4409     sp->first         = new_path;
4410     sp->last          = n;
4411     n->n.other        = NULL;
4412     new_path->p.other = NULL;
4415 /**
4416  * Return new node in subpath with given properties.
4417  * \param pos Position of node.
4418  * \param ppos Handle position in previous direction
4419  * \param npos Handle position in previous direction
4420  */
4421 Inkscape::NodePath::Node *
4422 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)
4424     g_assert(sp);
4425     g_assert(sp->nodepath);
4426     g_assert(sp->nodepath->desktop);
4428     if (nodechunk == NULL)
4429         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4431     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4433     n->subpath  = sp;
4435     if (type != Inkscape::NodePath::NODE_NONE) {
4436         // use the type from sodipodi:nodetypes
4437         n->type = type;
4438     } else {
4439         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4440             // points are (almost) collinear
4441             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4442                 // endnode, or a node with a retracted handle
4443                 n->type = Inkscape::NodePath::NODE_CUSP;
4444             } else {
4445                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4446             }
4447         } else {
4448             n->type = Inkscape::NodePath::NODE_CUSP;
4449         }
4450     }
4452     n->code     = code;
4453     n->selected = FALSE;
4454     n->pos      = *pos;
4455     n->p.pos    = *ppos;
4456     n->n.pos    = *npos;
4458     n->dragging_out = NULL;
4460     Inkscape::NodePath::Node *prev;
4461     if (next) {
4462         //g_assert(g_list_find(sp->nodes, next));
4463         prev = next->p.other;
4464     } else {
4465         prev = sp->last;
4466     }
4468     if (prev)
4469         prev->n.other = n;
4470     else
4471         sp->first = n;
4473     if (next)
4474         next->p.other = n;
4475     else
4476         sp->last = n;
4478     n->p.other = prev;
4479     n->n.other = next;
4481     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"));
4482     sp_knot_set_position(n->knot, *pos, 0);
4484     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4485     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4486     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4487     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4488     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4489     sp_knot_update_ctrl(n->knot);
4491     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4492     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4493     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4494     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4495     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4496     sp_knot_show(n->knot);
4498     // We only create handle knots and lines on demand
4499     n->p.knot = NULL;
4500     n->p.line = NULL;
4501     n->n.knot = NULL;
4502     n->n.line = NULL;
4504     sp->nodes = g_list_prepend(sp->nodes, n);
4506     return n;
4509 /**
4510  * Destroy node and its knots, link neighbors in subpath.
4511  */
4512 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4514     g_assert(node);
4515     g_assert(node->subpath);
4516     g_assert(SP_IS_KNOT(node->knot));
4518    Inkscape::NodePath::SubPath *sp = node->subpath;
4520     if (node->selected) { // first, deselect
4521         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4522         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4523     }
4525     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4527     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4528     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4529     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4530     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4531     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4532     g_object_unref(G_OBJECT(node->knot));
4534     if (node->p.knot) {
4535         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4536         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4537         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4538         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4539         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4540         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4541         g_object_unref(G_OBJECT(node->p.knot));
4542         node->p.knot = NULL;
4543     }
4545     if (node->n.knot) {
4546         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4547         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4548         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4549         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4550         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4551         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4552         g_object_unref(G_OBJECT(node->n.knot));
4553         node->n.knot = NULL;
4554     }
4556     if (node->p.line)
4557         gtk_object_destroy(GTK_OBJECT(node->p.line));
4558     if (node->n.line)
4559         gtk_object_destroy(GTK_OBJECT(node->n.line));
4561     if (sp->nodes) { // there are others nodes on the subpath
4562         if (sp->closed) {
4563             if (sp->first == node) {
4564                 g_assert(sp->last == node);
4565                 sp->first = node->n.other;
4566                 sp->last = sp->first;
4567             }
4568             node->p.other->n.other = node->n.other;
4569             node->n.other->p.other = node->p.other;
4570         } else {
4571             if (sp->first == node) {
4572                 sp->first = node->n.other;
4573                 sp->first->code = NR_MOVETO;
4574             }
4575             if (sp->last == node) sp->last = node->p.other;
4576             if (node->p.other) node->p.other->n.other = node->n.other;
4577             if (node->n.other) node->n.other->p.other = node->p.other;
4578         }
4579     } else { // this was the last node on subpath
4580         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4581     }
4583     g_mem_chunk_free(nodechunk, node);
4586 /**
4587  * Returns one of the node's two sides.
4588  * \param which Indicates which side.
4589  * \return Pointer to previous node side if which==-1, next if which==1.
4590  */
4591 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4593     g_assert(node);
4595     switch (which) {
4596         case -1:
4597             return &node->p;
4598         case 1:
4599             return &node->n;
4600         default:
4601             break;
4602     }
4604     g_assert_not_reached();
4606     return NULL;
4609 /**
4610  * Return the other side of the node, given one of its sides.
4611  */
4612 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4614     g_assert(node);
4616     if (me == &node->p) return &node->n;
4617     if (me == &node->n) return &node->p;
4619     g_assert_not_reached();
4621     return NULL;
4624 /**
4625  * Return NRPathcode on the given side of the node.
4626  */
4627 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4629     g_assert(node);
4631     if (me == &node->p) {
4632         if (node->p.other) return (NRPathcode)node->code;
4633         return NR_MOVETO;
4634     }
4636     if (me == &node->n) {
4637         if (node->n.other) return (NRPathcode)node->n.other->code;
4638         return NR_MOVETO;
4639     }
4641     g_assert_not_reached();
4643     return NR_END;
4646 /**
4647  * Return node with the given index
4648  */
4649 Inkscape::NodePath::Node *
4650 sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index)
4652     Inkscape::NodePath::Node *e = NULL;
4654     if (!nodepath) {
4655         return e;
4656     }
4658     //find segment
4659     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4661         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4662         int n = g_list_length(sp->nodes);
4663         if (sp->closed) {
4664             n++;
4665         }
4667         //if the piece belongs to this subpath grab it
4668         //otherwise move onto the next subpath
4669         if (index < n) {
4670             e = sp->first;
4671             for (int i = 0; i < index; ++i) {
4672                 e = e->n.other;
4673             }
4674             break;
4675         } else {
4676             if (sp->closed) {
4677                 index -= (n+1);
4678             } else {
4679                 index -= n;
4680             }
4681         }
4682     }
4684     return e;
4687 /**
4688  * Returns plain text meaning of node type.
4689  */
4690 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4692     unsigned retracted = 0;
4693     bool endnode = false;
4695     for (int which = -1; which <= 1; which += 2) {
4696         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4697         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4698             retracted ++;
4699         if (!side->other)
4700             endnode = true;
4701     }
4703     if (retracted == 0) {
4704         if (endnode) {
4705                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4706                 return _("end node");
4707         } else {
4708             switch (node->type) {
4709                 case Inkscape::NodePath::NODE_CUSP:
4710                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4711                     return _("cusp");
4712                 case Inkscape::NodePath::NODE_SMOOTH:
4713                     // TRANSLATORS: "smooth" is an adjective here
4714                     return _("smooth");
4715                 case Inkscape::NodePath::NODE_SYMM:
4716                     return _("symmetric");
4717             }
4718         }
4719     } else if (retracted == 1) {
4720         if (endnode) {
4721             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4722             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4723         } else {
4724             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4725         }
4726     } else {
4727         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4728     }
4730     return NULL;
4733 /**
4734  * Handles content of statusbar as long as node tool is active.
4735  */
4736 void
4737 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4739     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");
4740     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4742     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4743     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4744     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4745     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4747     SPDesktop *desktop = NULL;
4748     if (nodepath) {
4749         desktop = nodepath->desktop;
4750     } else {
4751         desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above
4752     }
4754     SPEventContext *ec = desktop->event_context;
4755     if (!ec) return;
4757     // FIXME: this is an ad-hoc crash fix but we need to find a better way (which also works in LPEToolContext)
4758     //Inkscape::MessageContext *mc = get_message_context(ec);
4759     if (!SP_IS_NODE_CONTEXT(ec)) {
4760         return;
4761     }
4763     Inkscape::MessageContext *mc = SP_NODE_CONTEXT(ec)->_node_message_context;
4764     if (!mc) return;
4766     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4768     if (selected_nodes == 0) {
4769         Inkscape::Selection *sel = desktop->selection;
4770         if (!sel || sel->isEmpty()) {
4771             mc->setF(Inkscape::NORMAL_MESSAGE,
4772                      _("Select a single object to edit its nodes or handles."));
4773         } else {
4774             if (nodepath) {
4775             mc->setF(Inkscape::NORMAL_MESSAGE,
4776                      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.",
4777                               "<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.",
4778                               total_nodes),
4779                      total_nodes);
4780             } else {
4781                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4782                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4783                 } else {
4784                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4785                 }
4786             }
4787         }
4788     } else if (nodepath && selected_nodes == 1) {
4789         mc->setF(Inkscape::NORMAL_MESSAGE,
4790                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4791                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4792                           total_nodes),
4793                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4794     } else {
4795         if (selected_subpaths > 1) {
4796             mc->setF(Inkscape::NORMAL_MESSAGE,
4797                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4798                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4799                               total_nodes),
4800                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4801         } else {
4802             mc->setF(Inkscape::NORMAL_MESSAGE,
4803                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4804                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4805                               total_nodes),
4806                      selected_nodes, total_nodes, when_selected);
4807         }
4808     }
4811 /*
4812  * returns a *copy* of the curve of that object.
4813  */
4814 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4815     if (!object)
4816         return NULL;
4818     SPCurve *curve = NULL;
4819     if (SP_IS_PATH(object)) {
4820         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4821         curve = curve_new->copy();
4822     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4823         const gchar *svgd = object->repr->attribute(key);
4824         if (svgd) {
4825             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4826             SPCurve *curve_new = new SPCurve(pv);
4827             if (curve_new) {
4828                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4829             }
4830         }
4831     }
4833     return curve;
4836 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4837     if (!np || !np->object || !curve)
4838         return;
4840     if (SP_IS_PATH(np->object)) {
4841         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4842             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4843         } else {
4844             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4845         }
4846     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4847         Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( LIVEPATHEFFECT(np->object)->lpe->getParameter(np->repr_key) );
4848         if (pathparam) {
4849             pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
4850             np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4851         }
4852     }
4855 /**
4856 SPCanvasItem *
4857 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
4858     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
4860 **/
4862 /**
4863 SPCanvasItem *
4864 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) {
4865     SPCurve *flash_curve = curve->copy();
4866     Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity();
4867     flash_curve->transform(i2d);
4868     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4869     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4870     // unless we also flash the nodes...
4871     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4872     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4873     sp_canvas_item_show(canvasitem);
4874     flash_curve->unref();
4875     return canvasitem;
4878 SPCanvasItem *
4879 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) {
4880     return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path),
4881                                            prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff));
4883 **/
4885 SPCanvasItem *
4886 sp_nodepath_helperpath_from_path(SPDesktop *desktop, SPPath *path) {
4887     SPCurve *flash_curve = sp_path_get_curve_for_edit(path)->copy();
4888     Geom::Matrix i2d = sp_item_i2d_affine(SP_ITEM(path));
4889     flash_curve->transform(i2d);
4890     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4891     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4892     // unless we also flash the nodes...
4893     guint32 color = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
4894     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4895     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4896     sp_canvas_item_show(canvasitem);
4897     flash_curve->unref();
4898     return canvasitem;
4901 // TODO: Merge this with sp_nodepath_make_helper_item()!
4902 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4903     np->show_helperpath = show;
4905     if (show) {
4906         SPCurve *helper_curve = np->curve->copy();
4907         helper_curve->transform(np->i2d);
4908         if (!np->helper_path) {
4909             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
4911             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4912             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);
4913             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4914             sp_canvas_item_move_to_z(np->helper_path, 0);
4915             sp_canvas_item_show(np->helper_path);
4916         } else {
4917             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4918         }
4919         helper_curve->unref();
4920     } else {
4921         if (np->helper_path) {
4922             GtkObject *temp = np->helper_path;
4923             np->helper_path = NULL;
4924             gtk_object_destroy(temp);
4925         }
4926     }
4929 /* sp_nodepath_make_straight_path:
4930  *   Prevents user from curving the path by dragging a segment or activating handles etc.
4931  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
4932  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
4933  */
4934 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4935     np->straight_path = true;
4936     np->show_handles = false;
4937     g_message("add code to make the path straight.");
4938     // do sp_nodepath_convert_node_type on all nodes?
4939     // coding tip: search for this text : "Make selected segments lines"
4942 /*
4943   Local Variables:
4944   mode:c++
4945   c-file-style:"stroustrup"
4946   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4947   indent-tabs-mode:nil
4948   fill-column:99
4949   End:
4950 */
4951 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :