Code

Next roud of NR ==> Geom conversion
[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 Geom::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, Geom::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, Geom::Point *p, guint state, gpointer data);
136 static void node_handle_moved(SPKnot *knot, Geom::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                                          Geom::Point *ppos, Geom::Point *pos, Geom::Point *npos);
147 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
149 /* Helpers */
151 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
152 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
153 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
155 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
156 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
158 // active_node indicates mouseover node
159 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
161 static SPCanvasItem *
162 sp_nodepath_make_helper_item(Inkscape::NodePath::Path *np, /*SPDesktop *desktop, */const SPCurve *curve, bool show = false) {
163     SPCurve *helper_curve = curve->copy();
164     helper_curve->transform(np->i2d);
165     SPCanvasItem *helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
166     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
167     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helper_path), 0, SP_WIND_RULE_NONZERO);
168     sp_canvas_item_move_to_z(helper_path, 0);
169     if (show) {
170         sp_canvas_item_show(helper_path);
171     }
172     helper_curve->unref();
173     return helper_path;
176 static SPCanvasItem *
177 canvasitem_from_pathvec(Inkscape::NodePath::Path *np, Geom::PathVector const &pathv, bool show) {
178     SPCurve *helper_curve = new SPCurve(pathv);
179     return sp_nodepath_make_helper_item(np, helper_curve, show);
182 static void
183 sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) {
184     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> >* helper_path_vec;
185     if (!SP_IS_LPE_ITEM(np->item)) {
186         g_print ("Only LPEItems can have helperpaths!\n");
187         return;
188     }
190     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
191     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
192     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
193         Inkscape::LivePathEffect::LPEObjectReference *lperef = (*i);
194         Inkscape::LivePathEffect::Effect *lpe = lperef->lpeobject->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;
269     }
271     if (curve->get_segment_count() < 1) {
272         curve->unref();
273         return NULL; // prevent crash for one-node paths
274     }
276     //Create new nodepath
277     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
278     if (!np) {
279         curve->unref();
280         return NULL;
281     }
283     // Set defaults
284     np->desktop     = desktop;
285     np->object      = object;
286     np->subpaths    = NULL;
287     np->selected    = NULL;
288     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
289     np->local_change = 0;
290     np->show_handles = show_handles;
291     np->helper_path = NULL;
292     np->helper_path_vec = new HelperPathList;
293     np->helperpath_rgba = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
294     np->helperpath_width = 1.0;
295     np->curve = curve->copy();
296     np->show_helperpath = (prefs_get_int_attribute ("tools.nodes", "show_helperpath",  0) == 1);
297     if (SP_IS_LPE_ITEM(object)) {
298         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
299         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
300             np->show_helperpath = true;
301         }            
302     }
303     np->straight_path = false;
304     if (IS_LIVEPATHEFFECT(object) && item) {
305         np->item = item;
306     } else {
307         np->item = SP_ITEM(object);
308     }
310     // we need to update item's transform from the repr here,
311     // because they may be out of sync when we respond
312     // to a change in repr by regenerating nodepath     --bb
313     sp_object_read_attr(SP_OBJECT(np->item), "transform");
315     np->i2d  = sp_item_i2d_affine(np->item);
316     np->d2i  = np->i2d.inverse();
318     np->repr = repr;
319     if (repr_key_in) { // apparantly the object is an LPEObject
320         np->repr_key = g_strdup(repr_key_in);
321         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
322         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
323         if (lpeparam) {
324             lpeparam->param_setup_nodepath(np);
325         }
326     } else {
327         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
328         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
329             np->repr_key = g_strdup("inkscape:original-d");
331             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
332             if (lpe) {
333                 lpe->setup_nodepath(np);
334             }
335         } else {
336             np->repr_key = g_strdup("d");
337         }
338     }
340     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
341      * So for example a closed rectangle has a nodetypestring of length 5.
342      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
343     Geom::PathVector pathv_sanitized = pathv_to_linear_and_cubic_beziers(np->curve->get_pathvector());
344     np->curve->set_pathvector(pathv_sanitized);
345     guint length = np->curve->get_segment_count();
346     for (Geom::PathVector::const_iterator pit = pathv_sanitized.begin(); pit != pathv_sanitized.end(); ++pit) {
347         length += pit->empty() ? 0 : 1;
348     }
350     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
351     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
353     // create the subpath(s) from the bpath
354     subpaths_from_pathvector(np, pathv_sanitized, typestr);
356     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
357     np->subpaths = g_list_reverse(np->subpaths);
359     delete[] typestr;
360     curve->unref();
362     // Draw helper curve
363     if (np->show_helperpath) {
364         np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true);
365     }
367     sp_nodepath_create_helperpaths(np);
369     return np;
372 /**
373  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
374  */
375 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
377     if (!np) {  //soft fail, like delete
378         return;
379     }
381     while (np->subpaths) {
382         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
383     }
385     //Inform the ShapeEditor that made me, if any, that I am gone.
386     if (np->shape_editor)
387         np->shape_editor->nodepath_destroyed();
389     g_assert(!np->selected);
391     if (np->helper_path) {
392         GtkObject *temp = np->helper_path;
393         np->helper_path = NULL;
394         gtk_object_destroy(temp);
395     }
396     if (np->curve) {
397         np->curve->unref();
398         np->curve = NULL;
399     }
401     if (np->repr_key) {
402         g_free(np->repr_key);
403         np->repr_key = NULL;
404     }
405     if (np->repr_nodetypes_key) {
406         g_free(np->repr_nodetypes_key);
407         np->repr_nodetypes_key = NULL;
408     }
410     sp_nodepath_destroy_helperpaths(np);
411     delete np->helper_path_vec;
412     np->helper_path_vec = NULL;
414     np->desktop = NULL;
416     g_free(np);
419 /**
420  *  Return the node count of a given NodeSubPath.
421  */
422 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
424     int nodeCount = 0;
426     if (subpath) {
427         nodeCount = g_list_length(subpath->nodes);
428     }
430     return nodeCount;
433 /**
434  *  Return the node count of a given NodePath.
435  */
436 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
438     gint nodeCount = 0;
439     if (np) {
440         for (GList *item = np->subpaths ; item ; item=item->next) {
441             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
442             nodeCount += g_list_length(subpath->nodes);
443         }
444     }
445     return nodeCount;
448 /**
449  *  Return the subpath count of a given NodePath.
450  */
451 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
453     gint nodeCount = 0;
454     if (np) {
455         nodeCount = g_list_length(np->subpaths);
456     }
457     return nodeCount;
460 /**
461  *  Return the selected node count of a given NodePath.
462  */
463 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
465     gint nodeCount = 0;
466     if (np) {
467         nodeCount = g_list_length(np->selected);
468     }
469     return nodeCount;
472 /**
473  *  Return the number of subpaths where nodes are selected in a given NodePath.
474  */
475 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
477     gint nodeCount = 0;
478     if (np && np->selected) {
479         if (!np->selected->next) {
480             nodeCount = 1;
481         } else {
482             for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
483                 Inkscape::NodePath::SubPath *subpath = static_cast<Inkscape::NodePath::SubPath *>(spl->data);
484                 for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
485                     Inkscape::NodePath::Node *node = static_cast<Inkscape::NodePath::Node *>(nl->data);
486                     if (node->selected) {
487                         nodeCount++;
488                         break;
489                     }
490                 }
491             }
492         }
493     }
494     return nodeCount;
497 /**
498  * Clean up a nodepath after editing.
499  *
500  * Currently we are deleting trivial subpaths.
501  */
502 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
504     GList *badSubPaths = NULL;
506     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
507     for (GList *l = nodepath->subpaths; l ; l=l->next) {
508        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
509        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
510             badSubPaths = g_list_append(badSubPaths, sp);
511     }
513     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
514     //also removes the subpath from nodepath->subpaths
515     for (GList *l = badSubPaths; l ; l=l->next) {
516        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
517         sp_nodepath_subpath_destroy(sp);
518     }
520     g_list_free(badSubPaths);
523 /**
524  * Create new nodepaths from pathvector, make it subpaths of np.
525  * \param t The node type array.
526  */
527 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
529     guint i = 0;  // index into node type array
530     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
531         if (pit->empty())
532             continue;  // don't add single knot paths
534         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
536         Geom::Point ppos = pit->initialPoint() * np->i2d;
537         NRPathcode pcode = NR_MOVETO;
539         /* 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)*/
540         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
541             if( dynamic_cast<Geom::LineSegment const*>(&*cit) ||
542                 dynamic_cast<Geom::HLineSegment const*>(&*cit) ||
543                 dynamic_cast<Geom::VLineSegment const*>(&*cit) )
544             {
545                 Geom::Point pos = cit->initialPoint() * (Geom::Matrix)np->i2d;
546                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
548                 ppos = cit->finalPoint() * (Geom::Matrix)np->i2d;
549                 pcode = NR_LINETO;
550             }
551             else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
552                 std::vector<Geom::Point> points = cubic_bezier->points();
553                 Geom::Point pos = points[0] * (Geom::Matrix)np->i2d;
554                 Geom::Point npos = points[1] * (Geom::Matrix)np->i2d;
555                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
557                 ppos = points[2] * (Geom::Matrix)np->i2d;
558                 pcode = NR_CURVETO;
559             }
560         }
562         if (pit->closed()) {
563             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
564             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
565              * If the length is zero, don't add it to the nodepath. */
566             Geom::Curve const &closing_seg = pit->back_closed();
567             // Don't use !closing_seg.isDegenerate() as it is too precise, and does not account for floating point rounding probs (LP bug #257289)
568             if ( ! are_near(closing_seg.initialPoint(), closing_seg.finalPoint()) ) {
569                 Geom::Point pos = closing_seg.finalPoint() * (Geom::Matrix)np->i2d;
570                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
571             }
573             sp_nodepath_subpath_close(sp);
574         }
575     }
578 /**
579  * Convert from sodipodi:nodetypes to new style type array.
580  */
581 static
582 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
584     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
586     guint pos = 0;
588     if (types) {
589         for (guint i = 0; types[i] && ( i < length ); i++) {
590             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
591             if (types[i] != '\0') {
592                 switch (types[i]) {
593                     case 's':
594                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
595                         break;
596                     case 'z':
597                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
598                         break;
599                     case 'c':
600                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
601                         break;
602                     default:
603                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
604                         break;
605                 }
606             }
607         }
608     }
610     while (pos < length) {
611         typestr[pos++] = Inkscape::NodePath::NODE_NONE;
612     }
614     return typestr;
617 /**
618  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
619  * updated but repr is not (for speed). Used during curve and node drag.
620  */
621 static void update_object(Inkscape::NodePath::Path *np)
623     g_assert(np);
625     np->curve->unref();
626     np->curve = create_curve(np);
628     sp_nodepath_set_curve(np, np->curve);
630     if (np->show_helperpath) {
631         SPCurve * helper_curve = np->curve->copy();
632         helper_curve->transform(np->i2d);
633         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
634         helper_curve->unref();
635     }
637     // updating helperpaths of LPEItems is now done in sp_lpe_item_update();
638     //sp_nodepath_update_helperpaths(np);
640     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
641     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
642     np->shape_editor->update_knotholder();
645 /**
646  * Update XML path node with data from path object.
647  */
648 static void update_repr_internal(Inkscape::NodePath::Path *np)
650     g_assert(np);
652     Inkscape::XML::Node *repr = np->object->repr;
654     np->curve->unref();
655     np->curve = create_curve(np);
657     gchar *typestr = create_typestr(np);
658     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
660     // determine if path has an effect applied and write to correct "d" attribute.
661     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
662         np->local_change++;
663         repr->setAttribute(np->repr_key, svgpath);
664     }
666     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
667         np->local_change++;
668         repr->setAttribute(np->repr_nodetypes_key, typestr);
669     }
671     g_free(svgpath);
672     g_free(typestr);
674     if (np->show_helperpath) {
675         SPCurve * helper_curve = np->curve->copy();
676         helper_curve->transform(np->i2d);
677         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
678         helper_curve->unref();
679     }
681     // TODO: do we need this call here? after all, update_object() should have been called just before
682     //sp_nodepath_update_helperpaths(np);
685 /**
686  * Update XML path node with data from path object, commit changes forever.
687  */
688 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
690     //fixme: np can be NULL, so check before proceeding
691     g_return_if_fail(np != NULL);
693     update_repr_internal(np);
694     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
696     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
697                      annotation);
700 /**
701  * Update XML path node with data from path object, commit changes with undo.
702  */
703 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
705     update_repr_internal(np);
706     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
707                            annotation);
710 /**
711  * Make duplicate of path, replace corresponding XML node in tree, commit.
712  */
713 static void stamp_repr(Inkscape::NodePath::Path *np)
715     g_assert(np);
717     Inkscape::XML::Node *old_repr = np->object->repr;
718     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
720     // remember the position of the item
721     gint pos = old_repr->position();
722     // remember parent
723     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
725     SPCurve *curve = create_curve(np);
726     gchar *typestr = create_typestr(np);
728     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
730     new_repr->setAttribute(np->repr_key, svgpath);
731     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
733     // add the new repr to the parent
734     parent->appendChild(new_repr);
735     // move to the saved position
736     new_repr->setPosition(pos > 0 ? pos : 0);
738     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
739                      _("Stamp"));
741     Inkscape::GC::release(new_repr);
742     g_free(svgpath);
743     g_free(typestr);
744     curve->unref();
747 /**
748  * Create curve from path.
749  */
750 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
752     SPCurve *curve = new SPCurve();
754     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
755        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
756        curve->moveto(sp->first->pos * np->d2i);
757        Inkscape::NodePath::Node *n = sp->first->n.other;
758         while (n) {
759             Geom::Point const end_pt = n->pos * np->d2i;
760             switch (n->code) {
761                 case NR_LINETO:
762                     curve->lineto(end_pt);
763                     break;
764                 case NR_CURVETO:
765                     curve->curveto(n->p.other->n.pos * np->d2i,
766                                      n->p.pos * np->d2i,
767                                      end_pt);
768                     break;
769                 default:
770                     g_assert_not_reached();
771                     break;
772             }
773             if (n != sp->last) {
774                 n = n->n.other;
775             } else {
776                 n = NULL;
777             }
778         }
779         if (sp->closed) {
780             curve->closepath();
781         }
782     }
784     return curve;
787 /**
788  * Convert path type string to sodipodi:nodetypes style.
789  */
790 static gchar *create_typestr(Inkscape::NodePath::Path *np)
792     gchar *typestr = g_new(gchar, 32);
793     gint len = 32;
794     gint pos = 0;
796     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
797        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
799         if (pos >= len) {
800             typestr = g_renew(gchar, typestr, len + 32);
801             len += 32;
802         }
804         typestr[pos++] = 'c';
806        Inkscape::NodePath::Node *n;
807         n = sp->first->n.other;
808         while (n) {
809             gchar code;
811             switch (n->type) {
812                 case Inkscape::NodePath::NODE_CUSP:
813                     code = 'c';
814                     break;
815                 case Inkscape::NodePath::NODE_SMOOTH:
816                     code = 's';
817                     break;
818                 case Inkscape::NodePath::NODE_SYMM:
819                     code = 'z';
820                     break;
821                 default:
822                     g_assert_not_reached();
823                     code = '\0';
824                     break;
825             }
827             if (pos >= len) {
828                 typestr = g_renew(gchar, typestr, len + 32);
829                 len += 32;
830             }
832             typestr[pos++] = code;
834             if (n != sp->last) {
835                 n = n->n.other;
836             } else {
837                 n = NULL;
838             }
839         }
840     }
842     if (pos >= len) {
843         typestr = g_renew(gchar, typestr, len + 1);
844         len += 1;
845     }
847     typestr[pos++] = '\0';
849     return typestr;
852 // Returns different message contexts depending on the current context. This function should only
853 // be called when ec is either a SPNodeContext or SPLPEToolContext, thus we return NULL in all
854 // other cases.
855 static Inkscape::MessageContext *
856 get_message_context(SPEventContext *ec)
858     Inkscape::MessageContext *mc = 0;
860     if (SP_IS_NODE_CONTEXT(ec)) {
861         mc = SP_NODE_CONTEXT(ec)->_node_message_context;
862     } else if (SP_IS_LPETOOL_CONTEXT(ec)) {
863         mc = SP_LPETOOL_CONTEXT(ec)->_lpetool_message_context;
864     } else {
865         g_warning ("Nodepath should only be present in Node tool or Geometric tool.");
866     }
868     return mc;
871 /**
872  \brief Fills node and handle positions for three nodes, splitting line
873   marked by end at distance t.
874  */
875 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
877     g_assert(new_path != NULL);
878     g_assert(end      != NULL);
880     g_assert(end->p.other == new_path);
881    Inkscape::NodePath::Node *start = new_path->p.other;
882     g_assert(start);
884     if (end->code == NR_LINETO) {
885         new_path->type =Inkscape::NodePath::NODE_CUSP;
886         new_path->code = NR_LINETO;
887         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
888     } else {
889         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
890         new_path->code = NR_CURVETO;
891         gdouble s      = 1 - t;
892         for (int dim = 0; dim < 2; dim++) {
893             Geom::Coord const f000 = start->pos[dim];
894             Geom::Coord const f001 = start->n.pos[dim];
895             Geom::Coord const f011 = end->p.pos[dim];
896             Geom::Coord const f111 = end->pos[dim];
897             Geom::Coord const f00t = s * f000 + t * f001;
898             Geom::Coord const f01t = s * f001 + t * f011;
899             Geom::Coord const f11t = s * f011 + t * f111;
900             Geom::Coord const f0tt = s * f00t + t * f01t;
901             Geom::Coord const f1tt = s * f01t + t * f11t;
902             Geom::Coord const fttt = s * f0tt + t * f1tt;
903             start->n.pos[dim]    = f00t;
904             new_path->p.pos[dim] = f0tt;
905             new_path->pos[dim]   = fttt;
906             new_path->n.pos[dim] = f1tt;
907             end->p.pos[dim]      = f11t;
908         }
909     }
912 /**
913  * Adds new node on direct line between two nodes, activates handles of all
914  * three nodes.
915  */
916 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
918     g_assert(end);
919     g_assert(end->subpath);
920     g_assert(g_list_find(end->subpath->nodes, end));
922    Inkscape::NodePath::Node *start = end->p.other;
923     g_assert( start->n.other == end );
924    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
925                                                end,
926                                                (NRPathcode)end->code == NR_LINETO?
927                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
928                                                (NRPathcode)end->code,
929                                                &start->pos, &start->pos, &start->n.pos);
930     sp_nodepath_line_midpoint(newnode, end, t);
932     sp_node_adjust_handles(start);
933     sp_node_update_handles(start);
934     sp_node_update_handles(newnode);
935     sp_node_adjust_handles(end);
936     sp_node_update_handles(end);
938     return newnode;
941 /**
942 \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
943 */
944 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
946     g_assert(node);
947     g_assert(node->subpath);
948     g_assert(g_list_find(node->subpath->nodes, node));
950     Inkscape::NodePath::Node* result = 0;
951     Inkscape::NodePath::SubPath *sp = node->subpath;
952     Inkscape::NodePath::Path *np    = sp->nodepath;
954     if (sp->closed) {
955         sp_nodepath_subpath_open(sp, node);
956         result = sp->first;
957     } else if ( (node == sp->first) || (node == sp->last ) ){
958         // no break for end nodes
959         result = 0;
960     } else {
961         // create a new subpath
962         Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
964         // duplicate the break node as start of the new subpath
965         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL,
966                                                                  static_cast<Inkscape::NodePath::NodeType>(node->type),
967                                                                  NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
969         // attach rest of curve to new node
970         g_assert(node->n.other);
971         newnode->n.other = node->n.other; node->n.other = NULL;
972         newnode->n.other->p.other = newnode;
973         newsubpath->last = sp->last;
974         sp->last = node;
975         node = newnode;
976         while (node->n.other) {
977             node = node->n.other;
978             node->subpath = newsubpath;
979             sp->nodes = g_list_remove(sp->nodes, node);
980             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
981         }
984         result = newnode;
985     }
986     return result;
989 /**
990  * Duplicate node and connect to neighbours.
991  */
992 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
994     g_assert(node);
995     g_assert(node->subpath);
996     g_assert(g_list_find(node->subpath->nodes, node));
998    Inkscape::NodePath::SubPath *sp = node->subpath;
1000     NRPathcode code = (NRPathcode) node->code;
1001     if (code == NR_MOVETO) { // if node is the endnode,
1002         node->code = NR_LINETO; // new one is inserted before it, so change that to line
1003     }
1005     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
1007     if (!node->n.other || !node->p.other) { // if node is an endnode, select it
1008         return node;
1009     } else {
1010         return newnode; // otherwise select the newly created node
1011     }
1014 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
1016     node->p.pos = (node->pos + (node->pos - node->n.pos));
1019 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
1021     node->n.pos = (node->pos + (node->pos - node->p.pos));
1024 /**
1025  * Change line type at node, with side effects on neighbours.
1026  */
1027 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
1029     g_assert(end);
1030     g_assert(end->subpath);
1031     g_assert(end->p.other);
1033     if (end->code != static_cast<guint>(code) ) {
1034         Inkscape::NodePath::Node *start = end->p.other;
1036         end->code = code;
1038         if (code == NR_LINETO) {
1039             if (start->code == NR_LINETO) {
1040                 sp_nodepath_set_node_type(start, Inkscape::NodePath::NODE_CUSP);
1041             }
1042             if (end->n.other) {
1043                 if (end->n.other->code == NR_LINETO) {
1044                     sp_nodepath_set_node_type(end, Inkscape::NodePath::NODE_CUSP);
1045                 }
1046             }
1047         } else {
1048             Geom::Point delta = end->pos - start->pos;
1049             start->n.pos = start->pos + delta / 3;
1050             end->p.pos = end->pos - delta / 3;
1051             sp_node_adjust_handle(start, 1);
1052             sp_node_adjust_handle(end, -1);
1053         }
1055         sp_node_update_handles(start);
1056         sp_node_update_handles(end);
1057     }
1060 /**
1061  * Change node type, and its handles accordingly.
1062  */
1063 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1065     g_assert(node);
1066     g_assert(node->subpath);
1068     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1069         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1070             type =Inkscape::NodePath::NODE_CUSP;
1071         }
1072     }
1074     node->type = type;
1076     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1077         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1078         node->knot->setSize (node->selected? 11 : 9);
1079         sp_knot_update_ctrl(node->knot);
1080     } else {
1081         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1082         node->knot->setSize (node->selected? 9 : 7);
1083         sp_knot_update_ctrl(node->knot);
1084     }
1086     // if one of handles is mouseovered, preserve its position
1087     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1088         sp_node_adjust_handle(node, 1);
1089     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1090         sp_node_adjust_handle(node, -1);
1091     } else {
1092         sp_node_adjust_handles(node);
1093     }
1095     sp_node_update_handles(node);
1097     sp_nodepath_update_statusbar(node->subpath->nodepath);
1099     return node;
1102 bool
1103 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1105 // TODO clean up multiple returns
1106         Inkscape::NodePath::Node *othernode = side->other;
1107         if (!othernode)
1108             return false;
1109         NRPathcode const code = sp_node_path_code_from_side(node, side);
1110         if (code == NR_LINETO)
1111             return true;
1112         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1113         if (&node->p == side) {
1114             other_to_me = &othernode->n;
1115         } else if (&node->n == side) {
1116             other_to_me = &othernode->p;
1117         } 
1118         if (!other_to_me)
1119             return false;
1120         bool is_line = 
1121              (Geom::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1122               Geom::L2(node->pos - side->pos) < 1e-6);
1123         return is_line;
1126 /**
1127  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1128  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1129  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1130  * If already cusp and set to cusp, retracts handles.
1131 */
1132 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1134     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1136 /* 
1137   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1138  
1139         if (two_handles) {
1140             // do nothing, adjust_handles called via set_node_type will line them up
1141         } else if (one_handle) {
1142             if (opposite_to_handle_is_line) {
1143                 if (lined_up) {
1144                     // already half-smooth; pull opposite handle too making it fully smooth
1145                 } else {
1146                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1147                 }
1148             } else {
1149                 // pull opposite handle in line with the existing one
1150             }
1151         } else if (no_handles) {
1152             if (both_segments_are_lines OR both_segments_are_curves) {
1153                 //pull both handles
1154             } else {
1155                 // pull the handle opposite to line segment, making node half-smooth
1156             }
1157         }
1158 */
1159         bool p_has_handle = (Geom::L2(node->pos  - node->p.pos) > 1e-6);
1160         bool n_has_handle = (Geom::L2(node->pos  - node->n.pos) > 1e-6);
1161         bool p_is_line = sp_node_side_is_line(node, &node->p);
1162         bool n_is_line = sp_node_side_is_line(node, &node->n);
1164         if (p_has_handle && n_has_handle) {
1165             // do nothing, adjust_handles will line them up
1166         } else if (p_has_handle || n_has_handle) {
1167             if (p_has_handle && n_is_line) {
1168                 Radial line (node->n.other->pos - node->pos);
1169                 Radial handle (node->pos - node->p.pos);
1170                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1171                     // already half-smooth; pull opposite handle too making it fully smooth
1172                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1173                 } else {
1174                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1175                 }
1176             } else if (n_has_handle && p_is_line) {
1177                 Radial line (node->p.other->pos - node->pos);
1178                 Radial handle (node->pos - node->n.pos);
1179                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1180                     // already half-smooth; pull opposite handle too making it fully smooth
1181                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1182                 } else {
1183                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1184                 }
1185             } else if (p_has_handle && node->n.other) {
1186                 // pull n handle
1187                 node->n.other->code = NR_CURVETO;
1188                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1189                     Geom::L2(node->p.pos - node->pos) :
1190                     Geom::L2(node->n.other->pos - node->pos) / 3;
1191                 node->n.pos = node->pos - (len / Geom::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1192             } else if (n_has_handle && node->p.other) {
1193                 // pull p handle
1194                 node->code = NR_CURVETO;
1195                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1196                     Geom::L2(node->n.pos - node->pos) :
1197                     Geom::L2(node->p.other->pos - node->pos) / 3;
1198                 node->p.pos = node->pos - (len / Geom::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1199             }
1200         } else if (!p_has_handle && !n_has_handle) {
1201             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1202                 // no handles, but both segments are either lnes or curves:
1203                 //pull both handles
1205                 // convert both to curves:
1206                 node->code = NR_CURVETO;
1207                 node->n.other->code = NR_CURVETO;
1209                 Geom::Point leg_prev = node->pos - node->p.other->pos;
1210                 Geom::Point leg_next = node->pos - node->n.other->pos;
1212                 double norm_leg_prev = L2(leg_prev);
1213                 double norm_leg_next = L2(leg_next);
1215                 Geom::Point delta;
1216                 if (norm_leg_next > 0.0) {
1217                     delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1218                     (&delta)->normalize();
1219                 }
1221                 if (type == Inkscape::NodePath::NODE_SYMM) {
1222                     double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1223                     node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1224                     node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1225                 } else {
1226                     // length of handle is proportional to distance to adjacent node
1227                     node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1228                     node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1229                 }
1231             } else {
1232                 // pull the handle opposite to line segment, making it half-smooth
1233                 if (p_is_line && node->n.other) {
1234                     if (type != Inkscape::NodePath::NODE_SYMM) {
1235                         // pull n handle
1236                         node->n.other->code = NR_CURVETO;
1237                         double len =  Geom::L2(node->n.other->pos - node->pos) / 3;
1238                         node->n.pos = node->pos + (len / Geom::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1239                     }
1240                 } else if (n_is_line && node->p.other) {
1241                     if (type != Inkscape::NodePath::NODE_SYMM) {
1242                         // pull p handle
1243                         node->code = NR_CURVETO;
1244                         double len =  Geom::L2(node->p.other->pos - node->pos) / 3;
1245                         node->p.pos = node->pos + (len / Geom::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1246                     }
1247                 }
1248             }
1249         }
1250     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1251         // cusping a cusp: retract nodes
1252         node->p.pos = node->pos;
1253         node->n.pos = node->pos;
1254     }
1256     sp_nodepath_set_node_type (node, type);
1259 /**
1260  * Move node to point, and adjust its and neighbouring handles.
1261  */
1262 void sp_node_moveto(Inkscape::NodePath::Node *node, Geom::Point p)
1264     Geom::Point delta = p - node->pos;
1265     node->pos = p;
1267     node->p.pos += delta;
1268     node->n.pos += delta;
1270     Inkscape::NodePath::Node *node_p = NULL;
1271     Inkscape::NodePath::Node *node_n = NULL;
1273     if (node->p.other) {
1274         if (node->code == NR_LINETO) {
1275             sp_node_adjust_handle(node, 1);
1276             sp_node_adjust_handle(node->p.other, -1);
1277             node_p = node->p.other;
1278         }
1279     }
1280     if (node->n.other) {
1281         if (node->n.other->code == NR_LINETO) {
1282             sp_node_adjust_handle(node, -1);
1283             sp_node_adjust_handle(node->n.other, 1);
1284             node_n = node->n.other;
1285         }
1286     }
1288     // this function is only called from batch movers that will update display at the end
1289     // themselves, so here we just move all the knots without emitting move signals, for speed
1290     sp_node_update_handles(node, false);
1291     if (node_n) {
1292         sp_node_update_handles(node_n, false);
1293     }
1294     if (node_p) {
1295         sp_node_update_handles(node_p, false);
1296     }
1299 /**
1300  * Call sp_node_moveto() for node selection and handle possible snapping.
1301  */
1302 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, Geom::Coord dx, Geom::Coord dy,
1303                                             bool const snap, bool constrained = false, 
1304                                             Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point())
1306     Geom::Coord best = NR_HUGE;
1307     Geom::Point delta(dx, dy);
1308     Geom::Point best_pt = delta;
1309     Inkscape::SnappedPoint best_abs;
1310     
1311     if (snap) {    
1312         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1313          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1314          * must provide that information. */
1315           
1316         // Build a list of the unselected nodes to which the snapper should snap 
1317         std::vector<Geom::Point> unselected_nodes;
1318         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1319             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1320             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1321                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1322                 if (!node->selected) {
1323                     unselected_nodes.push_back(to_2geom(node->pos));
1324                 }    
1325             }
1326         }        
1327         
1328         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1329         
1330         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1331             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1332             m.setup(nodepath->desktop, false, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1333             Inkscape::SnappedPoint s;
1334             if (constrained) {
1335                 Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1336                 dedicated_constraint.setPoint(n->pos);
1337                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(n->pos + delta), dedicated_constraint);
1338             } else {
1339                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(n->pos + delta));
1340             }            
1341             if (s.getSnapped() && (s.getDistance() < best)) {
1342                 best = s.getDistance();
1343                 best_abs = s;
1344                 best_pt = from_2geom(s.getPoint()) - n->pos;
1345             }
1346         }
1347                         
1348         if (best_abs.getSnapped()) {
1349             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1350         } else {
1351             nodepath->desktop->snapindicator->remove_snappoint();    
1352         }
1353     }
1355     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1356         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1357         sp_node_moveto(n, n->pos + best_pt);
1358     }
1360     // do not update repr here so that node dragging is acceptably fast
1361     update_object(nodepath);
1364 /**
1365 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1366 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1367 near x = 0.
1368  */
1369 double
1370 sculpt_profile (double x, double alpha, guint profile)
1372     double result = 1;
1374     if (x >= 1) {
1375         result = 0;
1376     } else if (x <= 0) {
1377         result = 1;
1378     } else {
1379         switch (profile) {
1380             case SCULPT_PROFILE_LINEAR:
1381                 result = 1 - x;
1382                 break;
1383             case SCULPT_PROFILE_BELL:
1384                 result = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1385                 break;
1386             case SCULPT_PROFILE_ELLIPTIC:
1387                 result = sqrt(1 - x*x);
1388                 break;
1389             default:
1390                 g_assert_not_reached();
1391         }
1392     }
1394     return result;
1397 double
1398 bezier_length (Geom::Point a, Geom::Point ah, Geom::Point bh, Geom::Point b)
1400     // extremely primitive for now, don't have time to look for the real one
1401     double lower = Geom::L2(b - a);
1402     double upper = Geom::L2(ah - a) + Geom::L2(bh - ah) + Geom::L2(bh - b);
1403     return (lower + upper)/2;
1406 void
1407 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, Geom::Point delta, Geom::Point delta_n, Geom::Point delta_p)
1409     n->pos = n->origin + delta;
1410     n->n.pos = n->n.origin + delta_n;
1411     n->p.pos = n->p.origin + delta_p;
1412     sp_node_adjust_handles(n);
1413     sp_node_update_handles(n, false);
1416 /**
1417  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1418  * on how far they are from the dragged node n.
1419  */
1420 static void
1421 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, Geom::Point delta)
1423     g_assert (n);
1424     g_assert (nodepath);
1425     g_assert (n->subpath->nodepath == nodepath);
1427     double pressure = n->knot->pressure;
1428     if (pressure == 0)
1429         pressure = 0.5; // default
1430     pressure = CLAMP (pressure, 0.2, 0.8);
1432     // map pressure to alpha = 1/5 ... 5
1433     double alpha = 1 - 2 * fabs(pressure - 0.5);
1434     if (pressure > 0.5)
1435         alpha = 1/alpha;
1437     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1439     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1440         // Only one subpath has selected nodes:
1441         // use linear mode, where the distance from n to node being dragged is calculated along the path
1443         double n_sel_range = 0, p_sel_range = 0;
1444         guint n_nodes = 0, p_nodes = 0;
1445         guint n_sel_nodes = 0, p_sel_nodes = 0;
1447         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1448         {
1449             double n_range = 0, p_range = 0;
1450             bool n_going = true, p_going = true;
1451             Inkscape::NodePath::Node *n_node = n;
1452             Inkscape::NodePath::Node *p_node = n;
1453             do {
1454                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1455                 if (n_node && n_going)
1456                     n_node = n_node->n.other;
1457                 if (n_node == NULL) {
1458                     n_going = false;
1459                 } else {
1460                     n_nodes ++;
1461                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1462                     if (n_node->selected) {
1463                         n_sel_nodes ++;
1464                         n_sel_range = n_range;
1465                     }
1466                     if (n_node == p_node) {
1467                         n_going = false;
1468                         p_going = false;
1469                     }
1470                 }
1471                 if (p_node && p_going)
1472                     p_node = p_node->p.other;
1473                 if (p_node == NULL) {
1474                     p_going = false;
1475                 } else {
1476                     p_nodes ++;
1477                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1478                     if (p_node->selected) {
1479                         p_sel_nodes ++;
1480                         p_sel_range = p_range;
1481                     }
1482                     if (p_node == n_node) {
1483                         n_going = false;
1484                         p_going = false;
1485                     }
1486                 }
1487             } while (n_going || p_going);
1488         }
1490         // Second pass: actually move nodes in this subpath
1491         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1492         {
1493             double n_range = 0, p_range = 0;
1494             bool n_going = true, p_going = true;
1495             Inkscape::NodePath::Node *n_node = n;
1496             Inkscape::NodePath::Node *p_node = n;
1497             do {
1498                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1499                 if (n_node && n_going)
1500                     n_node = n_node->n.other;
1501                 if (n_node == NULL) {
1502                     n_going = false;
1503                 } else {
1504                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1505                     if (n_node->selected) {
1506                         sp_nodepath_move_node_and_handles (n_node,
1507                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1508                                                            sculpt_profile ((n_range + Geom::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1509                                                            sculpt_profile ((n_range - Geom::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1510                     }
1511                     if (n_node == p_node) {
1512                         n_going = false;
1513                         p_going = false;
1514                     }
1515                 }
1516                 if (p_node && p_going)
1517                     p_node = p_node->p.other;
1518                 if (p_node == NULL) {
1519                     p_going = false;
1520                 } else {
1521                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1522                     if (p_node->selected) {
1523                         sp_nodepath_move_node_and_handles (p_node,
1524                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1525                                                            sculpt_profile ((p_range - Geom::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1526                                                            sculpt_profile ((p_range + Geom::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1527                     }
1528                     if (p_node == n_node) {
1529                         n_going = false;
1530                         p_going = false;
1531                     }
1532                 }
1533             } while (n_going || p_going);
1534         }
1536     } else {
1537         // Multiple subpaths have selected nodes:
1538         // use spatial mode, where the distance from n to node being dragged is measured directly as Geom::L2.
1539         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1540         // fix the pear-like shape when sculpting e.g. a ring
1542         // First pass: calculate range
1543         gdouble direct_range = 0;
1544         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1545             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1546             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1547                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1548                 if (node->selected) {
1549                     direct_range = MAX(direct_range, Geom::L2(node->origin - n->origin));
1550                 }
1551             }
1552         }
1554         // Second pass: actually move nodes
1555         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1556             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1557             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1558                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1559                 if (node->selected) {
1560                     if (direct_range > 1e-6) {
1561                         sp_nodepath_move_node_and_handles (node,
1562                                                        sculpt_profile (Geom::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1563                                                        sculpt_profile (Geom::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1564                                                        sculpt_profile (Geom::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1565                     } else {
1566                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1567                     }
1569                 }
1570             }
1571         }
1572     }
1574     // do not update repr here so that node dragging is acceptably fast
1575     update_object(nodepath);
1579 /**
1580  * Move node selection to point, adjust its and neighbouring handles,
1581  * handle possible snapping, and commit the change with possible undo.
1582  */
1583 void
1584 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1586     if (!nodepath) return;
1588     sp_nodepath_selected_nodes_move(nodepath, dx, dy, 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 node selection off screen and commit the change.
1601  */
1602 void
1603 sp_node_selected_move_screen(SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1605     // borrowed from sp_selection_move_screen in selection-chemistry.c
1606     // we find out the current zoom factor and divide deltas by it
1608     gdouble zoom = desktop->current_zoom();
1609     gdouble zdx = dx / zoom;
1610     gdouble zdy = dy / zoom;
1612     if (!nodepath) return;
1614     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1616     if (dx == 0) {
1617         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1618     } else if (dy == 0) {
1619         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1620     } else {
1621         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1622     }
1625 /**
1626  * Move selected nodes to the absolute position given
1627  */
1628 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis)
1630     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1631         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1632         Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]);
1633         sp_node_moveto(n, npos);
1634     }
1636     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1639 /**
1640  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return Geom::Nothing
1641  */
1642 boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1644     boost::optional<Geom::Coord> no_coord;
1645     g_return_val_if_fail(nodepath->selected, no_coord);
1647     // determine coordinate of first selected node
1648     GList *nsel = nodepath->selected;
1649     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1650     Geom::Coord coord = n->pos[axis];
1651     bool coincide = true;
1653     // compare it to the coordinates of all the other selected nodes
1654     for (GList *l = nsel->next; l != NULL; l = l->next) {
1655         n = (Inkscape::NodePath::Node *) l->data;
1656         if (n->pos[axis] != coord) {
1657             coincide = false;
1658         }
1659     }
1660     if (coincide) {
1661         return coord;
1662     } else {
1663         Geom::Rect bbox = sp_node_selected_bbox(nodepath);
1664         // currently we return the coordinate of the bounding box midpoint because I don't know how
1665         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1666         return bbox.midpoint()[axis];
1667     }
1670 /** If they don't yet exist, creates knot and line for the given side of the node */
1671 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1673     if (!side->knot) {
1674         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"));
1676         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1677         side->knot->setSize (7);
1678         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1679         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1680         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1681         sp_knot_update_ctrl(side->knot);
1683         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1684         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1685         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1686         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1687         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1688         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1689     }
1691     if (!side->line) {
1692         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1693                                         SP_TYPE_CTRLLINE, NULL);
1694     }
1697 /**
1698  * Ensure the given handle of the node is visible/invisible, update its screen position
1699  */
1700 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1702     g_assert(node != NULL);
1704    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1705     NRPathcode code = sp_node_path_code_from_side(node, side);
1707     show_handle = show_handle && (code == NR_CURVETO) && (Geom::L2(side->pos - node->pos) > 1e-6);
1709     if (show_handle) {
1710         if (!side->knot) { // No handle knot at all
1711             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1712             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1713             side->knot->pos = side->pos;
1714             if (side->knot->item)
1715                 SP_CTRL(side->knot->item)->moveto(side->pos);
1716             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1717             sp_knot_show(side->knot);
1718         } else {
1719             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1720                 if (fire_move_signals) {
1721                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1722                 } else {
1723                     sp_knot_moveto(side->knot, side->pos);
1724                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1725                 }
1726             }
1727             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1728                 sp_knot_show(side->knot);
1729             }
1730         }
1731         sp_canvas_item_show(side->line);
1732     } else {
1733         if (side->knot) {
1734             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1735                 sp_knot_hide(side->knot);
1736             }
1737         }
1738         if (side->line) {
1739             sp_canvas_item_hide(side->line);
1740         }
1741     }
1744 /**
1745  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1746  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1747  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1748  * updated; otherwise, just move the knots silently (used in batch moves).
1749  */
1750 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1752     g_assert(node != NULL);
1754     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1755         sp_knot_show(node->knot);
1756     }
1758     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1759         if (fire_move_signals)
1760             sp_knot_set_position(node->knot, node->pos, 0);
1761         else
1762             sp_knot_moveto(node->knot, node->pos);
1763     }
1765     gboolean show_handles = node->selected;
1766     if (node->p.other != NULL) {
1767         if (node->p.other->selected) show_handles = TRUE;
1768     }
1769     if (node->n.other != NULL) {
1770         if (node->n.other->selected) show_handles = TRUE;
1771     }
1773     if (node->subpath->nodepath->show_handles == false)
1774         show_handles = FALSE;
1776     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1777     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1780 /**
1781  * Call sp_node_update_handles() for all nodes on subpath.
1782  */
1783 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1785     g_assert(subpath != NULL);
1787     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1788         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1789     }
1792 /**
1793  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1794  */
1795 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1797     g_assert(nodepath != NULL);
1799     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1800         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1801     }
1804 void
1805 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1807     if (nodepath) {
1808         nodepath->show_handles = show;
1809         sp_nodepath_update_handles(nodepath);
1810     }
1813 /**
1814  * Adds all selected nodes in nodepath to list.
1815  */
1816 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1818     StlConv<Node *>::list(l, selected);
1819 /// \todo this adds a copying, rework when the selection becomes a stl list
1822 /**
1823  * Align selected nodes on the specified axis.
1824  */
1825 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1827     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1828         return;
1829     }
1831     if ( !nodepath->selected->next ) { // only one node selected
1832         return;
1833     }
1834    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1835     Geom::Point dest(pNode->pos);
1836     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1837         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1838         if (pNode) {
1839             dest[axis] = pNode->pos[axis];
1840             sp_node_moveto(pNode, dest);
1841         }
1842     }
1844     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1847 /// Helper struct.
1848 struct NodeSort
1850    Inkscape::NodePath::Node *_node;
1851     Geom::Coord _coord;
1852     /// \todo use vectorof pointers instead of calling copy ctor
1853     NodeSort(Inkscape::NodePath::Node *node, Geom::Dim2 axis) :
1854         _node(node), _coord(node->pos[axis])
1855     {}
1857 };
1859 static bool operator<(NodeSort const &a, NodeSort const &b)
1861     return (a._coord < b._coord);
1864 /**
1865  * Distribute selected nodes on the specified axis.
1866  */
1867 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1869     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1870         return;
1871     }
1873     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1874         return;
1875     }
1877    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1878     std::vector<NodeSort> sorted;
1879     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1880         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1881         if (pNode) {
1882             NodeSort n(pNode, axis);
1883             sorted.push_back(n);
1884             //dest[axis] = pNode->pos[axis];
1885             //sp_node_moveto(pNode, dest);
1886         }
1887     }
1888     std::sort(sorted.begin(), sorted.end());
1889     unsigned int len = sorted.size();
1890     //overall bboxes span
1891     float dist = (sorted.back()._coord -
1892                   sorted.front()._coord);
1893     //new distance between each bbox
1894     float step = (dist) / (len - 1);
1895     float pos = sorted.front()._coord;
1896     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1897           it < sorted.end();
1898           it ++ )
1899     {
1900         Geom::Point dest((*it)._node->pos);
1901         dest[axis] = pos;
1902         sp_node_moveto((*it)._node, dest);
1903         pos += step;
1904     }
1906     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1910 /**
1911  * Call sp_nodepath_line_add_node() for all selected segments.
1912  */
1913 void
1914 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1916     if (!nodepath) {
1917         return;
1918     }
1920     GList *nl = NULL;
1922     int n_added = 0;
1924     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1925        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1926         g_assert(t->selected);
1927         if (t->p.other && t->p.other->selected) {
1928             nl = g_list_prepend(nl, t);
1929         }
1930     }
1932     while (nl) {
1933        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1934        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1935        sp_nodepath_node_select(n, TRUE, FALSE);
1936        n_added ++;
1937        nl = g_list_remove(nl, t);
1938     }
1940     /** \todo fixme: adjust ? */
1941     sp_nodepath_update_handles(nodepath);
1943     if (n_added > 1) {
1944         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1945     } else if (n_added > 0) {
1946         sp_nodepath_update_repr(nodepath, _("Add node"));
1947     }
1949     sp_nodepath_update_statusbar(nodepath);
1952 /**
1953  * Select segment nearest to point
1954  */
1955 void
1956 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p, bool toggle)
1958     if (!nodepath) {
1959         return;
1960     }
1962     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
1963     Geom::PathVector const &pathv = curve->get_pathvector();
1964     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
1965     if (!pvpos) {
1966         g_print ("Possible error?\n");
1967         return;
1968     }
1970     // calculate index for nodepath's representation.
1971     unsigned int segment_index = floor(pvpos->t) + 1;
1972     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
1973         segment_index += pathv[i].size() + 1;
1974         if (pathv[i].closed()) {
1975             segment_index += 1;
1976         }
1977     }
1979     curve->unref();
1981     //find segment to segment
1982     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
1984     //fixme: this can return NULL, so check before proceeding.
1985     g_return_if_fail(e != NULL);
1987     gboolean force = FALSE;
1988     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1989         force = TRUE;
1990     }
1991     sp_nodepath_node_select(e, (gboolean) toggle, force);
1992     if (e->p.other)
1993         sp_nodepath_node_select(e->p.other, TRUE, force);
1995     sp_nodepath_update_handles(nodepath);
1997     sp_nodepath_update_statusbar(nodepath);
2000 /**
2001  * Add a node nearest to point
2002  */
2003 void
2004 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p)
2006     if (!nodepath) {
2007         return;
2008     }
2010     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2011     Geom::PathVector const &pathv = curve->get_pathvector();
2012     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2013     if (!pvpos) {
2014         g_print ("Possible error?\n");
2015         return;
2016     }
2018     // calculate index for nodepath's representation.
2019     double int_part;
2020     double t = std::modf(pvpos->t, &int_part);
2021     unsigned int segment_index = (unsigned int)int_part + 1;
2022     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2023         segment_index += pathv[i].size() + 1;
2024         if (pathv[i].closed()) {
2025             segment_index += 1;
2026         }
2027     }
2029     curve->unref();
2031     //find segment to split
2032     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2034     //don't know why but t seems to flip for lines
2035     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2036         t = 1.0 - t;
2037     }
2039     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2040     sp_nodepath_node_select(n, FALSE, TRUE);
2042     /* fixme: adjust ? */
2043     sp_nodepath_update_handles(nodepath);
2045     sp_nodepath_update_repr(nodepath, _("Add node"));
2047     sp_nodepath_update_statusbar(nodepath);
2050 /*
2051  * Adjusts a segment so that t moves by a certain delta for dragging
2052  * converts lines to curves
2053  *
2054  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2055  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2056  */
2057 void
2058 sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, Geom::Point delta)
2060     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node);
2062     //fixme: e and e->p can be NULL, so check for those before proceeding
2063     g_return_if_fail(e != NULL);
2064     g_return_if_fail(&e->p != NULL);
2066     /* feel good is an arbitrary parameter that distributes the delta between handles
2067      * if t of the drag point is less than 1/6 distance form the endpoint only
2068      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2069      */
2070     double feel_good;
2071     if (t <= 1.0 / 6.0)
2072         feel_good = 0;
2073     else if (t <= 0.5)
2074         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2075     else if (t <= 5.0 / 6.0)
2076         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2077     else
2078         feel_good = 1;
2080     //if we're dragging a line convert it to a curve
2081     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2082         sp_nodepath_set_line_type(e, NR_CURVETO);
2083     }
2085     Geom::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2086     Geom::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2087     e->p.other->n.pos += offsetcoord0;
2088     e->p.pos += offsetcoord1;
2090     // adjust handles of adjacent nodes where necessary
2091     sp_node_adjust_handle(e,1);
2092     sp_node_adjust_handle(e->p.other,-1);
2094     sp_nodepath_update_handles(e->subpath->nodepath);
2096     update_object(e->subpath->nodepath);
2098     sp_nodepath_update_statusbar(e->subpath->nodepath);
2102 /**
2103  * Call sp_nodepath_break() for all selected segments.
2104  */
2105 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2107     if (!nodepath) return;
2109     GList *tempin = g_list_copy(nodepath->selected);
2110     GList *temp = NULL;
2111     for (GList *l = tempin; l != NULL; l = l->next) {
2112        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2113        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2114         if (nn == NULL) continue; // no break, no new node
2115         temp = g_list_prepend(temp, nn);
2116     }
2117     g_list_free(tempin);
2119     if (temp) {
2120         sp_nodepath_deselect(nodepath);
2121     }
2122     for (GList *l = temp; l != NULL; l = l->next) {
2123         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2124     }
2126     sp_nodepath_update_handles(nodepath);
2128     sp_nodepath_update_repr(nodepath, _("Break path"));
2131 /**
2132  * Duplicate the selected node(s).
2133  */
2134 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2136     if (!nodepath) {
2137         return;
2138     }
2140     GList *temp = NULL;
2141     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2142        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2143        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2144         if (nn == NULL) continue; // could not duplicate
2145         temp = g_list_prepend(temp, nn);
2146     }
2148     if (temp) {
2149         sp_nodepath_deselect(nodepath);
2150     }
2151     for (GList *l = temp; l != NULL; l = l->next) {
2152         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2153     }
2155     sp_nodepath_update_handles(nodepath);
2157     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2160 /**
2161  *  Internal function to join two nodes by merging them into one.
2162  */
2163 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2165     /* a and b are endpoints */
2167     // if one of the two nodes is mouseovered, fix its position
2168     Geom::Point c;
2169     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2170         c = a->pos;
2171     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2172         c = b->pos;
2173     } else {
2174         // otherwise, move joined node to the midpoint
2175         c = (a->pos + b->pos) / 2;
2176     }
2178     if (a->subpath == b->subpath) {
2179        Inkscape::NodePath::SubPath *sp = a->subpath;
2180         sp_nodepath_subpath_close(sp);
2181         sp_node_moveto (sp->first, c);
2183         sp_nodepath_update_handles(sp->nodepath);
2184         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2185         return;
2186     }
2188     /* a and b are separate subpaths */
2189     Inkscape::NodePath::SubPath *sa = a->subpath;
2190     Inkscape::NodePath::SubPath *sb = b->subpath;
2191     Geom::Point p;
2192     Inkscape::NodePath::Node *n;
2193     NRPathcode code;
2194     if (a == sa->first) {
2195         // we will now reverse sa, so that a is its last node, not first, and drop that node
2196         p = sa->first->n.pos;
2197         code = (NRPathcode)sa->first->n.other->code;
2198         // create new subpath
2199        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2200        // create a first moveto node on it
2201         n = sa->last;
2202         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2203         n = n->p.other;
2204         if (n == sa->first) n = NULL;
2205         while (n) {
2206             // copy the rest of the nodes from sa to t, going backwards
2207             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2208             n = n->p.other;
2209             if (n == sa->first) n = NULL;
2210         }
2211         // replace sa with t
2212         sp_nodepath_subpath_destroy(sa);
2213         sa = t;
2214     } else if (a == sa->last) {
2215         // a is already last, just drop it
2216         p = sa->last->p.pos;
2217         code = (NRPathcode)sa->last->code;
2218         sp_nodepath_node_destroy(sa->last);
2219     } else {
2220         code = NR_END;
2221         g_assert_not_reached();
2222     }
2224     if (b == sb->first) {
2225         // copy all nodes from b to a, forward 
2226         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2227         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2228             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2229         }
2230     } else if (b == sb->last) {
2231         // copy all nodes from b to a, backward 
2232         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2233         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2234             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2235         }
2236     } else {
2237         g_assert_not_reached();
2238     }
2239     /* and now destroy sb */
2241     sp_nodepath_subpath_destroy(sb);
2243     sp_nodepath_update_handles(sa->nodepath);
2245     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2247     sp_nodepath_update_statusbar(nodepath);
2250 /**
2251  *  Internal function to join two nodes by adding a segment between them.
2252  */
2253 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2255     if (a->subpath == b->subpath) {
2256        Inkscape::NodePath::SubPath *sp = a->subpath;
2258         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2259         sp->closed = TRUE;
2261         sp->first->p.other = sp->last;
2262         sp->last->n.other  = sp->first;
2264         sp_node_handle_mirror_p_to_n(sp->last);
2265         sp_node_handle_mirror_n_to_p(sp->first);
2267         sp->first->code = sp->last->code;
2268         sp->first       = sp->last;
2270         sp_nodepath_update_handles(sp->nodepath);
2272         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2274         return;
2275     }
2277     /* a and b are separate subpaths */
2278     Inkscape::NodePath::SubPath *sa = a->subpath;
2279     Inkscape::NodePath::SubPath *sb = b->subpath;
2281     Inkscape::NodePath::Node *n;
2282     Geom::Point p;
2283     NRPathcode code;
2284     if (a == sa->first) {
2285         code = (NRPathcode) sa->first->n.other->code;
2286        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2287         n = sa->last;
2288         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2289         for (n = n->p.other; n != NULL; n = n->p.other) {
2290             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2291         }
2292         sp_nodepath_subpath_destroy(sa);
2293         sa = t;
2294     } else if (a == sa->last) {
2295         code = (NRPathcode)sa->last->code;
2296     } else {
2297         code = NR_END;
2298         g_assert_not_reached();
2299     }
2301     if (b == sb->first) {
2302         n = sb->first;
2303         sp_node_handle_mirror_p_to_n(sa->last);
2304         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2305         sp_node_handle_mirror_n_to_p(sa->last);
2306         for (n = n->n.other; n != NULL; n = n->n.other) {
2307             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2308         }
2309     } else if (b == sb->last) {
2310         n = sb->last;
2311         sp_node_handle_mirror_p_to_n(sa->last);
2312         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2313         sp_node_handle_mirror_n_to_p(sa->last);
2314         for (n = n->p.other; n != NULL; n = n->p.other) {
2315             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2316         }
2317     } else {
2318         g_assert_not_reached();
2319     }
2320     /* and now destroy sb */
2322     sp_nodepath_subpath_destroy(sb);
2324     sp_nodepath_update_handles(sa->nodepath);
2326     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2329 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2331 /**
2332  * Internal function to handle joining two nodes.
2333  */
2334 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2336     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2338     if (g_list_length(nodepath->selected) != 2) {
2339         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2340         return;
2341     }
2343     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2344     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2346     g_assert(a != b);
2347     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2348         // someone tried to join an orphan node (i.e. a single-node subpath).
2349         // this is not worth an error message, just fail silently.
2350         return;
2351     }
2353     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2354         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2355         return;
2356     }
2358     switch(mode) {
2359         case NODE_JOIN_ENDPOINTS:
2360             do_node_selected_join(nodepath, a, b);
2361             break;
2362         case NODE_JOIN_SEGMENT:
2363             do_node_selected_join_segment(nodepath, a, b);
2364             break;
2365     }
2368 /**
2369  *  Join two nodes by merging them into one.
2370  */
2371 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2373     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2376 /**
2377  *  Join two nodes by adding a segment between them.
2378  */
2379 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2381     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2384 /**
2385  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2386  */
2387 void sp_node_delete_preserve(GList *nodes_to_delete)
2389     GSList *nodepaths = NULL;
2391     while (nodes_to_delete) {
2392         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2393         Inkscape::NodePath::SubPath *sp = node->subpath;
2394         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2395         Inkscape::NodePath::Node *sample_cursor = NULL;
2396         Inkscape::NodePath::Node *sample_end = NULL;
2397         Inkscape::NodePath::Node *delete_cursor = node;
2398         bool just_delete = false;
2400         //find the start of this contiguous selection
2401         //move left to the first node that is not selected
2402         //or the start of the non-closed path
2403         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2404             delete_cursor = curr;
2405         }
2407         //just delete at the beginning of an open path
2408         if (!delete_cursor->p.other) {
2409             sample_cursor = delete_cursor;
2410             just_delete = true;
2411         } else {
2412             sample_cursor = delete_cursor->p.other;
2413         }
2415         //calculate points for each segment
2416         int rate = 5;
2417         float period = 1.0 / rate;
2418         std::vector<Geom::Point> data;
2419         if (!just_delete) {
2420             data.push_back(sample_cursor->pos);
2421             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2422                 //just delete at the end of an open path
2423                 if (!sp->closed && curr == sp->last) {
2424                     just_delete = true;
2425                     break;
2426                 }
2428                 //sample points on the contiguous selected segment
2429                 Geom::Point *bez;
2430                 bez = new Geom::Point [4];
2431                 bez[0] = curr->pos;
2432                 bez[1] = curr->n.pos;
2433                 bez[2] = curr->n.other->p.pos;
2434                 bez[3] = curr->n.other->pos;
2435                 for (int i=1; i<rate; i++) {
2436                     gdouble t = i * period;
2437                     Geom::Point p = bezier_pt(3, bez, t);
2438                     data.push_back(p);
2439                 }
2440                 data.push_back(curr->n.other->pos);
2442                 sample_end = curr->n.other;
2443                 //break if we've come full circle or hit the end of the selection
2444                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2445                     break;
2446                 }
2447             }
2448         }
2450         if (!just_delete) {
2451             //calculate the best fitting single segment and adjust the endpoints
2452             Geom::Point *adata;
2453             adata = new Geom::Point [data.size()];
2454             copy(data.begin(), data.end(), adata);
2456             Geom::Point *bez;
2457             bez = new Geom::Point [4];
2458             //would decreasing error create a better fitting approximation?
2459             gdouble error = 1.0;
2460             gint ret;
2461             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2463             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2464             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2465             //the resulting nodes behave as expected.
2466             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2467                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2468             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2469                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2471             //adjust endpoints
2472             sample_cursor->n.pos = bez[1];
2473             sample_end->p.pos = bez[2];
2474         }
2476         //destroy this contiguous selection
2477         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2478             Inkscape::NodePath::Node *temp = delete_cursor;
2479             if (delete_cursor->n.other == delete_cursor) {
2480                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2481                 delete_cursor = NULL;
2482             } else {
2483                 delete_cursor = delete_cursor->n.other;
2484             }
2485             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2486             sp_nodepath_node_destroy(temp);
2487         }
2489         sp_nodepath_update_handles(nodepath);
2491         if (!g_slist_find(nodepaths, nodepath))
2492             nodepaths = g_slist_prepend (nodepaths, nodepath);
2493     }
2495     for (GSList *i = nodepaths; i; i = i->next) {
2496         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2497         // different nodepaths will give us one undo event per nodepath
2498         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2500         // if the entire nodepath is removed, delete the selected object.
2501         if (nodepath->subpaths == NULL ||
2502             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2503             //at least 2
2504             sp_nodepath_get_node_count(nodepath) < 2) {
2505             SPDocument *document = sp_desktop_document (nodepath->desktop);
2506             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2507             //delete this nodepath's object, not the entire selection! (though at this time, this
2508             //does not matter)
2509             sp_selection_delete(nodepath->desktop);
2510             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2511                               _("Delete nodes"));
2512         } else {
2513             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2514             sp_nodepath_update_statusbar(nodepath);
2515         }
2516     }
2518     g_slist_free (nodepaths);
2521 /**
2522  * Delete one or more selected nodes.
2523  */
2524 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2526     if (!nodepath) return;
2527     if (!nodepath->selected) return;
2529     /** \todo fixme: do it the right way */
2530     while (nodepath->selected) {
2531        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2532         sp_nodepath_node_destroy(node);
2533     }
2536     //clean up the nodepath (such as for trivial subpaths)
2537     sp_nodepath_cleanup(nodepath);
2539     sp_nodepath_update_handles(nodepath);
2541     // if the entire nodepath is removed, delete the selected object.
2542     if (nodepath->subpaths == NULL ||
2543         sp_nodepath_get_node_count(nodepath) < 2) {
2544         SPDocument *document = sp_desktop_document (nodepath->desktop);
2545         sp_selection_delete(nodepath->desktop);
2546         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2547                           _("Delete nodes"));
2548         return;
2549     }
2551     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2553     sp_nodepath_update_statusbar(nodepath);
2556 /**
2557  * Delete one or more segments between two selected nodes.
2558  * This is the code for 'split'.
2559  */
2560 void
2561 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2563    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2564    Inkscape::NodePath::Node *curr, *next;     //Iterators
2566     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2568     if (g_list_length(nodepath->selected) != 2) {
2569         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2570                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2571         return;
2572     }
2574     //Selected nodes, not inclusive
2575    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2576    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2578     if ( ( a==b)                       ||  //same node
2579          (a->subpath  != b->subpath )  ||  //not the same path
2580          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2581          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2582     {
2583         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2584                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2585         return;
2586     }
2588     //###########################################
2589     //# BEGIN EDITS
2590     //###########################################
2591     //##################################
2592     //# CLOSED PATH
2593     //##################################
2594     if (a->subpath->closed) {
2597         gboolean reversed = FALSE;
2599         //Since we can go in a circle, we need to find the shorter distance.
2600         //  a->b or b->a
2601         start = end = NULL;
2602         int distance    = 0;
2603         int minDistance = 0;
2604         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2605             if (curr==b) {
2606                 //printf("a to b:%d\n", distance);
2607                 start = a;//go from a to b
2608                 end   = b;
2609                 minDistance = distance;
2610                 //printf("A to B :\n");
2611                 break;
2612             }
2613             distance++;
2614         }
2616         //try again, the other direction
2617         distance = 0;
2618         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2619             if (curr==a) {
2620                 //printf("b to a:%d\n", distance);
2621                 if (distance < minDistance) {
2622                     start    = b;  //we go from b to a
2623                     end      = a;
2624                     reversed = TRUE;
2625                     //printf("B to A\n");
2626                 }
2627                 break;
2628             }
2629             distance++;
2630         }
2633         //Copy everything from 'end' to 'start' to a new subpath
2634        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2635         for (curr=end ; curr ; curr=curr->n.other) {
2636             NRPathcode code = (NRPathcode) curr->code;
2637             if (curr == end)
2638                 code = NR_MOVETO;
2639             sp_nodepath_node_new(t, NULL,
2640                                  (Inkscape::NodePath::NodeType)curr->type, code,
2641                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2642             if (curr == start)
2643                 break;
2644         }
2645         sp_nodepath_subpath_destroy(a->subpath);
2648     }
2652     //##################################
2653     //# OPEN PATH
2654     //##################################
2655     else {
2657         //We need to get the direction of the list between A and B
2658         //Can we walk from a to b?
2659         start = end = NULL;
2660         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2661             if (curr==b) {
2662                 start = a;  //did it!  we go from a to b
2663                 end   = b;
2664                 //printf("A to B\n");
2665                 break;
2666             }
2667         }
2668         if (!start) {//didn't work?  let's try the other direction
2669             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2670                 if (curr==a) {
2671                     start = b;  //did it!  we go from b to a
2672                     end   = a;
2673                     //printf("B to A\n");
2674                     break;
2675                 }
2676             }
2677         }
2678         if (!start) {
2679             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2680                                                      _("Cannot find path between nodes."));
2681             return;
2682         }
2686         //Copy everything after 'end' to a new subpath
2687        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2688         for (curr=end ; curr ; curr=curr->n.other) {
2689             NRPathcode code = (NRPathcode) curr->code;
2690             if (curr == end)
2691                 code = NR_MOVETO;
2692             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2693                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2694         }
2696         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2697         for (curr = start->n.other ; curr  ; curr=next) {
2698             next = curr->n.other;
2699             sp_nodepath_node_destroy(curr);
2700         }
2702     }
2703     //###########################################
2704     //# END EDITS
2705     //###########################################
2707     //clean up the nodepath (such as for trivial subpaths)
2708     sp_nodepath_cleanup(nodepath);
2710     sp_nodepath_update_handles(nodepath);
2712     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2714     sp_nodepath_update_statusbar(nodepath);
2717 /**
2718  * Call sp_nodepath_set_line() for all selected segments.
2719  */
2720 void
2721 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2723     if (nodepath == NULL) return;
2725     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2726        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2727         g_assert(n->selected);
2728         if (n->p.other && n->p.other->selected) {
2729             sp_nodepath_set_line_type(n, code);
2730         }
2731     }
2733     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2736 /**
2737  * Call sp_nodepath_convert_node_type() for all selected nodes.
2738  */
2739 void
2740 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2742     if (nodepath == NULL) return;
2744     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2746     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2747         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2748     }
2750     sp_nodepath_update_repr(nodepath, _("Change node type"));
2753 /**
2754  * Change select status of node, update its own and neighbour handles.
2755  */
2756 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2758     node->selected = selected;
2760     if (selected) {
2761         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2762         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2763         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2764         sp_knot_update_ctrl(node->knot);
2765     } else {
2766         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2767         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2768         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2769         sp_knot_update_ctrl(node->knot);
2770     }
2772     sp_node_update_handles(node);
2773     if (node->n.other) sp_node_update_handles(node->n.other);
2774     if (node->p.other) sp_node_update_handles(node->p.other);
2777 /**
2778 \brief Select a node
2779 \param node     The node to select
2780 \param incremental   If true, add to selection, otherwise deselect others
2781 \param override   If true, always select this node, otherwise toggle selected status
2782 */
2783 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2785     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2787     if (incremental) {
2788         if (override) {
2789             if (!g_list_find(nodepath->selected, node)) {
2790                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2791             }
2792             sp_node_set_selected(node, TRUE);
2793         } else { // toggle
2794             if (node->selected) {
2795                 g_assert(g_list_find(nodepath->selected, node));
2796                 nodepath->selected = g_list_remove(nodepath->selected, node);
2797             } else {
2798                 g_assert(!g_list_find(nodepath->selected, node));
2799                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2800             }
2801             sp_node_set_selected(node, !node->selected);
2802         }
2803     } else {
2804         sp_nodepath_deselect(nodepath);
2805         nodepath->selected = g_list_prepend(nodepath->selected, node);
2806         sp_node_set_selected(node, TRUE);
2807     }
2809     sp_nodepath_update_statusbar(nodepath);
2813 /**
2814 \brief Deselect all nodes in the nodepath
2815 */
2816 void
2817 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2819     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2821     while (nodepath->selected) {
2822         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2823         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2824     }
2825     sp_nodepath_update_statusbar(nodepath);
2828 /**
2829 \brief Select or invert selection of all nodes in the nodepath
2830 */
2831 void
2832 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2834     if (!nodepath) return;
2836     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2837        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2838         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2839            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2840            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2841         }
2842     }
2845 /**
2846  * If nothing selected, does the same as sp_nodepath_select_all();
2847  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2848  * (i.e., similar to "select all in layer", with the "selected" subpaths
2849  * being treated as "layers" in the path).
2850  */
2851 void
2852 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2854     if (!nodepath) return;
2856     if (g_list_length (nodepath->selected) == 0) {
2857         sp_nodepath_select_all (nodepath, invert);
2858         return;
2859     }
2861     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2862     GSList *subpaths = NULL;
2864     for (GList *l = copy; l != NULL; l = l->next) {
2865         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2866         Inkscape::NodePath::SubPath *subpath = n->subpath;
2867         if (!g_slist_find (subpaths, subpath))
2868             subpaths = g_slist_prepend (subpaths, subpath);
2869     }
2871     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2872         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2873         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2874             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2875             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2876         }
2877     }
2879     g_slist_free (subpaths);
2880     g_list_free (copy);
2883 /**
2884  * \brief Select the node after the last selected; if none is selected,
2885  * select the first within path.
2886  */
2887 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2889     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2891    Inkscape::NodePath::Node *last = NULL;
2892     if (nodepath->selected) {
2893         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2894            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2895             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2896             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2897                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2898                 if (node->selected) {
2899                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2900                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2901                             if (spl->next) { // there's a next subpath
2902                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2903                                 last = subpath_next->first;
2904                             } else if (spl->prev) { // there's a previous subpath
2905                                 last = NULL; // to be set later to the first node of first subpath
2906                             } else {
2907                                 last = node->n.other;
2908                             }
2909                         } else {
2910                             last = node->n.other;
2911                         }
2912                     } else {
2913                         if (node->n.other) {
2914                             last = node->n.other;
2915                         } else {
2916                             if (spl->next) { // there's a next subpath
2917                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2918                                 last = subpath_next->first;
2919                             } else if (spl->prev) { // there's a previous subpath
2920                                 last = NULL; // to be set later to the first node of first subpath
2921                             } else {
2922                                 last = (Inkscape::NodePath::Node *) subpath->first;
2923                             }
2924                         }
2925                     }
2926                 }
2927             }
2928         }
2929         sp_nodepath_deselect(nodepath);
2930     }
2932     if (last) { // there's at least one more node after selected
2933         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2934     } else { // no more nodes, select the first one in first subpath
2935        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2936         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2937     }
2940 /**
2941  * \brief Select the node before the first selected; if none is selected,
2942  * select the last within path
2943  */
2944 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2946     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2948    Inkscape::NodePath::Node *last = NULL;
2949     if (nodepath->selected) {
2950         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2951            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2952             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2953                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2954                 if (node->selected) {
2955                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2956                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2957                             if (spl->prev) { // there's a prev subpath
2958                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2959                                 last = subpath_prev->last;
2960                             } else if (spl->next) { // there's a next subpath
2961                                 last = NULL; // to be set later to the last node of last subpath
2962                             } else {
2963                                 last = node->p.other;
2964                             }
2965                         } else {
2966                             last = node->p.other;
2967                         }
2968                     } else {
2969                         if (node->p.other) {
2970                             last = node->p.other;
2971                         } else {
2972                             if (spl->prev) { // there's a prev subpath
2973                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2974                                 last = subpath_prev->last;
2975                             } else if (spl->next) { // there's a next subpath
2976                                 last = NULL; // to be set later to the last node of last subpath
2977                             } else {
2978                                 last = (Inkscape::NodePath::Node *) subpath->last;
2979                             }
2980                         }
2981                     }
2982                 }
2983             }
2984         }
2985         sp_nodepath_deselect(nodepath);
2986     }
2988     if (last) { // there's at least one more node before selected
2989         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2990     } else { // no more nodes, select the last one in last subpath
2991         GList *spl = g_list_last(nodepath->subpaths);
2992        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2993         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2994     }
2997 /**
2998  * \brief Select all nodes that are within the rectangle.
2999  */
3000 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, Geom::Rect const &b, gboolean incremental)
3002     if (!incremental) {
3003         sp_nodepath_deselect(nodepath);
3004     }
3006     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3007        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3008         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3009            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3011             if (b.contains(node->pos)) {
3012                 sp_nodepath_node_select(node, TRUE, TRUE);
3013             }
3014         }
3015     }
3019 void
3020 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3022     g_assert (n);
3023     g_assert (nodepath);
3024     g_assert (n->subpath->nodepath == nodepath);
3026     if (g_list_length (nodepath->selected) == 0) {
3027         if (grow > 0) {
3028             sp_nodepath_node_select(n, TRUE, TRUE);
3029         }
3030         return;
3031     }
3033     if (g_list_length (nodepath->selected) == 1) {
3034         if (grow < 0) {
3035             sp_nodepath_deselect (nodepath);
3036             return;
3037         }
3038     }
3040         double n_sel_range = 0, p_sel_range = 0;
3041             Inkscape::NodePath::Node *farthest_n_node = n;
3042             Inkscape::NodePath::Node *farthest_p_node = n;
3044         // Calculate ranges
3045         {
3046             double n_range = 0, p_range = 0;
3047             bool n_going = true, p_going = true;
3048             Inkscape::NodePath::Node *n_node = n;
3049             Inkscape::NodePath::Node *p_node = n;
3050             do {
3051                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3052                 if (n_node && n_going)
3053                     n_node = n_node->n.other;
3054                 if (n_node == NULL) {
3055                     n_going = false;
3056                 } else {
3057                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3058                     if (n_node->selected) {
3059                         n_sel_range = n_range;
3060                         farthest_n_node = n_node;
3061                     }
3062                     if (n_node == p_node) {
3063                         n_going = false;
3064                         p_going = false;
3065                     }
3066                 }
3067                 if (p_node && p_going)
3068                     p_node = p_node->p.other;
3069                 if (p_node == NULL) {
3070                     p_going = false;
3071                 } else {
3072                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3073                     if (p_node->selected) {
3074                         p_sel_range = p_range;
3075                         farthest_p_node = p_node;
3076                     }
3077                     if (p_node == n_node) {
3078                         n_going = false;
3079                         p_going = false;
3080                     }
3081                 }
3082             } while (n_going || p_going);
3083         }
3085     if (grow > 0) {
3086         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3087                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3088         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3089                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3090         }
3091     } else {
3092         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3093                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3094         } else if (farthest_p_node && farthest_p_node->selected) {
3095                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3096         }
3097     }
3100 void
3101 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3103     g_assert (n);
3104     g_assert (nodepath);
3105     g_assert (n->subpath->nodepath == nodepath);
3107     if (g_list_length (nodepath->selected) == 0) {
3108         if (grow > 0) {
3109             sp_nodepath_node_select(n, TRUE, TRUE);
3110         }
3111         return;
3112     }
3114     if (g_list_length (nodepath->selected) == 1) {
3115         if (grow < 0) {
3116             sp_nodepath_deselect (nodepath);
3117             return;
3118         }
3119     }
3121     Inkscape::NodePath::Node *farthest_selected = NULL;
3122     double farthest_dist = 0;
3124     Inkscape::NodePath::Node *closest_unselected = NULL;
3125     double closest_dist = NR_HUGE;
3127     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3128        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3129         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3130            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3131            if (node == n)
3132                continue;
3133            if (node->selected) {
3134                if (Geom::L2(node->pos - n->pos) > farthest_dist) {
3135                    farthest_dist = Geom::L2(node->pos - n->pos);
3136                    farthest_selected = node;
3137                }
3138            } else {
3139                if (Geom::L2(node->pos - n->pos) < closest_dist) {
3140                    closest_dist = Geom::L2(node->pos - n->pos);
3141                    closest_unselected = node;
3142                }
3143            }
3144         }
3145     }
3147     if (grow > 0) {
3148         if (closest_unselected) {
3149             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3150         }
3151     } else {
3152         if (farthest_selected) {
3153             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3154         }
3155     }
3159 /**
3160 \brief  Saves all nodes' and handles' current positions in their origin members
3161 */
3162 void
3163 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3165     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3166        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3167         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3168            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3169            n->origin = n->pos;
3170            n->p.origin = n->p.pos;
3171            n->n.origin = n->n.pos;
3172         }
3173     }
3176 /**
3177 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3178 */
3179 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3181     GList *r = NULL;
3182     if (nodepath->selected) {
3183         guint i = 0;
3184         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3185             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3186             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3187                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3188                 i++;
3189                 if (node->selected) {
3190                     r = g_list_append(r, GINT_TO_POINTER(i));
3191                 }
3192             }
3193         }
3194     }
3195     return r;
3198 /**
3199 \brief  Restores selection by selecting nodes whose positions are in the list
3200 */
3201 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3203     sp_nodepath_deselect(nodepath);
3205     guint i = 0;
3206     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3207        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3208         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3209            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3210             i++;
3211             if (g_list_find(r, GINT_TO_POINTER(i))) {
3212                 sp_nodepath_node_select(node, TRUE, TRUE);
3213             }
3214         }
3215     }
3219 /**
3220 \brief Adjusts handle according to node type and line code.
3221 */
3222 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3224     g_assert(node);
3226    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3227    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3229    // nothing to do if we are an end node
3230     if (me->other == NULL) return;
3231     if (other->other == NULL) return;
3233     // nothing to do if we are a cusp node
3234     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3236     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3237     NRPathcode mecode;
3238     if (which_adjust == 1) {
3239         mecode = (NRPathcode)me->other->code;
3240     } else {
3241         mecode = (NRPathcode)node->code;
3242     }
3243     if (mecode == NR_LINETO) return;
3245     if (sp_node_side_is_line(node, other)) {
3246         // other is a line, and we are either smooth or symm
3247        Inkscape::NodePath::Node *othernode = other->other;
3248         double len = Geom::L2(me->pos - node->pos);
3249         Geom::Point delta = node->pos - othernode->pos;
3250         double linelen = Geom::L2(delta);
3251         if (linelen < 1e-18)
3252             return;
3253         me->pos = node->pos + (len / linelen)*delta;
3254         return;
3255     }
3257     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3258         // symmetrize 
3259         me->pos = 2 * node->pos - other->pos;
3260         return;
3261     } else {
3262         // smoothify
3263         double len = Geom::L2(me->pos - node->pos);
3264         Geom::Point delta = other->pos - node->pos;
3265         double otherlen = Geom::L2(delta);
3266         if (otherlen < 1e-18) return;
3267         me->pos = node->pos - (len / otherlen) * delta;
3268     }
3271 /**
3272  \brief Adjusts both handles according to node type and line code
3273  */
3274 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3276     g_assert(node);
3278     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3280     /* we are either smooth or symm */
3282     if (node->p.other == NULL) return;
3283     if (node->n.other == NULL) return;
3285     if (sp_node_side_is_line(node, &node->p)) {
3286         sp_node_adjust_handle(node, 1);
3287         return;
3288     }
3290     if (sp_node_side_is_line(node, &node->n)) {
3291         sp_node_adjust_handle(node, -1);
3292         return;
3293     }
3295     /* both are curves */
3296     Geom::Point const delta( node->n.pos - node->p.pos );
3298     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3299         node->p.pos = node->pos - delta / 2;
3300         node->n.pos = node->pos + delta / 2;
3301         return;
3302     }
3304     /* We are smooth */
3305     double plen = Geom::L2(node->p.pos - node->pos);
3306     if (plen < 1e-18) return;
3307     double nlen = Geom::L2(node->n.pos - node->pos);
3308     if (nlen < 1e-18) return;
3309     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3310     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3313 /**
3314  * Node event callback.
3315  */
3316 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3318     gboolean ret = FALSE;
3319     switch (event->type) {
3320         case GDK_ENTER_NOTIFY:
3321             Inkscape::NodePath::Path::active_node = n;
3322             break;
3323         case GDK_LEAVE_NOTIFY:
3324             Inkscape::NodePath::Path::active_node = NULL;
3325             break;
3326         case GDK_SCROLL:
3327             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3328                 switch (event->scroll.direction) {
3329                     case GDK_SCROLL_UP:
3330                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3331                         break;
3332                     case GDK_SCROLL_DOWN:
3333                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3334                         break;
3335                     default:
3336                         break;
3337                 }
3338                 ret = TRUE;
3339             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3340                 switch (event->scroll.direction) {
3341                     case GDK_SCROLL_UP:
3342                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3343                         break;
3344                     case GDK_SCROLL_DOWN:
3345                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3346                         break;
3347                     default:
3348                         break;
3349                 }
3350                 ret = TRUE;
3351             }
3352             break;
3353         case GDK_KEY_PRESS:
3354             switch (get_group0_keyval (&event->key)) {
3355                 case GDK_space:
3356                     if (event->key.state & GDK_BUTTON1_MASK) {
3357                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3358                         stamp_repr(nodepath);
3359                         ret = TRUE;
3360                     }
3361                     break;
3362                 case GDK_Page_Up:
3363                     if (event->key.state & GDK_CONTROL_MASK) {
3364                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3365                     } else {
3366                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3367                     }
3368                     break;
3369                 case GDK_Page_Down:
3370                     if (event->key.state & GDK_CONTROL_MASK) {
3371                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3372                     } else {
3373                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3374                     }
3375                     break;
3376                 default:
3377                     break;
3378             }
3379             break;
3380         default:
3381             break;
3382     }
3384     return ret;
3387 /**
3388  * Handle keypress on node; directly called.
3389  */
3390 gboolean node_key(GdkEvent *event)
3392     Inkscape::NodePath::Path *np;
3394     // there is no way to verify nodes so set active_node to nil when deleting!!
3395     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3397     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3398         gint ret = FALSE;
3399         switch (get_group0_keyval (&event->key)) {
3400             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3401             case GDK_BackSpace:
3402                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3403                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3404                 sp_nodepath_update_repr(np, _("Delete node"));
3405                 Inkscape::NodePath::Path::active_node = NULL;
3406                 ret = TRUE;
3407                 break;
3408             case GDK_c:
3409                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3410                 ret = TRUE;
3411                 break;
3412             case GDK_s:
3413                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3414                 ret = TRUE;
3415                 break;
3416             case GDK_y:
3417                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3418                 ret = TRUE;
3419                 break;
3420             case GDK_b:
3421                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3422                 ret = TRUE;
3423                 break;
3424         }
3425         return ret;
3426     }
3427     return FALSE;
3430 /**
3431  * Mouseclick on node callback.
3432  */
3433 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3435    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3437     if (state & GDK_CONTROL_MASK) {
3438         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3440         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3441             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3442                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3443             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3444                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3445             } else {
3446                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3447             }
3448             sp_nodepath_update_repr(nodepath, _("Change node type"));
3449             sp_nodepath_update_statusbar(nodepath);
3451         } else { //ctrl+alt+click: delete node
3452             GList *node_to_delete = NULL;
3453             node_to_delete = g_list_append(node_to_delete, n);
3454             sp_node_delete_preserve(node_to_delete);
3455         }
3457     } else {
3458         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3459     }
3462 /**
3463  * Mouse grabbed node callback.
3464  */
3465 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3467    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3469     if (!n->selected) {
3470         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3471     }
3473     n->is_dragging = true;
3474     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3476     sp_nodepath_remember_origins (n->subpath->nodepath);
3479 /**
3480  * Mouse ungrabbed node callback.
3481  */
3482 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3484    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3486    n->dragging_out = NULL;
3487    n->is_dragging = false;
3488    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3490    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3493 /**
3494  * The point on a line, given by its angle, closest to the given point.
3495  * \param p  A point.
3496  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3497  * \param closest  Pointer to the point struct where the result is stored.
3498  * \todo FIXME: use dot product perhaps?
3499  */
3500 static void point_line_closest(Geom::Point *p, double a, Geom::Point *closest)
3502     if (a == HUGE_VAL) { // vertical
3503         *closest = Geom::Point(0, (*p)[Geom::Y]);
3504     } else {
3505         (*closest)[Geom::X] = ( a * (*p)[Geom::Y] + (*p)[Geom::X]) / (a*a + 1);
3506         (*closest)[Geom::Y] = a * (*closest)[Geom::X];
3507     }
3510 /**
3511  * Distance from the point to a line given by its angle.
3512  * \param p  A point.
3513  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3514  */
3515 static double point_line_distance(Geom::Point *p, double a)
3517     Geom::Point c;
3518     point_line_closest(p, a, &c);
3519     return sqrt(((*p)[Geom::X] - c[Geom::X])*((*p)[Geom::X] - c[Geom::X]) + ((*p)[Geom::Y] - c[Geom::Y])*((*p)[Geom::Y] - c[Geom::Y]));
3522 /**
3523  * Callback for node "request" signal.
3524  * \todo fixme: This goes to "moved" event? (lauris)
3525  */
3526 static gboolean
3527 node_request(SPKnot */*knot*/, Geom::Point *p, guint state, gpointer data)
3529     double yn, xn, yp, xp;
3530     double an, ap, na, pa;
3531     double d_an, d_ap, d_na, d_pa;
3532     gboolean collinear = FALSE;
3533     Geom::Point c;
3534     Geom::Point pr;
3536     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3538     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3540     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3541     if ( (!n->subpath->nodepath->straight_path) &&
3542          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3543            || n->dragging_out ) )
3544     {
3545        Geom::Point mouse = (*p);
3547        if (!n->dragging_out) {
3548            // This is the first drag-out event; find out which handle to drag out
3549            double appr_n = (n->n.other ? Geom::L2(n->n.other->pos - n->pos) - Geom::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3550            double appr_p = (n->p.other ? Geom::L2(n->p.other->pos - n->pos) - Geom::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3552            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3553                return FALSE;
3555            Inkscape::NodePath::NodeSide *opposite;
3556            if (appr_p > appr_n) { // closer to p
3557                n->dragging_out = &n->p;
3558                opposite = &n->n;
3559                n->code = NR_CURVETO;
3560            } else if (appr_p < appr_n) { // closer to n
3561                n->dragging_out = &n->n;
3562                opposite = &n->p;
3563                n->n.other->code = NR_CURVETO;
3564            } else { // p and n nodes are the same
3565                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3566                    n->dragging_out = &n->p;
3567                    opposite = &n->n;
3568                    n->code = NR_CURVETO;
3569                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3570                    n->dragging_out = &n->n;
3571                    opposite = &n->p;
3572                    n->n.other->code = NR_CURVETO;
3573                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3574                    double appr_other_n = (n->n.other ? Geom::L2(n->n.other->n.pos - n->pos) - Geom::L2(n->n.other->n.pos - (*p)) : -HUGE_VAL);
3575                    double appr_other_p = (n->n.other ? Geom::L2(n->n.other->p.pos - n->pos) - Geom::L2(n->n.other->p.pos - (*p)) : -HUGE_VAL);
3576                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3577                        n->dragging_out = &n->n;
3578                        opposite = &n->p;
3579                        n->n.other->code = NR_CURVETO;
3580                    } else { // closer to other's n handle
3581                        n->dragging_out = &n->p;
3582                        opposite = &n->n;
3583                        n->code = NR_CURVETO;
3584                    }
3585                }
3586            }
3588            // if there's another handle, make sure the one we drag out starts parallel to it
3589            if (opposite->pos != n->pos) {
3590                mouse = n->pos - Geom::L2(mouse - n->pos) * Geom::unit_vector(opposite->pos - n->pos);
3591            }
3593            // knots might not be created yet!
3594            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3595            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3596        }
3598        // pass this on to the handle-moved callback
3599        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3600        sp_node_update_handles(n);
3601        return TRUE;
3602    }
3604     if (state & GDK_CONTROL_MASK) { // constrained motion
3606         // calculate relative distances of handles
3607         // n handle:
3608         yn = n->n.pos[Geom::Y] - n->pos[Geom::Y];
3609         xn = n->n.pos[Geom::X] - n->pos[Geom::X];
3610         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3611         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3612             if (n->n.other) { // if there is the next point
3613                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3614                     yn = n->n.other->origin[Geom::Y] - n->origin[Geom::Y]; // use origin because otherwise the direction will change as you drag
3615                     xn = n->n.other->origin[Geom::X] - n->origin[Geom::X];
3616             }
3617         }
3618         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3619         if (yn < 0) { xn = -xn; yn = -yn; }
3621         // p handle:
3622         yp = n->p.pos[Geom::Y] - n->pos[Geom::Y];
3623         xp = n->p.pos[Geom::X] - n->pos[Geom::X];
3624         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3625         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3626             if (n->p.other) {
3627                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3628                     yp = n->p.other->origin[Geom::Y] - n->origin[Geom::Y];
3629                     xp = n->p.other->origin[Geom::X] - n->origin[Geom::X];
3630             }
3631         }
3632         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3633         if (yp < 0) { xp = -xp; yp = -yp; }
3635         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3636             // sliding on handles, only if at least one of the handles is non-vertical
3637             // (otherwise it's the same as ctrl+drag anyway)
3639             // calculate angles of the handles
3640             if (xn == 0) {
3641                 if (yn == 0) { // no handle, consider it the continuation of the other one
3642                     an = 0;
3643                     collinear = TRUE;
3644                 }
3645                 else an = 0; // vertical; set the angle to horizontal
3646             } else an = yn/xn;
3648             if (xp == 0) {
3649                 if (yp == 0) { // no handle, consider it the continuation of the other one
3650                     ap = an;
3651                 }
3652                 else ap = 0; // vertical; set the angle to horizontal
3653             } else  ap = yp/xp;
3655             if (collinear) an = ap;
3657             // angles of the perpendiculars; HUGE_VAL means vertical
3658             if (an == 0) na = HUGE_VAL; else na = -1/an;
3659             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3661             // mouse point relative to the node's original pos
3662             pr = (*p) - n->origin;
3664             // distances to the four lines (two handles and two perpendiculars)
3665             d_an = point_line_distance(&pr, an);
3666             d_na = point_line_distance(&pr, na);
3667             d_ap = point_line_distance(&pr, ap);
3668             d_pa = point_line_distance(&pr, pa);
3670             // find out which line is the closest, save its closest point in c
3671             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3672                 point_line_closest(&pr, an, &c);
3673             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3674                 point_line_closest(&pr, ap, &c);
3675             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3676                 point_line_closest(&pr, na, &c);
3677             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3678                 point_line_closest(&pr, pa, &c);
3679             }
3681             // move the node to the closest point
3682             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3683                                             n->origin[Geom::X] + c[Geom::X] - n->pos[Geom::X],
3684                                             n->origin[Geom::Y] + c[Geom::Y] - n->pos[Geom::Y], 
3685                                             true);
3687         } else {  // constraining to hor/vert
3689             if (fabs((*p)[Geom::X] - n->origin[Geom::X]) > fabs((*p)[Geom::Y] - n->origin[Geom::Y])) { // snap to hor
3690                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3691                                                 (*p)[Geom::X] - n->pos[Geom::X], 
3692                                                 n->origin[Geom::Y] - n->pos[Geom::Y],
3693                                                 true, 
3694                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::X]));
3695             } else { // snap to vert
3696                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3697                                                 n->origin[Geom::X] - n->pos[Geom::X],
3698                                                 (*p)[Geom::Y] - n->pos[Geom::Y],
3699                                                 true,
3700                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::Y]));
3701             }
3702         }
3703     } else { // move freely
3704         if (n->is_dragging) {
3705             if (state & GDK_MOD1_MASK) { // sculpt
3706                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3707             } else {
3708                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3709                                             (*p)[Geom::X] - n->pos[Geom::X],
3710                                             (*p)[Geom::Y] - n->pos[Geom::Y],
3711                                             (state & GDK_SHIFT_MASK) == 0);
3712             }
3713         }
3714     }
3716     n->subpath->nodepath->desktop->scroll_to_point(*p);
3718     return TRUE;
3721 /**
3722  * Node handle clicked callback.
3723  */
3724 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3726    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3728     if (state & GDK_CONTROL_MASK) { // "delete" handle
3729         if (n->p.knot == knot) {
3730             n->p.pos = n->pos;
3731         } else if (n->n.knot == knot) {
3732             n->n.pos = n->pos;
3733         }
3734         sp_node_update_handles(n);
3735         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3736         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3737         sp_nodepath_update_statusbar(nodepath);
3739     } else { // just select or add to selection, depending in Shift
3740         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3741     }
3744 /**
3745  * Node handle grabbed callback.
3746  */
3747 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3749    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3751     if (!n->selected) {
3752         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3753     }
3755     // remember the origin point of the handle
3756     if (n->p.knot == knot) {
3757         n->p.origin_radial = n->p.pos - n->pos;
3758     } else if (n->n.knot == knot) {
3759         n->n.origin_radial = n->n.pos - n->pos;
3760     } else {
3761         g_assert_not_reached();
3762     }
3764     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3767 /**
3768  * Node handle ungrabbed callback.
3769  */
3770 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3772    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3774     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3775     if (n->p.knot == knot) {
3776         n->p.origin_radial.a = 0;
3777         sp_knot_set_position(knot, n->p.pos, state);
3778     } else if (n->n.knot == knot) {
3779         n->n.origin_radial.a = 0;
3780         sp_knot_set_position(knot, n->n.pos, state);
3781     } else {
3782         g_assert_not_reached();
3783     }
3785     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3788 /**
3789  * Node handle "request" signal callback.
3790  */
3791 static gboolean node_handle_request(SPKnot *knot, Geom::Point *p, guint state, gpointer data)
3793     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3795     Inkscape::NodePath::NodeSide *me, *opposite;
3796     gint which;
3797     if (n->p.knot == knot) {
3798         me = &n->p;
3799         opposite = &n->n;
3800         which = -1;
3801     } else if (n->n.knot == knot) {
3802         me = &n->n;
3803         opposite = &n->p;
3804         which = 1;
3805     } else {
3806         me = opposite = NULL;
3807         which = 0;
3808         g_assert_not_reached();
3809     }
3811     SPDesktop *desktop = n->subpath->nodepath->desktop;
3812     SnapManager &m = desktop->namedview->snap_manager;
3813     m.setup(desktop, true, n->subpath->nodepath->item);
3814     Inkscape::SnappedPoint s;
3815     
3816     if ((state & GDK_SHIFT_MASK) != 0) {
3817         // We will not try to snap when the shift-key is pressed
3818         // so remove the old snap indicator and don't wait for it to time-out  
3819         desktop->snapindicator->remove_snappoint();     
3820     }
3822     Inkscape::NodePath::Node *othernode = opposite->other;
3823     if (othernode) {
3824         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3825             /* We are smooth node adjacent with line */
3826             Geom::Point const delta = *p - n->pos;
3827             Geom::Coord const len = Geom::L2(delta);
3828             Inkscape::NodePath::Node *othernode = opposite->other;
3829             Geom::Point const ndelta = n->pos - othernode->pos;
3830             Geom::Coord const linelen = Geom::L2(ndelta);
3831             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3832                 Geom::Coord const scal = dot(delta, ndelta) / linelen;
3833                 (*p) = n->pos + (scal / linelen) * ndelta;
3834             }
3835             if ((state & GDK_SHIFT_MASK) == 0) {
3836                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(*p), Inkscape::Snapper::ConstraintLine(*p, ndelta));
3837             }
3838         } else {
3839                 if ((state & GDK_SHIFT_MASK) == 0) {
3840                         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(*p));
3841                 }
3842         }
3843     } else {
3844         if ((state & GDK_SHIFT_MASK) == 0) {
3845                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(*p));
3846         }
3847     }
3848     
3849     Geom::Point pt2g = *p;
3850     s.getPoint(pt2g);
3851     *p = pt2g;
3852     
3853     sp_node_adjust_handle(n, -which);
3855     return FALSE;
3858 /**
3859  * Node handle moved callback.
3860  */
3861 static void node_handle_moved(SPKnot *knot, Geom::Point *p, guint state, gpointer data)
3863    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3865    Inkscape::NodePath::NodeSide *me;
3866    Inkscape::NodePath::NodeSide *other;
3867     if (n->p.knot == knot) {
3868         me = &n->p;
3869         other = &n->n;
3870     } else if (n->n.knot == knot) {
3871         me = &n->n;
3872         other = &n->p;
3873     } else {
3874         me = NULL;
3875         other = NULL;
3876         g_assert_not_reached();
3877     }
3879     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3880     Radial rme(me->pos - n->pos);
3881     Radial rother(other->pos - n->pos);
3882     Radial rnew(*p - n->pos);
3884     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3885         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3886         /* 0 interpreted as "no snapping". */
3888         // 1. Snap to the closest PI/snaps angle, starting from zero.
3889         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3891         // 2. Snap to the original angle, its opposite and perpendiculars
3892         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3893             /* The closest PI/2 angle, starting from original angle */
3894             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3896             // Snap to the closest.
3897             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3898                        ? a_snapped
3899                        : a_ortho );
3900         }
3902         // 3. Snap to the angle of the opposite line, if any
3903         Inkscape::NodePath::Node *othernode = other->other;
3904         if (othernode) {
3905             Geom::Point other_to_snap(0,0);
3906             if (sp_node_side_is_line(n, other)) {
3907                 other_to_snap = othernode->pos - n->pos;
3908             } else {
3909                 other_to_snap = other->pos - n->pos;
3910             }
3911             if (Geom::L2(other_to_snap) > 1e-3) {
3912                 Radial rother_to_snap(other_to_snap);
3913                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
3914                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
3916                 // Snap to the closest.
3917                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
3918                        ? a_snapped
3919                        : a_oppo );
3920             }
3921         }
3923         rnew.a = a_snapped;
3924     }
3926     if (state & GDK_MOD1_MASK) {
3927         // lock handle length
3928         rnew.r = me->origin_radial.r;
3929     }
3931     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3932         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3933         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3934         rother.a += rnew.a - rme.a;
3935         other->pos = Geom::Point(rother) + n->pos;
3936         if (other->knot) {
3937             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3938             sp_knot_moveto(other->knot, other->pos);
3939         }
3940     }
3942     me->pos = Geom::Point(rnew) + n->pos;
3943     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3945     // move knot, but without emitting the signal:
3946     // we cannot emit a "moved" signal because we're now processing it
3947     sp_knot_moveto(me->knot, me->pos);
3949     update_object(n->subpath->nodepath);
3951     /* status text */
3952     SPDesktop *desktop = n->subpath->nodepath->desktop;
3953     if (!desktop) return;
3954     SPEventContext *ec = desktop->event_context;
3955     if (!ec) return;
3957     Inkscape::MessageContext *mc = get_message_context(ec);
3959     if (!mc) return;
3961     double degrees = 180 / M_PI * rnew.a;
3962     if (degrees > 180) degrees -= 360;
3963     if (degrees < -180) degrees += 360;
3964     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3965         degrees = angle_to_compass (degrees);
3967     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3969     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
3970          _("<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);
3972     g_string_free(length, TRUE);
3975 /**
3976  * Node handle event callback.
3977  */
3978 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3980     gboolean ret = FALSE;
3981     switch (event->type) {
3982         case GDK_KEY_PRESS:
3983             switch (get_group0_keyval (&event->key)) {
3984                 case GDK_space:
3985                     if (event->key.state & GDK_BUTTON1_MASK) {
3986                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3987                         stamp_repr(nodepath);
3988                         ret = TRUE;
3989                     }
3990                     break;
3991                 default:
3992                     break;
3993             }
3994             break;
3995         case GDK_ENTER_NOTIFY:
3996             // we use an experimentally determined threshold that seems to work fine
3997             if (Geom::L2(n->pos - knot->pos) < 0.75)
3998                 Inkscape::NodePath::Path::active_node = n;
3999             break;
4000         case GDK_LEAVE_NOTIFY:
4001             // we use an experimentally determined threshold that seems to work fine
4002             if (Geom::L2(n->pos - knot->pos) < 0.75)
4003                 Inkscape::NodePath::Path::active_node = NULL;
4004             break;
4005         default:
4006             break;
4007     }
4009     return ret;
4012 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
4013                                  Radial &rme, Radial &rother, gboolean const both)
4015     rme.a += angle;
4016     if ( both
4017          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4018          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4019     {
4020         rother.a += angle;
4021     }
4024 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
4025                                         Radial &rme, Radial &rother, gboolean const both)
4027     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
4029     gdouble r;
4030     if ( both
4031          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4032          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4033     {
4034         r = MAX(rme.r, rother.r);
4035     } else {
4036         r = rme.r;
4037     }
4039     gdouble const weird_angle = atan2(norm_angle, r);
4040 /* Bulia says norm_angle is just the visible distance that the
4041  * object's end must travel on the screen.  Left as 'angle' for want of
4042  * a better name.*/
4044     rme.a += weird_angle;
4045     if ( both
4046          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4047          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4048     {
4049         rother.a += weird_angle;
4050     }
4053 /**
4054  * Rotate one node.
4055  */
4056 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4058     Inkscape::NodePath::NodeSide *me, *other;
4059     bool both = false;
4061     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4062     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4064     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4065         me = &(n->p);
4066         other = &(n->n);
4067     } else if (!n->p.other) {
4068         me = &(n->n);
4069         other = &(n->p);
4070     } else {
4071         if (which > 0) { // right handle
4072             if (xn > xp) {
4073                 me = &(n->n);
4074                 other = &(n->p);
4075             } else {
4076                 me = &(n->p);
4077                 other = &(n->n);
4078             }
4079         } else if (which < 0){ // left handle
4080             if (xn <= xp) {
4081                 me = &(n->n);
4082                 other = &(n->p);
4083             } else {
4084                 me = &(n->p);
4085                 other = &(n->n);
4086             }
4087         } else { // both handles
4088             me = &(n->n);
4089             other = &(n->p);
4090             both = true;
4091         }
4092     }
4094     Radial rme(me->pos - n->pos);
4095     Radial rother(other->pos - n->pos);
4097     if (screen) {
4098         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4099     } else {
4100         node_rotate_one_internal (*n, angle, rme, rother, both);
4101     }
4103     me->pos = n->pos + Geom::Point(rme);
4105     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4106         other->pos =  n->pos + Geom::Point(rother);
4107     }
4109     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4110     // so here we just move all the knots without emitting move signals, for speed
4111     sp_node_update_handles(n, false);
4114 /**
4115  * Rotate selected nodes.
4116  */
4117 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4119     if (!nodepath || !nodepath->selected) return;
4121     if (g_list_length(nodepath->selected) == 1) {
4122        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4123         node_rotate_one (n, angle, which, screen);
4124     } else {
4125        // rotate as an object:
4127         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4128         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4129         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4130             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4131             box.expandTo (n->pos); // contain all selected nodes
4132         }
4134         gdouble rot;
4135         if (screen) {
4136             gdouble const zoom = nodepath->desktop->current_zoom();
4137             gdouble const zmove = angle / zoom;
4138             gdouble const r = Geom::L2(box.max() - box.midpoint());
4139             rot = atan2(zmove, r);
4140         } else {
4141             rot = angle;
4142         }
4144         Geom::Point rot_center;
4145         if (Inkscape::NodePath::Path::active_node == NULL)
4146             rot_center = box.midpoint();
4147         else
4148             rot_center = Inkscape::NodePath::Path::active_node->pos;
4150         Geom::Matrix t =
4151             Geom::Matrix (Geom::Translate(-rot_center)) *
4152             Geom::Matrix (Geom::Rotate(rot)) *
4153             Geom::Matrix (Geom::Translate(rot_center));
4155         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4156             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4157             n->pos *= t;
4158             n->n.pos *= t;
4159             n->p.pos *= t;
4160             sp_node_update_handles(n, false);
4161         }
4162     }
4164     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4167 /**
4168  * Scale one node.
4169  */
4170 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4172     bool both = false;
4173     Inkscape::NodePath::NodeSide *me, *other;
4175     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4176     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4178     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4179         me = &(n->p);
4180         other = &(n->n);
4181         n->code = NR_CURVETO;
4182     } else if (!n->p.other) {
4183         me = &(n->n);
4184         other = &(n->p);
4185         if (n->n.other)
4186             n->n.other->code = NR_CURVETO;
4187     } else {
4188         if (which > 0) { // right handle
4189             if (xn > xp) {
4190                 me = &(n->n);
4191                 other = &(n->p);
4192                 if (n->n.other)
4193                     n->n.other->code = NR_CURVETO;
4194             } else {
4195                 me = &(n->p);
4196                 other = &(n->n);
4197                 n->code = NR_CURVETO;
4198             }
4199         } else if (which < 0){ // left handle
4200             if (xn <= xp) {
4201                 me = &(n->n);
4202                 other = &(n->p);
4203                 if (n->n.other)
4204                     n->n.other->code = NR_CURVETO;
4205             } else {
4206                 me = &(n->p);
4207                 other = &(n->n);
4208                 n->code = NR_CURVETO;
4209             }
4210         } else { // both handles
4211             me = &(n->n);
4212             other = &(n->p);
4213             both = true;
4214             n->code = NR_CURVETO;
4215             if (n->n.other)
4216                 n->n.other->code = NR_CURVETO;
4217         }
4218     }
4220     Radial rme(me->pos - n->pos);
4221     Radial rother(other->pos - n->pos);
4223     rme.r += grow;
4224     if (rme.r < 0) rme.r = 0;
4225     if (rme.a == HUGE_VAL) {
4226         if (me->other) { // if direction is unknown, initialize it towards the next node
4227             Radial rme_next(me->other->pos - n->pos);
4228             rme.a = rme_next.a;
4229         } else { // if there's no next, initialize to 0
4230             rme.a = 0;
4231         }
4232     }
4233     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4234         rother.r += grow;
4235         if (rother.r < 0) rother.r = 0;
4236         if (rother.a == HUGE_VAL) {
4237             rother.a = rme.a + M_PI;
4238         }
4239     }
4241     me->pos = n->pos + Geom::Point(rme);
4243     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4244         other->pos = n->pos + Geom::Point(rother);
4245     }
4247     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4248     // so here we just move all the knots without emitting move signals, for speed
4249     sp_node_update_handles(n, false);
4252 /**
4253  * Scale selected nodes.
4254  */
4255 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4257     if (!nodepath || !nodepath->selected) return;
4259     if (g_list_length(nodepath->selected) == 1) {
4260         // scale handles of the single selected node
4261         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4262         node_scale_one (n, grow, which);
4263     } else {
4264         // scale nodes as an "object":
4266         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4267         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4268         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4269             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4270             box.expandTo (n->pos); // contain all selected nodes
4271         }
4273         double scale = (box.maxExtent() + grow)/box.maxExtent();
4275         Geom::Point scale_center;
4276         if (Inkscape::NodePath::Path::active_node == NULL)
4277             scale_center = box.midpoint();
4278         else
4279             scale_center = Inkscape::NodePath::Path::active_node->pos;
4281         Geom::Matrix t =
4282             Geom::Matrix (Geom::Translate(-scale_center)) *
4283             Geom::Matrix (Geom::Scale(scale, scale)) *
4284             Geom::Matrix (Geom::Translate(scale_center));
4286         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4287             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4288             n->pos *= t;
4289             n->n.pos *= t;
4290             n->p.pos *= t;
4291             sp_node_update_handles(n, false);
4292         }
4293     }
4295     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4298 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4300     if (!nodepath) return;
4301     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4304 /**
4305  * Flip selected nodes horizontally/vertically.
4306  */
4307 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis, boost::optional<Geom::Point> center)
4309     if (!nodepath || !nodepath->selected) return;
4311     if (g_list_length(nodepath->selected) == 1 && !center) {
4312         // flip handles of the single selected node
4313         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4314         double temp = n->p.pos[axis];
4315         n->p.pos[axis] = n->n.pos[axis];
4316         n->n.pos[axis] = temp;
4317         sp_node_update_handles(n, false);
4318     } else {
4319         // scale nodes as an "object":
4321         Geom::Rect box = sp_node_selected_bbox (nodepath);
4322         if (!center) {
4323             center = box.midpoint();
4324         }
4325         Geom::Matrix t =
4326             Geom::Matrix (Geom::Translate(- *center)) *
4327             Geom::Matrix ((axis == Geom::X)? Geom::Scale(-1, 1) : Geom::Scale(1, -1)) *
4328             Geom::Matrix (Geom::Translate(*center));
4330         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4331             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4332             n->pos *= t;
4333             n->n.pos *= t;
4334             n->p.pos *= t;
4335             sp_node_update_handles(n, false);
4336         }
4337     }
4339     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4342 Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4344     g_assert (nodepath->selected);
4346     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4347     Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4348     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4349         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4350         box.expandTo (n->pos); // contain all selected nodes
4351     }
4352     return box;
4355 //-----------------------------------------------
4356 /**
4357  * Return new subpath under given nodepath.
4358  */
4359 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4361     g_assert(nodepath);
4362     g_assert(nodepath->desktop);
4364    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4366     s->nodepath = nodepath;
4367     s->closed = FALSE;
4368     s->nodes = NULL;
4369     s->first = NULL;
4370     s->last = NULL;
4372     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4373     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4374     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4376     return s;
4379 /**
4380  * Destroy nodes in subpath, then subpath itself.
4381  */
4382 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4384     g_assert(subpath);
4385     g_assert(subpath->nodepath);
4386     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4388     while (subpath->nodes) {
4389         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4390     }
4392     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4394     g_free(subpath);
4397 /**
4398  * Link head to tail in subpath.
4399  */
4400 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4402     g_assert(!sp->closed);
4403     g_assert(sp->last != sp->first);
4404     g_assert(sp->first->code == NR_MOVETO);
4406     sp->closed = TRUE;
4408     //Link the head to the tail
4409     sp->first->p.other = sp->last;
4410     sp->last->n.other  = sp->first;
4411     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4412     sp->first          = sp->last;
4414     //Remove the extra end node
4415     sp_nodepath_node_destroy(sp->last->n.other);
4418 /**
4419  * Open closed (loopy) subpath at node.
4420  */
4421 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4423     g_assert(sp->closed);
4424     g_assert(n->subpath == sp);
4425     g_assert(sp->first == sp->last);
4427     /* We create new startpoint, current node will become last one */
4429    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4430                                                 &n->pos, &n->pos, &n->n.pos);
4433     sp->closed        = FALSE;
4435     //Unlink to make a head and tail
4436     sp->first         = new_path;
4437     sp->last          = n;
4438     n->n.other        = NULL;
4439     new_path->p.other = NULL;
4442 /**
4443  * Return new node in subpath with given properties.
4444  * \param pos Position of node.
4445  * \param ppos Handle position in previous direction
4446  * \param npos Handle position in previous direction
4447  */
4448 Inkscape::NodePath::Node *
4449 sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp, Inkscape::NodePath::Node *next, Inkscape::NodePath::NodeType type, NRPathcode code, Geom::Point *ppos, Geom::Point *pos, Geom::Point *npos)
4451     g_assert(sp);
4452     g_assert(sp->nodepath);
4453     g_assert(sp->nodepath->desktop);
4455     if (nodechunk == NULL)
4456         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4458     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4460     n->subpath  = sp;
4462     if (type != Inkscape::NodePath::NODE_NONE) {
4463         // use the type from sodipodi:nodetypes
4464         n->type = type;
4465     } else {
4466         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4467             // points are (almost) collinear
4468             if (Geom::L2(*pos - *ppos) < 1e-6 || Geom::L2(*pos - *npos) < 1e-6) {
4469                 // endnode, or a node with a retracted handle
4470                 n->type = Inkscape::NodePath::NODE_CUSP;
4471             } else {
4472                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4473             }
4474         } else {
4475             n->type = Inkscape::NodePath::NODE_CUSP;
4476         }
4477     }
4479     n->code     = code;
4480     n->selected = FALSE;
4481     n->pos      = *pos;
4482     n->p.pos    = *ppos;
4483     n->n.pos    = *npos;
4485     n->dragging_out = NULL;
4487     Inkscape::NodePath::Node *prev;
4488     if (next) {
4489         //g_assert(g_list_find(sp->nodes, next));
4490         prev = next->p.other;
4491     } else {
4492         prev = sp->last;
4493     }
4495     if (prev)
4496         prev->n.other = n;
4497     else
4498         sp->first = n;
4500     if (next)
4501         next->p.other = n;
4502     else
4503         sp->last = n;
4505     n->p.other = prev;
4506     n->n.other = next;
4508     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"));
4509     sp_knot_set_position(n->knot, *pos, 0);
4511     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4512     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4513     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4514     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4515     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4516     sp_knot_update_ctrl(n->knot);
4518     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4519     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4520     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4521     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4522     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4523     sp_knot_show(n->knot);
4525     // We only create handle knots and lines on demand
4526     n->p.knot = NULL;
4527     n->p.line = NULL;
4528     n->n.knot = NULL;
4529     n->n.line = NULL;
4531     sp->nodes = g_list_prepend(sp->nodes, n);
4533     return n;
4536 /**
4537  * Destroy node and its knots, link neighbors in subpath.
4538  */
4539 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4541     g_assert(node);
4542     g_assert(node->subpath);
4543     g_assert(SP_IS_KNOT(node->knot));
4545    Inkscape::NodePath::SubPath *sp = node->subpath;
4547     if (node->selected) { // first, deselect
4548         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4549         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4550     }
4552     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4554     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4555     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4556     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4557     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4558     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4559     g_object_unref(G_OBJECT(node->knot));
4561     if (node->p.knot) {
4562         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4563         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4564         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4565         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4566         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4567         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4568         g_object_unref(G_OBJECT(node->p.knot));
4569         node->p.knot = NULL;
4570     }
4572     if (node->n.knot) {
4573         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4574         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4575         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4576         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4577         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4578         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4579         g_object_unref(G_OBJECT(node->n.knot));
4580         node->n.knot = NULL;
4581     }
4583     if (node->p.line)
4584         gtk_object_destroy(GTK_OBJECT(node->p.line));
4585     if (node->n.line)
4586         gtk_object_destroy(GTK_OBJECT(node->n.line));
4588     if (sp->nodes) { // there are others nodes on the subpath
4589         if (sp->closed) {
4590             if (sp->first == node) {
4591                 g_assert(sp->last == node);
4592                 sp->first = node->n.other;
4593                 sp->last = sp->first;
4594             }
4595             node->p.other->n.other = node->n.other;
4596             node->n.other->p.other = node->p.other;
4597         } else {
4598             if (sp->first == node) {
4599                 sp->first = node->n.other;
4600                 sp->first->code = NR_MOVETO;
4601             }
4602             if (sp->last == node) sp->last = node->p.other;
4603             if (node->p.other) node->p.other->n.other = node->n.other;
4604             if (node->n.other) node->n.other->p.other = node->p.other;
4605         }
4606     } else { // this was the last node on subpath
4607         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4608     }
4610     g_mem_chunk_free(nodechunk, node);
4613 /**
4614  * Returns one of the node's two sides.
4615  * \param which Indicates which side.
4616  * \return Pointer to previous node side if which==-1, next if which==1.
4617  */
4618 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4620     g_assert(node);
4621     Inkscape::NodePath::NodeSide * result = 0;
4622     switch (which) {
4623         case -1:
4624             result = &node->p;
4625             break;
4626         case 1:
4627             result = &node->n;
4628             break;
4629         default:
4630             g_assert_not_reached();
4631     }
4633     return result;
4636 /**
4637  * Return the other side of the node, given one of its sides.
4638  */
4639 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4641     g_assert(node);
4642     Inkscape::NodePath::NodeSide *result = 0;
4644     if (me == &node->p) {
4645         result = &node->n;
4646     } else if (me == &node->n) {
4647         result = &node->p;
4648     } else {
4649         g_assert_not_reached();
4650     }
4652     return result;
4655 /**
4656  * Return NRPathcode on the given side of the node.
4657  */
4658 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4660     g_assert(node);
4662     NRPathcode result = NR_END;
4663     if (me == &node->p) {
4664         if (node->p.other) {
4665             result = (NRPathcode)node->code;
4666         } else {
4667             result = NR_MOVETO;
4668         }
4669     } else if (me == &node->n) {
4670         if (node->n.other) {
4671             result = (NRPathcode)node->n.other->code;
4672         } else {
4673             result = NR_MOVETO;
4674         }
4675     } else {
4676         g_assert_not_reached();
4677     }
4679     return result;
4682 /**
4683  * Return node with the given index
4684  */
4685 Inkscape::NodePath::Node *
4686 sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index)
4688     Inkscape::NodePath::Node *e = NULL;
4690     if (!nodepath) {
4691         return e;
4692     }
4694     //find segment
4695     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4697         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4698         int n = g_list_length(sp->nodes);
4699         if (sp->closed) {
4700             n++;
4701         }
4703         //if the piece belongs to this subpath grab it
4704         //otherwise move onto the next subpath
4705         if (index < n) {
4706             e = sp->first;
4707             for (int i = 0; i < index; ++i) {
4708                 e = e->n.other;
4709             }
4710             break;
4711         } else {
4712             if (sp->closed) {
4713                 index -= (n+1);
4714             } else {
4715                 index -= n;
4716             }
4717         }
4718     }
4720     return e;
4723 /**
4724  * Returns plain text meaning of node type.
4725  */
4726 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4728     unsigned retracted = 0;
4729     bool endnode = false;
4731     for (int which = -1; which <= 1; which += 2) {
4732         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4733         if (side->other && Geom::L2(side->pos - node->pos) < 1e-6)
4734             retracted ++;
4735         if (!side->other)
4736             endnode = true;
4737     }
4739     if (retracted == 0) {
4740         if (endnode) {
4741                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4742                 return _("end node");
4743         } else {
4744             switch (node->type) {
4745                 case Inkscape::NodePath::NODE_CUSP:
4746                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4747                     return _("cusp");
4748                 case Inkscape::NodePath::NODE_SMOOTH:
4749                     // TRANSLATORS: "smooth" is an adjective here
4750                     return _("smooth");
4751                 case Inkscape::NodePath::NODE_SYMM:
4752                     return _("symmetric");
4753             }
4754         }
4755     } else if (retracted == 1) {
4756         if (endnode) {
4757             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4758             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4759         } else {
4760             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4761         }
4762     } else {
4763         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4764     }
4766     return NULL;
4769 /**
4770  * Handles content of statusbar as long as node tool is active.
4771  */
4772 void
4773 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4775     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");
4776     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4778     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4779     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4780     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4781     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4783     SPDesktop *desktop = NULL;
4784     if (nodepath) {
4785         desktop = nodepath->desktop;
4786     } else {
4787         desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above
4788     }
4790     SPEventContext *ec = desktop->event_context;
4791     if (!ec) return;
4793     Inkscape::MessageContext *mc = get_message_context(ec);
4794     if (!mc) return;
4796     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4798     if (selected_nodes == 0) {
4799         Inkscape::Selection *sel = desktop->selection;
4800         if (!sel || sel->isEmpty()) {
4801             mc->setF(Inkscape::NORMAL_MESSAGE,
4802                      _("Select a single object to edit its nodes or handles."));
4803         } else {
4804             if (nodepath) {
4805             mc->setF(Inkscape::NORMAL_MESSAGE,
4806                      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.",
4807                               "<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.",
4808                               total_nodes),
4809                      total_nodes);
4810             } else {
4811                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4812                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4813                 } else {
4814                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4815                 }
4816             }
4817         }
4818     } else if (nodepath && selected_nodes == 1) {
4819         mc->setF(Inkscape::NORMAL_MESSAGE,
4820                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4821                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4822                           total_nodes),
4823                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4824     } else {
4825         if (selected_subpaths > 1) {
4826             mc->setF(Inkscape::NORMAL_MESSAGE,
4827                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4828                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4829                               total_nodes),
4830                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4831         } else {
4832             mc->setF(Inkscape::NORMAL_MESSAGE,
4833                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4834                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4835                               total_nodes),
4836                      selected_nodes, total_nodes, when_selected);
4837         }
4838     }
4841 /*
4842  * returns a *copy* of the curve of that object.
4843  */
4844 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4845     if (!object)
4846         return NULL;
4848     SPCurve *curve = NULL;
4849     if (SP_IS_PATH(object)) {
4850         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4851         curve = curve_new->copy();
4852     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4853         const gchar *svgd = object->repr->attribute(key);
4854         if (svgd) {
4855             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4856             SPCurve *curve_new = new SPCurve(pv);
4857             if (curve_new) {
4858                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4859             }
4860         }
4861     }
4863     return curve;
4866 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4867     if (!np || !np->object || !curve)
4868         return;
4870     if (SP_IS_PATH(np->object)) {
4871         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4872             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4873         } else {
4874             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4875         }
4876     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4877         Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( LIVEPATHEFFECT(np->object)->lpe->getParameter(np->repr_key) );
4878         if (pathparam) {
4879             pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
4880             np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4881         }
4882     }
4885 /**
4886 SPCanvasItem *
4887 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
4888     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
4890 **/
4892 /**
4893 SPCanvasItem *
4894 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) {
4895     SPCurve *flash_curve = curve->copy();
4896     Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity();
4897     flash_curve->transform(i2d);
4898     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4899     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4900     // unless we also flash the nodes...
4901     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4902     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4903     sp_canvas_item_show(canvasitem);
4904     flash_curve->unref();
4905     return canvasitem;
4908 SPCanvasItem *
4909 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) {
4910     return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path),
4911                                            prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff));
4913 **/
4915 SPCanvasItem *
4916 sp_nodepath_helperpath_from_path(SPDesktop *desktop, SPPath *path) {
4917     SPCurve *flash_curve = sp_path_get_curve_for_edit(path)->copy();
4918     Geom::Matrix i2d = sp_item_i2d_affine(SP_ITEM(path));
4919     flash_curve->transform(i2d);
4920     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4921     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4922     // unless we also flash the nodes...
4923     guint32 color = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
4924     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4925     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4926     sp_canvas_item_show(canvasitem);
4927     flash_curve->unref();
4928     return canvasitem;
4931 // TODO: Merge this with sp_nodepath_make_helper_item()!
4932 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4933     np->show_helperpath = show;
4935     if (show) {
4936         SPCurve *helper_curve = np->curve->copy();
4937         helper_curve->transform(np->i2d);
4938         if (!np->helper_path) {
4939             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
4941             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4942             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);
4943             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4944             sp_canvas_item_move_to_z(np->helper_path, 0);
4945             sp_canvas_item_show(np->helper_path);
4946         } else {
4947             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4948         }
4949         helper_curve->unref();
4950     } else {
4951         if (np->helper_path) {
4952             GtkObject *temp = np->helper_path;
4953             np->helper_path = NULL;
4954             gtk_object_destroy(temp);
4955         }
4956     }
4959 /* sp_nodepath_make_straight_path:
4960  *   Prevents user from curving the path by dragging a segment or activating handles etc.
4961  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
4962  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
4963  */
4964 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4965     np->straight_path = true;
4966     np->show_handles = false;
4967     g_message("add code to make the path straight.");
4968     // do sp_nodepath_convert_node_type on all nodes?
4969     // coding tip: search for this text : "Make selected segments lines"
4972 /*
4973   Local Variables:
4974   mode:c++
4975   c-file-style:"stroustrup"
4976   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4977   indent-tabs-mode:nil
4978   fill-column:99
4979   End:
4980 */
4981 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :