Code

fix pulling out second handle when the segment was a lineto
[inkscape.git] / src / nodepath.cpp
1 #define __SP_NODEPATH_C__
3 /** \file
4  * Path handler in node edit mode
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *
10  * Portions of this code are in public domain; node sculpting functions written by bulia byak are under GNU GPL
11  */
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
17 #include <gdk/gdkkeysyms.h>
18 #include "display/canvas-bpath.h"
19 #include "display/curve.h"
20 #include "display/sp-ctrlline.h"
21 #include "display/sodipodi-ctrl.h"
22 #include "display/sp-canvas-util.h"
23 #include <glibmm/i18n.h>
24 #include "2geom/pathvector.h"
25 #include "2geom/sbasis-to-bezier.h"
26 #include "2geom/bezier-curve.h"
27 #include "2geom/hvlinesegment.h"
28 #include "helper/units.h"
29 #include "helper/geom.h"
30 #include "knot.h"
31 #include "inkscape.h"
32 #include "document.h"
33 #include "sp-namedview.h"
34 #include "desktop.h"
35 #include "desktop-handles.h"
36 #include "snap.h"
37 #include "message-stack.h"
38 #include "message-context.h"
39 #include "node-context.h"
40 #include "lpe-tool-context.h"
41 #include "shape-editor.h"
42 #include "selection-chemistry.h"
43 #include "selection.h"
44 #include "xml/repr.h"
45 #include "preferences.h"
46 #include "sp-metrics.h"
47 #include "sp-path.h"
48 #include "sp-text.h"
49 #include "sp-shape.h"
50 #include "libnr/nr-matrix-ops.h"
51 #include "svg/svg.h"
52 #include "verbs.h"
53 #include <2geom/bezier-utils.h>
54 #include <vector>
55 #include <algorithm>
56 #include <cstring>
57 #include <cmath>
58 #include "live_effects/lpeobject.h"
59 #include "live_effects/lpeobject-reference.h"
60 #include "live_effects/effect.h"
61 #include "live_effects/parameter/parameter.h"
62 #include "live_effects/parameter/path.h"
63 #include "util/mathfns.h"
64 #include "display/snap-indicator.h"
65 #include "snapped-point.h"
67 namespace Geom { class Matrix; }
69 /// \todo
70 /// evil evil evil. FIXME: conflict of two different Path classes!
71 /// There is a conflict in the namespace between two classes named Path.
72 /// #include "sp-flowtext.h"
73 /// #include "sp-flowregion.h"
75 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
76 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
77 GType sp_flowregion_get_type (void);
78 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
79 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
80 GType sp_flowtext_get_type (void);
81 // end evil workaround
83 #include "helper/stlport.h"
86 /// \todo fixme: Implement these via preferences */
88 #define NODE_FILL          0xbfbfbf00
89 #define NODE_STROKE        0x000000ff
90 #define NODE_FILL_HI       0xff000000
91 #define NODE_STROKE_HI     0x000000ff
92 #define NODE_FILL_SEL      0x0000ffff
93 #define NODE_STROKE_SEL    0x000000ff
94 #define NODE_FILL_SEL_HI   0xff000000
95 #define NODE_STROKE_SEL_HI 0x000000ff
96 #define KNOT_FILL          0xffffffff
97 #define KNOT_STROKE        0x000000ff
98 #define KNOT_FILL_HI       0xff000000
99 #define KNOT_STROKE_HI     0x000000ff
101 static GMemChunk *nodechunk = NULL;
103 /* Creation from object */
105 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t);
106 static Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length);
108 /* Object updating */
110 static void stamp_repr(Inkscape::NodePath::Path *np);
111 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
112 static gchar *create_typestr(Inkscape::NodePath::Path *np);
114 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
116 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
118 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
120 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
122 /* Adjust handle placement, if the node or the other handle is moved */
123 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
124 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
125 static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node);
127 /* Node event callbacks */
128 static void node_clicked(SPKnot *knot, guint state, gpointer data);
129 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
130 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
131 static gboolean node_request(SPKnot *knot, Geom::Point const &p, guint state, gpointer data);
133 /* Handle event callbacks */
134 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
135 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
136 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
137 static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, gpointer data);
138 static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data);
139 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
141 /* Constructors and destructors */
143 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
144 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
145 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
146 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
147 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
148                                          Geom::Point *ppos, Geom::Point *pos, Geom::Point *npos);
149 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
151 /* Helpers */
153 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
154 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
155 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
157 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
158 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
160 // active_node indicates mouseover node
161 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
163 static SPCanvasItem *
164 sp_nodepath_make_helper_item(Inkscape::NodePath::Path *np, /*SPDesktop *desktop, */const SPCurve *curve, bool show = false, guint32 color = 0xff0000ff) {
165     SPCurve *helper_curve = curve->copy();
166     helper_curve->transform(np->i2d);
167     SPCanvasItem *helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
168     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helper_path), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
169     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helper_path), 0, SP_WIND_RULE_NONZERO);
170     sp_canvas_item_move_to_z(helper_path, 0);
171     if (show) {
172         sp_canvas_item_show(helper_path);
173     }
174     helper_curve->unref();
175     return helper_path;
178 static void
179 sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) {
180     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> > helper_path_vec;
181     if (!SP_IS_LPE_ITEM(np->item)) {
182         g_print ("Only LPEItems can have helperpaths!\n");
183         return;
184     }
186     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
187     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
188     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
189         Inkscape::LivePathEffect::LPEObjectReference *lperef = (*i);
190         Inkscape::LivePathEffect::Effect *lpe = lperef->lpeobject->get_lpe();
191         if (lpe) {
192             // create new canvas items from the effect's helper paths
193             std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
194             for (std::vector<Geom::PathVector>::iterator j = hpaths.begin(); j != hpaths.end(); ++j) {
195                 SPCurve *helper_curve = new SPCurve(*j);
196                 SPCanvasItem * canvasitem = sp_nodepath_make_helper_item(np, helper_curve, true, 0x509050dd);
197                 np->helper_path_vec[lpe].push_back(canvasitem);
198                 helper_curve->unref();
199             }
200         }
201     }
204 void
205 sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np) {
206     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> > helper_path_vec;
207     if (!SP_IS_LPE_ITEM(np->item)) {
208         g_print ("Only LPEItems can have helperpaths!\n");
209         return;
210     }
212     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
213     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
214     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
215         Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->get_lpe();
216         if (lpe) {
217             /* update canvas items from the effect's helper paths; note that this code relies on the
218              * fact that getHelperPaths() will always return the same number of helperpaths in the same
219              * order as during their creation in sp_nodepath_create_helperpaths
220              */
221             std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
222             for (unsigned int j = 0; j < hpaths.size(); ++j) {
223                 SPCurve *curve = new SPCurve(hpaths[j]);
224                 curve->transform(np->i2d);
225                 sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH((np->helper_path_vec[lpe])[j]), curve);
226                 curve = curve->unref();
227             }
228         }
229     }
232 static void
233 sp_nodepath_destroy_helperpaths(Inkscape::NodePath::Path *np) {
234     for (HelperPathList::iterator i = np->helper_path_vec.begin(); i != np->helper_path_vec.end(); ++i) {
235         for (std::vector<SPCanvasItem *>::iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) {
236             GtkObject *temp = *j;
237             *j = NULL;
238             gtk_object_destroy(temp);
239         }
240     }
241     np->helper_path_vec.clear();
245 /**
246  * \brief Creates new nodepath from item
247  *
248  * If repr_key_in is not NULL, object *has* to be a LivePathEffectObject !
249  *
250  * \todo create proper constructor for nodepath::path, this method returns null a constructor cannot so this cannot be simply converted to constructor.
251  */
252 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
254     if (repr_key_in) {
255         g_assert(IS_LIVEPATHEFFECT(object));
256     }
258     Inkscape::XML::Node *repr = object->repr;
260     /** \todo
261      * FIXME: remove this. We don't want to edit paths inside flowtext.
262      * Instead we will build our flowtext with cloned paths, so that the
263      * real paths are outside the flowtext and thus editable as usual.
264      */
265     if (SP_IS_FLOWTEXT(object)) {
266         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
267             if SP_IS_FLOWREGION(child) {
268                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
269                 if (grandchild && SP_IS_PATH(grandchild)) {
270                     object = SP_ITEM(grandchild);
271                     break;
272                 }
273             }
274         }
275     }
277     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
279     if (curve == NULL) {
280         return NULL;
281     }
283     if (curve->get_segment_count() < 1) {
284         curve->unref();
285         return NULL; // prevent crash for one-node paths
286     }
288     //Create new nodepath
289     Inkscape::NodePath::Path *np = new Inkscape::NodePath::Path();
290     if (!np) {
291         curve->unref();
292         return NULL;
293     }
295     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
297     // Set defaults
298     np->desktop     = desktop;
299     np->object      = object;
300     np->subpaths    = NULL;
301     np->selected    = NULL;
302     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
303     np->local_change = 0;
304     np->show_handles = show_handles;
305     np->helper_path = NULL;
306     np->helperpath_rgba = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
307     np->helperpath_width = 1.0;
308     np->curve = curve->copy();
309     np->show_helperpath = prefs->getBool("/tools/nodes/show_helperpath");
310     if (SP_IS_LPE_ITEM(object)) {
311         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
312         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
313             np->show_helperpath = true;
314         }
315     }
316     np->straight_path = false;
317     if (IS_LIVEPATHEFFECT(object) && item) {
318         np->item = item;
319     } else {
320         np->item = SP_ITEM(object);
321     }
323     np->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
325     // we need to update item's transform from the repr here,
326     // because they may be out of sync when we respond
327     // to a change in repr by regenerating nodepath     --bb
328     sp_object_read_attr(SP_OBJECT(np->item), "transform");
330     np->i2d  = sp_item_i2d_affine(np->item);
331     np->d2i  = np->i2d.inverse();
333     np->repr = repr;
334     if (repr_key_in) { // apparently the object is an LPEObject
335         np->repr_key = g_strdup(repr_key_in);
336         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
337         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(object)->get_lpe();
338         if (!lpe) {
339             g_error("sp_nodepath_new: lpeobject without real lpe passed as argument!");
340             delete np;
341         }
342         Inkscape::LivePathEffect::Parameter *lpeparam = lpe->getParameter(repr_key_in);
343         if (lpeparam) {
344             lpeparam->param_setup_nodepath(np);
345         }
346     } else {
347         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
348         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
349             np->repr_key = g_strdup("inkscape:original-d");
351             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
352             if (lpe) {
353                 lpe->setup_nodepath(np);
354             }
355         } else {
356             np->repr_key = g_strdup("d");
357         }
358     }
360     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
361      * So for example a closed rectangle has a nodetypestring of length 5.
362      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
363     Geom::PathVector pathv_sanitized = pathv_to_linear_and_cubic_beziers(np->curve->get_pathvector());
364     np->curve->set_pathvector(pathv_sanitized);
365     guint length = np->curve->get_segment_count();
366     for (Geom::PathVector::const_iterator pit = pathv_sanitized.begin(); pit != pathv_sanitized.end(); ++pit) {
367         length += pit->empty() ? 0 : 1;
368     }
370     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
371     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
373     // create the subpath(s) from the bpath
374     subpaths_from_pathvector(np, pathv_sanitized, typestr);
376     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
377     np->subpaths = g_list_reverse(np->subpaths);
379     delete[] typestr;
380     curve->unref();
382     // Draw helper curve
383     if (np->show_helperpath) {
384         np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true, np->helperpath_rgba);
385     }
387     sp_nodepath_create_helperpaths(np);
389     return np;
392 /**
393  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
394  */
395 Inkscape::NodePath::Path::~Path() {
396     while (this->subpaths) {
397         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) this->subpaths->data);
398     }
400     //Inform the ShapeEditor that made me, if any, that I am gone.
401     if (this->shape_editor)
402         this->shape_editor->nodepath_destroyed();
404     g_assert(!this->selected);
406     if (this->helper_path) {
407         GtkObject *temp = this->helper_path;
408         this->helper_path = NULL;
409         gtk_object_destroy(temp);
410     }
411     if (this->curve) {
412         this->curve->unref();
413         this->curve = NULL;
414     }
416     if (this->repr_key) {
417         g_free(this->repr_key);
418         this->repr_key = NULL;
419     }
420     if (this->repr_nodetypes_key) {
421         g_free(this->repr_nodetypes_key);
422         this->repr_nodetypes_key = NULL;
423     }
425     sp_nodepath_destroy_helperpaths(this);
427     this->desktop = NULL;
430 /**
431  *  Return the node count of a given NodeSubPath.
432  */
433 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
435     int nodeCount = 0;
437     if (subpath) {
438         nodeCount = g_list_length(subpath->nodes);
439     }
441     return nodeCount;
444 /**
445  *  Return the node count of a given NodePath.
446  */
447 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
449     gint nodeCount = 0;
450     if (np) {
451         for (GList *item = np->subpaths ; item ; item=item->next) {
452             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
453             nodeCount += g_list_length(subpath->nodes);
454         }
455     }
456     return nodeCount;
459 /**
460  *  Return the subpath count of a given NodePath.
461  */
462 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
464     gint nodeCount = 0;
465     if (np) {
466         nodeCount = g_list_length(np->subpaths);
467     }
468     return nodeCount;
471 /**
472  *  Return the selected node count of a given NodePath.
473  */
474 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
476     gint nodeCount = 0;
477     if (np) {
478         nodeCount = g_list_length(np->selected);
479     }
480     return nodeCount;
483 /**
484  *  Return the number of subpaths where nodes are selected in a given NodePath.
485  */
486 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
488     gint nodeCount = 0;
489     if (np && np->selected) {
490         if (!np->selected->next) {
491             nodeCount = 1;
492         } else {
493             for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
494                 Inkscape::NodePath::SubPath *subpath = static_cast<Inkscape::NodePath::SubPath *>(spl->data);
495                 for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
496                     Inkscape::NodePath::Node *node = static_cast<Inkscape::NodePath::Node *>(nl->data);
497                     if (node->selected) {
498                         nodeCount++;
499                         break;
500                     }
501                 }
502             }
503         }
504     }
505     return nodeCount;
508 /**
509  * Clean up a nodepath after editing.
510  *
511  * Currently we are deleting trivial subpaths.
512  */
513 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
515     GList *badSubPaths = NULL;
517     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
518     for (GList *l = nodepath->subpaths; l ; l=l->next) {
519        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
520        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
521             badSubPaths = g_list_append(badSubPaths, sp);
522     }
524     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
525     //also removes the subpath from nodepath->subpaths
526     for (GList *l = badSubPaths; l ; l=l->next) {
527        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
528         sp_nodepath_subpath_destroy(sp);
529     }
531     g_list_free(badSubPaths);
534 /**
535  * Create new nodepaths from pathvector, make it subpaths of np.
536  * \param t The node type array.
537  */
538 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
540     guint i = 0;  // index into node type array
541     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
542         if (pit->empty())
543             continue;  // don't add single knot paths
545         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
547         Geom::Point ppos = pit->initialPoint() * np->i2d;
548         NRPathcode pcode = NR_MOVETO;
550         /* 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)*/
551         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
552             if( dynamic_cast<Geom::LineSegment const*>(&*cit) ||
553                 dynamic_cast<Geom::HLineSegment const*>(&*cit) ||
554                 dynamic_cast<Geom::VLineSegment const*>(&*cit) )
555             {
556                 Geom::Point pos = cit->initialPoint() * (Geom::Matrix)np->i2d;
557                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
559                 ppos = cit->finalPoint() * (Geom::Matrix)np->i2d;
560                 pcode = NR_LINETO;
561             }
562             else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
563                 std::vector<Geom::Point> points = cubic_bezier->points();
564                 Geom::Point pos = points[0] * (Geom::Matrix)np->i2d;
565                 Geom::Point npos = points[1] * (Geom::Matrix)np->i2d;
566                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
568                 ppos = points[2] * (Geom::Matrix)np->i2d;
569                 pcode = NR_CURVETO;
570             }
571         }
573         if (pit->closed()) {
574             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
575             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
576              * If the length is zero, don't add it to the nodepath. */
577             Geom::Curve const &closing_seg = pit->back_closed();
578             // Don't use !closing_seg.isDegenerate() as it is too precise, and does not account for floating point rounding probs (LP bug #257289)
579             if ( ! are_near(closing_seg.initialPoint(), closing_seg.finalPoint()) ) {
580                 Geom::Point pos = closing_seg.finalPoint() * (Geom::Matrix)np->i2d;
581                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
582             }
584             sp_nodepath_subpath_close(sp);
585         }
586     }
589 /**
590  * Convert from sodipodi:nodetypes to new style type array.
591  */
592 static
593 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
595     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
597     guint pos = 0;
599     if (types) {
600         for (guint i = 0; types[i] && ( i < length ); i++) {
601             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
602             if (types[i] != '\0') {
603                 switch (types[i]) {
604                     case 's':
605                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
606                         break;
607                     case 'a':
608                         typestr[pos++] =Inkscape::NodePath::NODE_AUTO;
609                         break;
610                     case 'z':
611                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
612                         break;
613                     case 'c':
614                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
615                         break;
616                     default:
617                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
618                         break;
619                 }
620             }
621         }
622     }
624     while (pos < length) {
625         typestr[pos++] = Inkscape::NodePath::NODE_NONE;
626     }
628     return typestr;
631 /**
632  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
633  * updated but repr is not (for speed). Used during curve and node drag.
634  */
635 static void update_object(Inkscape::NodePath::Path *np)
637     g_assert(np);
639     np->curve->unref();
640     np->curve = create_curve(np);
642     sp_nodepath_set_curve(np, np->curve);
644     if (np->show_helperpath) {
645         SPCurve * helper_curve = np->curve->copy();
646         helper_curve->transform(np->i2d);
647         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
648         helper_curve->unref();
649     }
651     // updating helperpaths of LPEItems is now done in sp_lpe_item_update();
652     //sp_nodepath_update_helperpaths(np);
654     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
655     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
656     np->shape_editor->update_knotholder();
659 /**
660  * Update XML path node with data from path object.
661  */
662 static void update_repr_internal(Inkscape::NodePath::Path *np)
664     g_assert(np);
666     Inkscape::XML::Node *repr = np->object->repr;
668     np->curve->unref();
669     np->curve = create_curve(np);
671     gchar *typestr = create_typestr(np);
672     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
674     // determine if path has an effect applied and write to correct "d" attribute.
675     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
676         np->local_change++;
677         repr->setAttribute(np->repr_key, svgpath);
678     }
680     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
681         np->local_change++;
682         repr->setAttribute(np->repr_nodetypes_key, typestr);
683     }
685     g_free(svgpath);
686     g_free(typestr);
688     if (np->show_helperpath) {
689         SPCurve * helper_curve = np->curve->copy();
690         helper_curve->transform(np->i2d);
691         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
692         helper_curve->unref();
693     }
695     // TODO: do we need this call here? after all, update_object() should have been called just before
696     //sp_nodepath_update_helperpaths(np);
699 /**
700  * Update XML path node with data from path object, commit changes forever.
701  */
702 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
704     //fixme: np can be NULL, so check before proceeding
705     g_return_if_fail(np != NULL);
707     update_repr_internal(np);
708     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
710     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
711                      annotation);
714 /**
715  * Update XML path node with data from path object, commit changes with undo.
716  */
717 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
719     update_repr_internal(np);
720     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
721                            annotation);
724 /**
725  * Make duplicate of path, replace corresponding XML node in tree, commit.
726  */
727 static void stamp_repr(Inkscape::NodePath::Path *np)
729     g_assert(np);
731     Inkscape::XML::Node *old_repr = np->object->repr;
732     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
734     // remember the position of the item
735     gint pos = old_repr->position();
736     // remember parent
737     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
739     SPCurve *curve = create_curve(np);
740     gchar *typestr = create_typestr(np);
742     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
744     new_repr->setAttribute(np->repr_key, svgpath);
745     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
747     // add the new repr to the parent
748     parent->appendChild(new_repr);
749     // move to the saved position
750     new_repr->setPosition(pos > 0 ? pos : 0);
752     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
753                      _("Stamp"));
755     Inkscape::GC::release(new_repr);
756     g_free(svgpath);
757     g_free(typestr);
758     curve->unref();
761 /**
762  * Create curve from path.
763  */
764 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
766     SPCurve *curve = new SPCurve();
768     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
769        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
770        curve->moveto(sp->first->pos * np->d2i);
771        Inkscape::NodePath::Node *n = sp->first->n.other;
772         while (n) {
773             Geom::Point const end_pt = n->pos * np->d2i;
774             if (!IS_FINITE(n->pos[0]) || !IS_FINITE(n->pos[1])){
775                 g_message("niet finite");
776             }
777             switch (n->code) {
778                 case NR_LINETO:
779                     curve->lineto(end_pt);
780                     break;
781                 case NR_CURVETO:
782                     curve->curveto(n->p.other->n.pos * np->d2i,
783                                      n->p.pos * np->d2i,
784                                      end_pt);
785                     break;
786                 default:
787                     g_assert_not_reached();
788                     break;
789             }
790             if (n != sp->last) {
791                 n = n->n.other;
792             } else {
793                 n = NULL;
794             }
795         }
796         if (sp->closed) {
797             curve->closepath();
798         }
799     }
801     return curve;
804 /**
805  * Convert path type string to sodipodi:nodetypes style.
806  */
807 static gchar *create_typestr(Inkscape::NodePath::Path *np)
809     gchar *typestr = g_new(gchar, 32);
810     gint len = 32;
811     gint pos = 0;
813     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
814        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
816         if (pos >= len) {
817             typestr = g_renew(gchar, typestr, len + 32);
818             len += 32;
819         }
821         typestr[pos++] = 'c';
823        Inkscape::NodePath::Node *n;
824         n = sp->first->n.other;
825         while (n) {
826             gchar code;
828             switch (n->type) {
829                 case Inkscape::NodePath::NODE_CUSP:
830                     code = 'c';
831                     break;
832                 case Inkscape::NodePath::NODE_SMOOTH:
833                     code = 's';
834                     break;
835                 case Inkscape::NodePath::NODE_AUTO:
836                     code = 'a';
837                     break;
838                 case Inkscape::NodePath::NODE_SYMM:
839                     code = 'z';
840                     break;
841                 default:
842                     g_assert_not_reached();
843                     code = '\0';
844                     break;
845             }
847             if (pos >= len) {
848                 typestr = g_renew(gchar, typestr, len + 32);
849                 len += 32;
850             }
852             typestr[pos++] = code;
854             if (n != sp->last) {
855                 n = n->n.other;
856             } else {
857                 n = NULL;
858             }
859         }
860     }
862     if (pos >= len) {
863         typestr = g_renew(gchar, typestr, len + 1);
864         len += 1;
865     }
867     typestr[pos++] = '\0';
869     return typestr;
872 // Returns different message contexts depending on the current context. This function should only
873 // be called when ec is either a SPNodeContext or SPLPEToolContext, thus we return NULL in all
874 // other cases.
875 static Inkscape::MessageContext *
876 get_message_context(SPEventContext *ec)
878     Inkscape::MessageContext *mc = 0;
880     if (SP_IS_NODE_CONTEXT(ec)) {
881         mc = SP_NODE_CONTEXT(ec)->_node_message_context;
882     } else if (SP_IS_LPETOOL_CONTEXT(ec)) {
883         mc = SP_LPETOOL_CONTEXT(ec)->_lpetool_message_context;
884     } else {
885         g_warning ("Nodepath should only be present in Node tool or Geometric tool.");
886     }
888     return mc;
891 /**
892  \brief Fills node and handle positions for three nodes, splitting line
893   marked by end at distance t.
894  */
895 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
897     g_assert(new_path != NULL);
898     g_assert(end      != NULL);
900     g_assert(end->p.other == new_path);
901    Inkscape::NodePath::Node *start = new_path->p.other;
902     g_assert(start);
904     if (end->code == NR_LINETO) {
905         new_path->type =Inkscape::NodePath::NODE_CUSP;
906         new_path->code = NR_LINETO;
907         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
908     } else {
909         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
910         new_path->code = NR_CURVETO;
911         gdouble s      = 1 - t;
912         for (int dim = 0; dim < 2; dim++) {
913             Geom::Coord const f000 = start->pos[dim];
914             Geom::Coord const f001 = start->n.pos[dim];
915             Geom::Coord const f011 = end->p.pos[dim];
916             Geom::Coord const f111 = end->pos[dim];
917             Geom::Coord const f00t = s * f000 + t * f001;
918             Geom::Coord const f01t = s * f001 + t * f011;
919             Geom::Coord const f11t = s * f011 + t * f111;
920             Geom::Coord const f0tt = s * f00t + t * f01t;
921             Geom::Coord const f1tt = s * f01t + t * f11t;
922             Geom::Coord const fttt = s * f0tt + t * f1tt;
923             start->n.pos[dim]    = f00t;
924             new_path->p.pos[dim] = f0tt;
925             new_path->pos[dim]   = fttt;
926             new_path->n.pos[dim] = f1tt;
927             end->p.pos[dim]      = f11t;
928         }
929     }
932 /**
933  * Adds new node on direct line between two nodes, activates handles of all
934  * three nodes.
935  */
936 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
938     g_assert(end);
939     g_assert(end->subpath);
940     g_assert(g_list_find(end->subpath->nodes, end));
942    Inkscape::NodePath::Node *start = end->p.other;
943     g_assert( start->n.other == end );
944    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
945                                                end,
946                                                (NRPathcode)end->code == NR_LINETO?
947                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
948                                                (NRPathcode)end->code,
949                                                &start->pos, &start->pos, &start->n.pos);
950     sp_nodepath_line_midpoint(newnode, end, t);
952     sp_node_adjust_handles(start);
953     sp_node_update_handles(start);
954     sp_node_update_handles(newnode);
955     sp_node_adjust_handles(end);
956     sp_node_update_handles(end);
958     return newnode;
961 /**
962 \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
963 */
964 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
966     g_assert(node);
967     g_assert(node->subpath);
968     g_assert(g_list_find(node->subpath->nodes, node));
970     Inkscape::NodePath::Node* result = 0;
971     Inkscape::NodePath::SubPath *sp = node->subpath;
972     Inkscape::NodePath::Path *np    = sp->nodepath;
974     if (sp->closed) {
975         sp_nodepath_subpath_open(sp, node);
976         result = sp->first;
977     } else if ( (node == sp->first) || (node == sp->last ) ){
978         // no break for end nodes
979         result = 0;
980     } else {
981         // create a new subpath
982         Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
984         // duplicate the break node as start of the new subpath
985         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL,
986                                                                  static_cast<Inkscape::NodePath::NodeType>(node->type),
987                                                                  NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
989         // attach rest of curve to new node
990         g_assert(node->n.other);
991         newnode->n.other = node->n.other; node->n.other = NULL;
992         newnode->n.other->p.other = newnode;
993         newsubpath->last = sp->last;
994         sp->last = node;
995         node = newnode;
996         while (node->n.other) {
997             node = node->n.other;
998             node->subpath = newsubpath;
999             sp->nodes = g_list_remove(sp->nodes, node);
1000             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
1001         }
1004         result = newnode;
1005     }
1006     return result;
1009 /**
1010  * Duplicate node and connect to neighbours.
1011  */
1012 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
1014     g_assert(node);
1015     g_assert(node->subpath);
1016     g_assert(g_list_find(node->subpath->nodes, node));
1018    Inkscape::NodePath::SubPath *sp = node->subpath;
1020     NRPathcode code = (NRPathcode) node->code;
1021     if (code == NR_MOVETO) { // if node is the endnode,
1022         node->code = NR_LINETO; // new one is inserted before it, so change that to line
1023     }
1025     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
1027     if (!node->n.other || !node->p.other) { // if node is an endnode, select it
1028         return node;
1029     } else {
1030         return newnode; // otherwise select the newly created node
1031     }
1034 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
1036     node->p.pos = (node->pos + (node->pos - node->n.pos));
1039 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
1041     node->n.pos = (node->pos + (node->pos - node->p.pos));
1044 /**
1045  * Change line type at node, with side effects on neighbours.
1046  */
1047 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
1049     g_assert(end);
1050     g_assert(end->subpath);
1051     g_assert(end->p.other);
1053     if (end->code != static_cast<guint>(code) ) {
1054         Inkscape::NodePath::Node *start = end->p.other;
1056         end->code = code;
1058         if (code == NR_LINETO) {
1059             if (start->code == NR_LINETO) {
1060                 sp_nodepath_set_node_type(start, Inkscape::NodePath::NODE_CUSP);
1061             }
1062             if (end->n.other) {
1063                 if (end->n.other->code == NR_LINETO) {
1064                     sp_nodepath_set_node_type(end, Inkscape::NodePath::NODE_CUSP);
1065                 }
1066             }
1068             if (start->type == Inkscape::NodePath::NODE_AUTO)
1069                 start->type = Inkscape::NodePath::NODE_SMOOTH;
1070             if (end->type == Inkscape::NodePath::NODE_AUTO)
1071                 end->type = Inkscape::NodePath::NODE_SMOOTH;
1073             start->n.pos = start->pos;
1074             end->p.pos = end->pos;
1076             sp_node_adjust_handle(start, -1);
1077             sp_node_adjust_handle(end, 1);
1079         } else {
1080             Geom::Point delta = end->pos - start->pos;
1081             start->n.pos = start->pos + delta / 3;
1082             end->p.pos = end->pos - delta / 3;
1083             sp_node_adjust_handle(start, 1);
1084             sp_node_adjust_handle(end, -1);
1085         }
1087         sp_node_update_handles(start);
1088         sp_node_update_handles(end);
1089     }
1092 static void
1093 sp_nodepath_update_node_knot(Inkscape::NodePath::Node *node)
1095     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1096         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1097         node->knot->setSize (node->selected? 11 : 9);
1098         sp_knot_update_ctrl(node->knot);
1099     } else if (node->type == Inkscape::NodePath::NODE_AUTO) {
1100         node->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1101         node->knot->setSize (node->selected? 11 : 9);
1102         sp_knot_update_ctrl(node->knot);
1103     } else {
1104         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1105         node->knot->setSize (node->selected? 9 : 7);
1106         sp_knot_update_ctrl(node->knot);
1107     }
1111 /**
1112  * Change node type, and its handles accordingly.
1113  */
1114 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1116     g_assert(node);
1117     g_assert(node->subpath);
1119     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1120         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1121             type =Inkscape::NodePath::NODE_CUSP;
1122         }
1123     }
1125     node->type = type;
1127     sp_nodepath_update_node_knot(node);
1129     // if one of handles is mouseovered, preserve its position
1130     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1131         sp_node_adjust_handle(node, 1);
1132     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1133         sp_node_adjust_handle(node, -1);
1134     } else {
1135         sp_node_adjust_handles(node);
1136     }
1138     sp_node_update_handles(node);
1140     sp_nodepath_update_statusbar(node->subpath->nodepath);
1142     return node;
1145 bool
1146 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1148 // TODO clean up multiple returns
1149         Inkscape::NodePath::Node *othernode = side->other;
1150         if (!othernode)
1151             return false;
1152         NRPathcode const code = sp_node_path_code_from_side(node, side);
1153         if (code == NR_LINETO)
1154             return true;
1155         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1156         if (&node->p == side) {
1157             other_to_me = &othernode->n;
1158         } else if (&node->n == side) {
1159             other_to_me = &othernode->p;
1160         }
1161         if (!other_to_me)
1162             return false;
1163         bool is_line =
1164              (Geom::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1165               Geom::L2(node->pos - side->pos) < 1e-6);
1166         return is_line;
1169 /**
1170  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1171  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1172  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too.
1173  * If already cusp and set to cusp, retracts handles.
1174 */
1175 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1177     if (type == Inkscape::NodePath::NODE_AUTO) {
1178         if (node->p.other != NULL)
1179             node->code = NR_CURVETO;
1180         if (node->n.other != NULL)
1181             node->n.other->code = NR_CURVETO;
1182     }
1184     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1186 /*
1187   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1189         if (two_handles) {
1190             // do nothing, adjust_handles called via set_node_type will line them up
1191         } else if (one_handle) {
1192             if (opposite_to_handle_is_line) {
1193                 if (lined_up) {
1194                     // already half-smooth; pull opposite handle too making it fully smooth
1195                 } else {
1196                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1197                 }
1198             } else {
1199                 // pull opposite handle in line with the existing one
1200             }
1201         } else if (no_handles) {
1202             if (both_segments_are_lines OR both_segments_are_curves) {
1203                 //pull both handles
1204             } else {
1205                 // pull the handle opposite to line segment, making node half-smooth
1206             }
1207         }
1208 */
1209         bool p_has_handle = (Geom::L2(node->pos  - node->p.pos) > 1e-6);
1210         bool n_has_handle = (Geom::L2(node->pos  - node->n.pos) > 1e-6);
1211         bool p_is_line = sp_node_side_is_line(node, &node->p);
1212         bool n_is_line = sp_node_side_is_line(node, &node->n);
1214         if (p_has_handle && n_has_handle) {
1215             // do nothing, adjust_handles will line them up
1216         } else if (p_has_handle || n_has_handle) {
1217             if (p_has_handle && n_is_line) {
1218                 Radial line (node->n.other->pos - node->pos);
1219                 Radial handle (node->pos - node->p.pos);
1220                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1221                     // already half-smooth; pull opposite handle too making it fully smooth
1222                     node->n.other->code = NR_CURVETO;
1223                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1224                 } else {
1225                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1226                 }
1227             } else if (n_has_handle && p_is_line) {
1228                 Radial line (node->p.other->pos - node->pos);
1229                 Radial handle (node->pos - node->n.pos);
1230                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1231                     // already half-smooth; pull opposite handle too making it fully smooth
1232                     node->code = NR_CURVETO;
1233                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1234                 } else {
1235                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1236                 }
1237             } else if (p_has_handle && node->n.other) {
1238                 // pull n handle
1239                 node->n.other->code = NR_CURVETO;
1240                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1241                     Geom::L2(node->p.pos - node->pos) :
1242                     Geom::L2(node->n.other->pos - node->pos) / 3;
1243                 node->n.pos = node->pos - (len / Geom::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1244             } else if (n_has_handle && node->p.other) {
1245                 // pull p handle
1246                 node->code = NR_CURVETO;
1247                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1248                     Geom::L2(node->n.pos - node->pos) :
1249                     Geom::L2(node->p.other->pos - node->pos) / 3;
1250                 node->p.pos = node->pos - (len / Geom::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1251             }
1252         } else if (!p_has_handle && !n_has_handle) {
1253             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1254                 // no handles, but both segments are either lnes or curves:
1255                 //pull both handles
1257                 // convert both to curves:
1258                 node->code = NR_CURVETO;
1259                 node->n.other->code = NR_CURVETO;
1261                 sp_node_adjust_handles_auto(node);
1262             } else {
1263                 // pull the handle opposite to line segment, making it half-smooth
1264                 if (p_is_line && node->n.other) {
1265                     if (type != Inkscape::NodePath::NODE_SYMM) {
1266                         // pull n handle
1267                         node->n.other->code = NR_CURVETO;
1268                         double len =  Geom::L2(node->n.other->pos - node->pos) / 3;
1269                         node->n.pos = node->pos + (len / Geom::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1270                     }
1271                 } else if (n_is_line && node->p.other) {
1272                     if (type != Inkscape::NodePath::NODE_SYMM) {
1273                         // pull p handle
1274                         node->code = NR_CURVETO;
1275                         double len =  Geom::L2(node->p.other->pos - node->pos) / 3;
1276                         node->p.pos = node->pos + (len / Geom::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1277                     }
1278                 }
1279             }
1280         }
1281     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1282         // cusping a cusp: retract nodes
1283         node->p.pos = node->pos;
1284         node->n.pos = node->pos;
1285     }
1287     sp_nodepath_set_node_type (node, type);
1290 /**
1291  * Move node to point, and adjust its and neighbouring handles.
1292  */
1293 void sp_node_moveto(Inkscape::NodePath::Node *node, Geom::Point p)
1295     if (node->type == Inkscape::NodePath::NODE_AUTO) {
1296         node->pos = p;
1297         sp_node_adjust_handles_auto(node);
1298     } else {
1299         Geom::Point delta = p - node->pos;
1300         node->pos = p;
1302         node->p.pos += delta;
1303         node->n.pos += delta;
1304     }
1306     Inkscape::NodePath::Node *node_p = NULL;
1307     Inkscape::NodePath::Node *node_n = NULL;
1309     if (node->p.other) {
1310         if (node->code == NR_LINETO) {
1311             sp_node_adjust_handle(node, 1);
1312             sp_node_adjust_handle(node->p.other, -1);
1313             node_p = node->p.other;
1314         }
1315         if (!node->p.other->selected && node->p.other->type == Inkscape::NodePath::NODE_AUTO) {
1316             sp_node_adjust_handles_auto(node->p.other);
1317             node_p = node->p.other;
1318         }
1319     }
1320     if (node->n.other) {
1321         if (node->n.other->code == NR_LINETO) {
1322             sp_node_adjust_handle(node, -1);
1323             sp_node_adjust_handle(node->n.other, 1);
1324             node_n = node->n.other;
1325         }
1326         if (!node->n.other->selected && node->n.other->type == Inkscape::NodePath::NODE_AUTO) {
1327             sp_node_adjust_handles_auto(node->n.other);
1328             node_n = node->n.other;
1329         }
1330     }
1332     // this function is only called from batch movers that will update display at the end
1333     // themselves, so here we just move all the knots without emitting move signals, for speed
1334     sp_node_update_handles(node, false);
1335     if (node_n) {
1336         sp_node_update_handles(node_n, false);
1337     }
1338     if (node_p) {
1339         sp_node_update_handles(node_p, false);
1340     }
1343 /**
1344  * Call sp_node_moveto() for node selection and handle possible snapping.
1345  */
1346 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, Geom::Coord dx, Geom::Coord dy,
1347                                             bool const snap, bool constrained = false,
1348                                             Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point())
1350     Geom::Point delta(dx, dy);
1351     Geom::Point best_pt = delta;
1352     Inkscape::SnappedPoint best;
1354     if (snap) {
1355         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1356          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we
1357          * must provide that information. */
1359         // Build a list of the unselected nodes to which the snapper should snap
1360         std::vector<std::pair<Geom::Point, int> > unselected_nodes;
1361         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1362             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1363             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1364                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1365                 if (!node->selected) {
1366                     unselected_nodes.push_back(std::make_pair(to_2geom(node->pos), node->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPTARGET_NODE_SMOOTH : Inkscape::SNAPTARGET_NODE_CUSP));
1367                 }
1368             }
1369         }
1371         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1373         // When only the node closest to the mouse pointer is to be snapped
1374         // then we will not even try to snap to other points and discard those immediately
1375         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1376         bool closest_only = prefs->getBool("/options/snapclosestonly/value", false);
1378         Inkscape::NodePath::Node *closest_node = NULL;
1379         Geom::Coord closest_dist = NR_HUGE;
1381         if (closest_only) {
1382                 for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1383                         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1384                         Geom::Coord dist = Geom::L2(nodepath->drag_origin_mouse - n->origin);
1385                         if (dist < closest_dist) {
1386                                 closest_node = n;
1387                                 closest_dist = dist;
1388                         }
1389                 }
1390         }
1392         // Iterate through all selected nodes
1393         m.setup(nodepath->desktop, false, nodepath->item, &unselected_nodes);
1394         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1395             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1396             if (!closest_only || n == closest_node) { //try to snap either all selected nodes or only the closest one
1397                     Inkscape::SnappedPoint s;
1398                     Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP);
1399                     if (constrained) {
1400                         Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1401                         dedicated_constraint.setPoint(n->pos);
1402                         s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type, dedicated_constraint, false);
1403                     } else {
1404                         s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type);
1405                     }
1407                     if (s.getSnapped()) {
1408                         s.setPointerDistance(Geom::L2(nodepath->drag_origin_mouse - n->origin));
1409                         if (!s.isOtherSnapBetter(best, true)) {
1410                                 best = s;
1411                                 best_pt = from_2geom(s.getPoint()) - n->pos;
1412                         }
1413                     }
1414             }
1415         }
1417         if (best.getSnapped()) {
1418             nodepath->desktop->snapindicator->set_new_snaptarget(best);
1419         } else {
1420             nodepath->desktop->snapindicator->remove_snaptarget();
1421         }
1422     }
1424     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1425         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1426         sp_node_moveto(n, n->pos + best_pt);
1427     }
1429     // do not update repr here so that node dragging is acceptably fast
1430     update_object(nodepath);
1433 /**
1434 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1435 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1436 near x = 0.
1437  */
1438 double
1439 sculpt_profile (double x, double alpha, guint profile)
1441     double result = 1;
1443     if (x >= 1) {
1444         result = 0;
1445     } else if (x <= 0) {
1446         result = 1;
1447     } else {
1448         switch (profile) {
1449             case SCULPT_PROFILE_LINEAR:
1450                 result = 1 - x;
1451                 break;
1452             case SCULPT_PROFILE_BELL:
1453                 result = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1454                 break;
1455             case SCULPT_PROFILE_ELLIPTIC:
1456                 result = sqrt(1 - x*x);
1457                 break;
1458             default:
1459                 g_assert_not_reached();
1460         }
1461     }
1463     return result;
1466 double
1467 bezier_length (Geom::Point a, Geom::Point ah, Geom::Point bh, Geom::Point b)
1469     // extremely primitive for now, don't have time to look for the real one
1470     double lower = Geom::L2(b - a);
1471     double upper = Geom::L2(ah - a) + Geom::L2(bh - ah) + Geom::L2(bh - b);
1472     return (lower + upper)/2;
1475 void
1476 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, Geom::Point delta, Geom::Point delta_n, Geom::Point delta_p)
1478     n->pos = n->origin + delta;
1479     n->n.pos = n->n.origin + delta_n;
1480     n->p.pos = n->p.origin + delta_p;
1481     sp_node_adjust_handles(n);
1482     sp_node_update_handles(n, false);
1485 /**
1486  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1487  * on how far they are from the dragged node n.
1488  */
1489 static void
1490 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, Geom::Point delta)
1492     g_assert (n);
1493     g_assert (nodepath);
1494     g_assert (n->subpath->nodepath == nodepath);
1496     double pressure = n->knot->pressure;
1497     if (pressure == 0)
1498         pressure = 0.5; // default
1499     pressure = CLAMP (pressure, 0.2, 0.8);
1501     // map pressure to alpha = 1/5 ... 5
1502     double alpha = 1 - 2 * fabs(pressure - 0.5);
1503     if (pressure > 0.5)
1504         alpha = 1/alpha;
1506     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1507     guint profile = prefs->getInt("/tools/nodes/sculpting_profile", SCULPT_PROFILE_BELL);
1509     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1510         // Only one subpath has selected nodes:
1511         // use linear mode, where the distance from n to node being dragged is calculated along the path
1513         double n_sel_range = 0, p_sel_range = 0;
1514         guint n_nodes = 0, p_nodes = 0;
1515         guint n_sel_nodes = 0, p_sel_nodes = 0;
1517         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1518         {
1519             double n_range = 0, p_range = 0;
1520             bool n_going = true, p_going = true;
1521             Inkscape::NodePath::Node *n_node = n;
1522             Inkscape::NodePath::Node *p_node = n;
1523             do {
1524                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1525                 if (n_node && n_going)
1526                     n_node = n_node->n.other;
1527                 if (n_node == NULL) {
1528                     n_going = false;
1529                 } else {
1530                     n_nodes ++;
1531                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1532                     if (n_node->selected) {
1533                         n_sel_nodes ++;
1534                         n_sel_range = n_range;
1535                     }
1536                     if (n_node == p_node) {
1537                         n_going = false;
1538                         p_going = false;
1539                     }
1540                 }
1541                 if (p_node && p_going)
1542                     p_node = p_node->p.other;
1543                 if (p_node == NULL) {
1544                     p_going = false;
1545                 } else {
1546                     p_nodes ++;
1547                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1548                     if (p_node->selected) {
1549                         p_sel_nodes ++;
1550                         p_sel_range = p_range;
1551                     }
1552                     if (p_node == n_node) {
1553                         n_going = false;
1554                         p_going = false;
1555                     }
1556                 }
1557             } while (n_going || p_going);
1558         }
1560         // Second pass: actually move nodes in this subpath
1561         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1562         {
1563             double n_range = 0, p_range = 0;
1564             bool n_going = true, p_going = true;
1565             Inkscape::NodePath::Node *n_node = n;
1566             Inkscape::NodePath::Node *p_node = n;
1567             do {
1568                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1569                 if (n_node && n_going)
1570                     n_node = n_node->n.other;
1571                 if (n_node == NULL) {
1572                     n_going = false;
1573                 } else {
1574                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1575                     if (n_node->selected) {
1576                         sp_nodepath_move_node_and_handles (n_node,
1577                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1578                                                            sculpt_profile ((n_range + Geom::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1579                                                            sculpt_profile ((n_range - Geom::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1580                     }
1581                     if (n_node == p_node) {
1582                         n_going = false;
1583                         p_going = false;
1584                     }
1585                 }
1586                 if (p_node && p_going)
1587                     p_node = p_node->p.other;
1588                 if (p_node == NULL) {
1589                     p_going = false;
1590                 } else {
1591                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1592                     if (p_node->selected) {
1593                         sp_nodepath_move_node_and_handles (p_node,
1594                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1595                                                            sculpt_profile ((p_range - Geom::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1596                                                            sculpt_profile ((p_range + Geom::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1597                     }
1598                     if (p_node == n_node) {
1599                         n_going = false;
1600                         p_going = false;
1601                     }
1602                 }
1603             } while (n_going || p_going);
1604         }
1606     } else {
1607         // Multiple subpaths have selected nodes:
1608         // use spatial mode, where the distance from n to node being dragged is measured directly as Geom::L2.
1609         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1610         // fix the pear-like shape when sculpting e.g. a ring
1612         // First pass: calculate range
1613         gdouble direct_range = 0;
1614         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1615             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1616             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1617                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1618                 if (node->selected) {
1619                     direct_range = MAX(direct_range, Geom::L2(node->origin - n->origin));
1620                 }
1621             }
1622         }
1624         // Second pass: actually move nodes
1625         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1626             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1627             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1628                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1629                 if (node->selected) {
1630                     if (direct_range > 1e-6) {
1631                         sp_nodepath_move_node_and_handles (node,
1632                                                        sculpt_profile (Geom::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1633                                                        sculpt_profile (Geom::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1634                                                        sculpt_profile (Geom::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1635                     } else {
1636                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1637                     }
1639                 }
1640             }
1641         }
1642     }
1644     // do not update repr here so that node dragging is acceptably fast
1645     update_object(nodepath);
1649 /**
1650  * Move node selection to point, adjust its and neighbouring handles,
1651  * handle possible snapping, and commit the change with possible undo.
1652  */
1653 void
1654 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1656     if (!nodepath) return;
1658     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1660     if (dx == 0) {
1661         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1662     } else if (dy == 0) {
1663         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1664     } else {
1665         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1666     }
1669 /**
1670  * Move node selection off screen and commit the change.
1671  */
1672 void
1673 sp_node_selected_move_screen(SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1675     // borrowed from sp_selection_move_screen in selection-chemistry.c
1676     // we find out the current zoom factor and divide deltas by it
1678     gdouble zoom = desktop->current_zoom();
1679     gdouble zdx = dx / zoom;
1680     gdouble zdy = dy / zoom;
1682     if (!nodepath) return;
1684     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1686     if (dx == 0) {
1687         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1688     } else if (dy == 0) {
1689         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1690     } else {
1691         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1692     }
1695 /**
1696  * Move selected nodes to the absolute position given
1697  */
1698 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis)
1700     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1701         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1702         Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]);
1703         sp_node_moveto(n, npos);
1704     }
1706     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1709 /**
1710  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return Geom::Nothing
1711  */
1712 boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1714     boost::optional<Geom::Coord> no_coord;
1715     g_return_val_if_fail(nodepath->selected, no_coord);
1717     // determine coordinate of first selected node
1718     GList *nsel = nodepath->selected;
1719     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1720     Geom::Coord coord = n->pos[axis];
1721     bool coincide = true;
1723     // compare it to the coordinates of all the other selected nodes
1724     for (GList *l = nsel->next; l != NULL; l = l->next) {
1725         n = (Inkscape::NodePath::Node *) l->data;
1726         if (n->pos[axis] != coord) {
1727             coincide = false;
1728         }
1729     }
1730     if (coincide) {
1731         return coord;
1732     } else {
1733         Geom::Rect bbox = sp_node_selected_bbox(nodepath);
1734         // currently we return the coordinate of the bounding box midpoint because I don't know how
1735         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1736         return bbox.midpoint()[axis];
1737     }
1740 /** If they don't yet exist, creates knot and line for the given side of the node */
1741 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1743     if (!side->knot) {
1744         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"));
1746         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1747         side->knot->setSize (7);
1748         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1749         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1750         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1751         sp_knot_update_ctrl(side->knot);
1753         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1754         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1755         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1756         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1757         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1758         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1759     }
1761     if (!side->line) {
1762         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1763                                         SP_TYPE_CTRLLINE, NULL);
1764     }
1767 /**
1768  * Ensure the given handle of the node is visible/invisible, update its screen position
1769  */
1770 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1772     g_assert(node != NULL);
1774    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1775     NRPathcode code = sp_node_path_code_from_side(node, side);
1777     show_handle = show_handle && (code == NR_CURVETO) && (Geom::L2(side->pos - node->pos) > 1e-6);
1779     if (show_handle) {
1780         if (!side->knot) { // No handle knot at all
1781             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1782             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1783             side->knot->pos = side->pos;
1784             if (side->knot->item)
1785                 SP_CTRL(side->knot->item)->moveto(side->pos);
1786             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1787             sp_knot_show(side->knot);
1788         } else {
1789             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1790                 if (fire_move_signals) {
1791                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1792                 } else {
1793                     sp_knot_moveto(side->knot, side->pos);
1794                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1795                 }
1796             }
1797             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1798                 sp_knot_show(side->knot);
1799             }
1800         }
1801         sp_canvas_item_show(side->line);
1802     } else {
1803         if (side->knot) {
1804             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1805                 sp_knot_hide(side->knot);
1806             }
1807         }
1808         if (side->line) {
1809             sp_canvas_item_hide(side->line);
1810         }
1811     }
1814 /**
1815  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1816  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1817  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1818  * updated; otherwise, just move the knots silently (used in batch moves).
1819  */
1820 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1822     g_assert(node != NULL);
1824     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1825         sp_knot_show(node->knot);
1826     }
1828     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1829         if (fire_move_signals)
1830             sp_knot_set_position(node->knot, node->pos, 0);
1831         else
1832             sp_knot_moveto(node->knot, node->pos);
1833     }
1835     gboolean show_handles = node->selected;
1836     if (node->p.other != NULL) {
1837         if (node->p.other->selected) show_handles = TRUE;
1838     }
1839     if (node->n.other != NULL) {
1840         if (node->n.other->selected) show_handles = TRUE;
1841     }
1843     if (node->subpath->nodepath->show_handles == false)
1844         show_handles = FALSE;
1846     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1847     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1850 /**
1851  * Call sp_node_update_handles() for all nodes on subpath.
1852  */
1853 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1855     g_assert(subpath != NULL);
1857     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1858         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1859     }
1862 /**
1863  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1864  */
1865 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1867     g_assert(nodepath != NULL);
1869     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1870         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1871     }
1874 void
1875 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1877     if (nodepath) {
1878         nodepath->show_handles = show;
1879         sp_nodepath_update_handles(nodepath);
1880     }
1883 /**
1884  * Adds all selected nodes in nodepath to list.
1885  */
1886 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1888     StlConv<Node *>::list(l, selected);
1889 /// \todo this adds a copying, rework when the selection becomes a stl list
1892 /**
1893  * Align selected nodes on the specified axis.
1894  */
1895 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1897     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1898         return;
1899     }
1901     if ( !nodepath->selected->next ) { // only one node selected
1902         return;
1903     }
1904    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1905     Geom::Point dest(pNode->pos);
1906     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1907         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1908         if (pNode) {
1909             dest[axis] = pNode->pos[axis];
1910             sp_node_moveto(pNode, dest);
1911         }
1912     }
1914     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1917 /// Helper struct.
1918 struct NodeSort
1920    Inkscape::NodePath::Node *_node;
1921     Geom::Coord _coord;
1922     /// \todo use vectorof pointers instead of calling copy ctor
1923     NodeSort(Inkscape::NodePath::Node *node, Geom::Dim2 axis) :
1924         _node(node), _coord(node->pos[axis])
1925     {}
1927 };
1929 static bool operator<(NodeSort const &a, NodeSort const &b)
1931     return (a._coord < b._coord);
1934 /**
1935  * Distribute selected nodes on the specified axis.
1936  */
1937 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1939     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1940         return;
1941     }
1943     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1944         return;
1945     }
1947    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1948     std::vector<NodeSort> sorted;
1949     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1950         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1951         if (pNode) {
1952             NodeSort n(pNode, axis);
1953             sorted.push_back(n);
1954             //dest[axis] = pNode->pos[axis];
1955             //sp_node_moveto(pNode, dest);
1956         }
1957     }
1958     std::sort(sorted.begin(), sorted.end());
1959     unsigned int len = sorted.size();
1960     //overall bboxes span
1961     float dist = (sorted.back()._coord -
1962                   sorted.front()._coord);
1963     //new distance between each bbox
1964     float step = (dist) / (len - 1);
1965     float pos = sorted.front()._coord;
1966     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1967           it < sorted.end();
1968           it ++ )
1969     {
1970         Geom::Point dest((*it)._node->pos);
1971         dest[axis] = pos;
1972         sp_node_moveto((*it)._node, dest);
1973         pos += step;
1974     }
1976     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1980 /**
1981  * Call sp_nodepath_line_add_node() for all selected segments.
1982  */
1983 void
1984 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1986     if (!nodepath) {
1987         return;
1988     }
1990     GList *nl = NULL;
1992     int n_added = 0;
1994     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1995        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1996         g_assert(t->selected);
1997         if (t->p.other && t->p.other->selected) {
1998             nl = g_list_prepend(nl, t);
1999         }
2000     }
2002     while (nl) {
2003        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
2004        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
2005        sp_nodepath_node_select(n, TRUE, FALSE);
2006        n_added ++;
2007        nl = g_list_remove(nl, t);
2008     }
2010     /** \todo fixme: adjust ? */
2011     sp_nodepath_update_handles(nodepath);
2013     if (n_added > 1) {
2014         sp_nodepath_update_repr(nodepath, _("Add nodes"));
2015     } else if (n_added > 0) {
2016         sp_nodepath_update_repr(nodepath, _("Add node"));
2017     }
2019     sp_nodepath_update_statusbar(nodepath);
2022 /**
2023  * Select segment nearest to point
2024  */
2025 void
2026 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p, bool toggle)
2028     if (!nodepath) {
2029         return;
2030     }
2032     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2033     Geom::PathVector const &pathv = curve->get_pathvector();
2034     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2035     if (!pvpos) {
2036         g_print ("Possible error?\n");
2037         return;
2038     }
2040     // calculate index for nodepath's representation.
2041     unsigned int segment_index = floor(pvpos->t) + 1;
2042     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2043         segment_index += pathv[i].size() + 1;
2044         if (pathv[i].closed()) {
2045             segment_index += 1;
2046         }
2047     }
2049     curve->unref();
2051     //find segment to segment
2052     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2054     //fixme: this can return NULL, so check before proceeding.
2055     g_return_if_fail(e != NULL);
2057     gboolean force = FALSE;
2058     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
2059         force = TRUE;
2060     }
2061     sp_nodepath_node_select(e, (gboolean) toggle, force);
2062     if (e->p.other)
2063         sp_nodepath_node_select(e->p.other, TRUE, force);
2065     sp_nodepath_update_handles(nodepath);
2067     sp_nodepath_update_statusbar(nodepath);
2070 /**
2071  * Add a node nearest to point
2072  */
2073 void
2074 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p)
2076     if (!nodepath) {
2077         return;
2078     }
2080     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2081     Geom::PathVector const &pathv = curve->get_pathvector();
2082     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2083     if (!pvpos) {
2084         g_print ("Possible error?\n");
2085         return;
2086     }
2088     // calculate index for nodepath's representation.
2089     double int_part;
2090     double t = std::modf(pvpos->t, &int_part);
2091     unsigned int segment_index = (unsigned int)int_part + 1;
2092     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2093         segment_index += pathv[i].size() + 1;
2094         if (pathv[i].closed()) {
2095             segment_index += 1;
2096         }
2097     }
2099     curve->unref();
2101     //find segment to split
2102     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2103     if (!e) {
2104         return;
2105     }
2107     //don't know why but t seems to flip for lines
2108     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2109         t = 1.0 - t;
2110     }
2112     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2113     sp_nodepath_node_select(n, FALSE, TRUE);
2115     /* fixme: adjust ? */
2116     sp_nodepath_update_handles(nodepath);
2118     sp_nodepath_update_repr(nodepath, _("Add node"));
2120     sp_nodepath_update_statusbar(nodepath);
2123 /*
2124  * Adjusts a segment so that t moves by a certain delta for dragging
2125  * converts lines to curves
2126  *
2127  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2128  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2129  */
2130 void
2131 sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, Geom::Point delta)
2133     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node);
2135     //fixme: e and e->p can be NULL, so check for those before proceeding
2136     g_return_if_fail(e != NULL);
2137     g_return_if_fail(&e->p != NULL);
2139     if (e->type == Inkscape::NodePath::NODE_AUTO) {
2140         e->type = Inkscape::NodePath::NODE_SMOOTH;
2141         sp_nodepath_update_node_knot (e);
2142     }
2143     if (e->p.other->type == Inkscape::NodePath::NODE_AUTO) {
2144         e->p.other->type = Inkscape::NodePath::NODE_SMOOTH;
2145         sp_nodepath_update_node_knot (e->p.other);
2146     }
2148     /* feel good is an arbitrary parameter that distributes the delta between handles
2149      * if t of the drag point is less than 1/6 distance form the endpoint only
2150      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2151      */
2152     double feel_good;
2153     if (t <= 1.0 / 6.0)
2154         feel_good = 0;
2155     else if (t <= 0.5)
2156         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2157     else if (t <= 5.0 / 6.0)
2158         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2159     else
2160         feel_good = 1;
2162     //if we're dragging a line convert it to a curve
2163     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2164         sp_nodepath_set_line_type(e, NR_CURVETO);
2165     }
2167     Geom::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2168     Geom::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2169     e->p.other->n.pos += offsetcoord0;
2170     e->p.pos += offsetcoord1;
2172     // adjust handles of adjacent nodes where necessary
2173     sp_node_adjust_handle(e,1);
2174     sp_node_adjust_handle(e->p.other,-1);
2176     sp_nodepath_update_handles(e->subpath->nodepath);
2178     update_object(e->subpath->nodepath);
2180     sp_nodepath_update_statusbar(e->subpath->nodepath);
2184 /**
2185  * Call sp_nodepath_break() for all selected segments.
2186  */
2187 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2189     if (!nodepath) return;
2191     GList *tempin = g_list_copy(nodepath->selected);
2192     GList *temp = NULL;
2193     for (GList *l = tempin; l != NULL; l = l->next) {
2194        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2195        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2196         if (nn == NULL) continue; // no break, no new node
2197         temp = g_list_prepend(temp, nn);
2198     }
2199     g_list_free(tempin);
2201     if (temp) {
2202         sp_nodepath_deselect(nodepath);
2203     }
2204     for (GList *l = temp; l != NULL; l = l->next) {
2205         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2206     }
2208     sp_nodepath_update_handles(nodepath);
2210     sp_nodepath_update_repr(nodepath, _("Break path"));
2213 /**
2214  * Duplicate the selected node(s).
2215  */
2216 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2218     if (!nodepath) {
2219         return;
2220     }
2222     GList *temp = NULL;
2223     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2224        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2225        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2226         if (nn == NULL) continue; // could not duplicate
2227         temp = g_list_prepend(temp, nn);
2228     }
2230     if (temp) {
2231         sp_nodepath_deselect(nodepath);
2232     }
2233     for (GList *l = temp; l != NULL; l = l->next) {
2234         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2235     }
2237     sp_nodepath_update_handles(nodepath);
2239     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2242 /**
2243  *  Internal function to join two nodes by merging them into one.
2244  */
2245 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2247     /* a and b are endpoints */
2249     // if one of the two nodes is mouseovered, fix its position
2250     Geom::Point c;
2251     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2252         c = a->pos;
2253     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2254         c = b->pos;
2255     } else {
2256         // otherwise, move joined node to the midpoint
2257         c = (a->pos + b->pos) / 2;
2258     }
2260     if (a->subpath == b->subpath) {
2261        Inkscape::NodePath::SubPath *sp = a->subpath;
2262         sp_nodepath_subpath_close(sp);
2263         sp_node_moveto (sp->first, c);
2265         sp_nodepath_update_handles(sp->nodepath);
2266         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2267         return;
2268     }
2270     /* a and b are separate subpaths */
2271     Inkscape::NodePath::SubPath *sa = a->subpath;
2272     Inkscape::NodePath::SubPath *sb = b->subpath;
2273     Geom::Point p;
2274     Inkscape::NodePath::Node *n;
2275     NRPathcode code;
2276     if (a == sa->first) {
2277         // we will now reverse sa, so that a is its last node, not first, and drop that node
2278         p = sa->first->n.pos;
2279         code = (NRPathcode)sa->first->n.other->code;
2280         // create new subpath
2281        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2282        // create a first moveto node on it
2283         n = sa->last;
2284         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2285         n = n->p.other;
2286         if (n == sa->first) n = NULL;
2287         while (n) {
2288             // copy the rest of the nodes from sa to t, going backwards
2289             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2290             n = n->p.other;
2291             if (n == sa->first) n = NULL;
2292         }
2293         // replace sa with t
2294         sp_nodepath_subpath_destroy(sa);
2295         sa = t;
2296     } else if (a == sa->last) {
2297         // a is already last, just drop it
2298         p = sa->last->p.pos;
2299         code = (NRPathcode)sa->last->code;
2300         sp_nodepath_node_destroy(sa->last);
2301     } else {
2302         code = NR_END;
2303         g_assert_not_reached();
2304     }
2306     if (b == sb->first) {
2307         // copy all nodes from b to a, forward
2308         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2309         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2310             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2311         }
2312     } else if (b == sb->last) {
2313         // copy all nodes from b to a, backward
2314         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2315         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2316             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2317         }
2318     } else {
2319         g_assert_not_reached();
2320     }
2321     /* and now destroy sb */
2323     sp_nodepath_subpath_destroy(sb);
2325     sp_nodepath_update_handles(sa->nodepath);
2327     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2329     sp_nodepath_update_statusbar(nodepath);
2332 /**
2333  *  Internal function to join two nodes by adding a segment between them.
2334  */
2335 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2337     if (a->subpath == b->subpath) {
2338        Inkscape::NodePath::SubPath *sp = a->subpath;
2340         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2341         sp->closed = TRUE;
2343         sp->first->p.other = sp->last;
2344         sp->last->n.other  = sp->first;
2346         sp_node_handle_mirror_p_to_n(sp->last);
2347         sp_node_handle_mirror_n_to_p(sp->first);
2349         sp->first->code = sp->last->code;
2350         sp->first       = sp->last;
2352         sp_nodepath_update_handles(sp->nodepath);
2354         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2356         return;
2357     }
2359     /* a and b are separate subpaths */
2360     Inkscape::NodePath::SubPath *sa = a->subpath;
2361     Inkscape::NodePath::SubPath *sb = b->subpath;
2363     Inkscape::NodePath::Node *n;
2364     Geom::Point p;
2365     NRPathcode code;
2366     if (a == sa->first) {
2367         code = (NRPathcode) sa->first->n.other->code;
2368        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2369         n = sa->last;
2370         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2371         for (n = n->p.other; n != NULL; n = n->p.other) {
2372             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2373         }
2374         sp_nodepath_subpath_destroy(sa);
2375         sa = t;
2376     } else if (a == sa->last) {
2377         code = (NRPathcode)sa->last->code;
2378     } else {
2379         code = NR_END;
2380         g_assert_not_reached();
2381     }
2383     if (b == sb->first) {
2384         n = sb->first;
2385         sp_node_handle_mirror_p_to_n(sa->last);
2386         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2387         sp_node_handle_mirror_n_to_p(sa->last);
2388         for (n = n->n.other; n != NULL; n = n->n.other) {
2389             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2390         }
2391     } else if (b == sb->last) {
2392         n = sb->last;
2393         sp_node_handle_mirror_p_to_n(sa->last);
2394         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2395         sp_node_handle_mirror_n_to_p(sa->last);
2396         for (n = n->p.other; n != NULL; n = n->p.other) {
2397             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2398         }
2399     } else {
2400         g_assert_not_reached();
2401     }
2402     /* and now destroy sb */
2404     sp_nodepath_subpath_destroy(sb);
2406     sp_nodepath_update_handles(sa->nodepath);
2408     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2411 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2413 /**
2414  * Internal function to handle joining two nodes.
2415  */
2416 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2418     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2420     if (g_list_length(nodepath->selected) != 2) {
2421         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2422         return;
2423     }
2425     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2426     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2428     g_assert(a != b);
2429     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2430         // someone tried to join an orphan node (i.e. a single-node subpath).
2431         // this is not worth an error message, just fail silently.
2432         return;
2433     }
2435     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2436         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2437         return;
2438     }
2440     switch(mode) {
2441         case NODE_JOIN_ENDPOINTS:
2442             do_node_selected_join(nodepath, a, b);
2443             break;
2444         case NODE_JOIN_SEGMENT:
2445             do_node_selected_join_segment(nodepath, a, b);
2446             break;
2447     }
2450 /**
2451  *  Join two nodes by merging them into one.
2452  */
2453 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2455     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2458 /**
2459  *  Join two nodes by adding a segment between them.
2460  */
2461 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2463     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2466 /**
2467  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2468  */
2469 void sp_node_delete_preserve(GList *nodes_to_delete)
2471     GSList *nodepaths = NULL;
2473     while (nodes_to_delete) {
2474         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2475         Inkscape::NodePath::SubPath *sp = node->subpath;
2476         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2477         Inkscape::NodePath::Node *sample_cursor = NULL;
2478         Inkscape::NodePath::Node *sample_end = NULL;
2479         Inkscape::NodePath::Node *delete_cursor = node;
2480         bool just_delete = false;
2482         //find the start of this contiguous selection
2483         //move left to the first node that is not selected
2484         //or the start of the non-closed path
2485         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2486             delete_cursor = curr;
2487         }
2489         //just delete at the beginning of an open path
2490         if (!delete_cursor->p.other) {
2491             sample_cursor = delete_cursor;
2492             just_delete = true;
2493         } else {
2494             sample_cursor = delete_cursor->p.other;
2495         }
2497         //calculate points for each segment
2498         int rate = 5;
2499         float period = 1.0 / rate;
2500         std::vector<Geom::Point> data;
2501         if (!just_delete) {
2502             data.push_back(sample_cursor->pos);
2503             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2504                 //just delete at the end of an open path
2505                 if (!sp->closed && curr == sp->last) {
2506                     just_delete = true;
2507                     break;
2508                 }
2510                 //sample points on the contiguous selected segment
2511                 Geom::Point *bez;
2512                 bez = new Geom::Point [4];
2513                 bez[0] = curr->pos;
2514                 bez[1] = curr->n.pos;
2515                 bez[2] = curr->n.other->p.pos;
2516                 bez[3] = curr->n.other->pos;
2517                 for (int i=1; i<rate; i++) {
2518                     gdouble t = i * period;
2519                     Geom::Point p = bezier_pt(3, bez, t);
2520                     data.push_back(p);
2521                 }
2522                 data.push_back(curr->n.other->pos);
2524                 sample_end = curr->n.other;
2525                 //break if we've come full circle or hit the end of the selection
2526                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2527                     break;
2528                 }
2529             }
2530         }
2532         if (!just_delete) {
2533             //calculate the best fitting single segment and adjust the endpoints
2534             Geom::Point *adata;
2535             adata = new Geom::Point [data.size()];
2536             copy(data.begin(), data.end(), adata);
2538             Geom::Point *bez;
2539             bez = new Geom::Point [4];
2540             //would decreasing error create a better fitting approximation?
2541             gdouble error = 1.0;
2542             gint ret;
2543             ret = Geom::bezier_fit_cubic (bez, adata, data.size(), error);
2545             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2546             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2547             //the resulting nodes behave as expected.
2548             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2549                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2550             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2551                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2553             //adjust endpoints
2554             sample_cursor->n.pos = bez[1];
2555             sample_end->p.pos = bez[2];
2556         }
2558         //destroy this contiguous selection
2559         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2560             Inkscape::NodePath::Node *temp = delete_cursor;
2561             if (delete_cursor->n.other == delete_cursor) {
2562                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2563                 delete_cursor = NULL;
2564             } else {
2565                 delete_cursor = delete_cursor->n.other;
2566             }
2567             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2568             sp_nodepath_node_destroy(temp);
2569         }
2571         sp_nodepath_update_handles(nodepath);
2573         if (!g_slist_find(nodepaths, nodepath))
2574             nodepaths = g_slist_prepend (nodepaths, nodepath);
2575     }
2577     for (GSList *i = nodepaths; i; i = i->next) {
2578         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2579         // different nodepaths will give us one undo event per nodepath
2580         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2582         // if the entire nodepath is removed, delete the selected object.
2583         if (nodepath->subpaths == NULL ||
2584             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2585             //at least 2
2586             sp_nodepath_get_node_count(nodepath) < 2) {
2587             SPDocument *document = sp_desktop_document (nodepath->desktop);
2588             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2589             //delete this nodepath's object, not the entire selection! (though at this time, this
2590             //does not matter)
2591             sp_selection_delete(nodepath->desktop);
2592             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2593                               _("Delete nodes"));
2594         } else {
2595             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2596             sp_nodepath_update_statusbar(nodepath);
2597         }
2598     }
2600     g_slist_free (nodepaths);
2603 /**
2604  * Delete one or more selected nodes.
2605  */
2606 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2608     if (!nodepath) return;
2609     if (!nodepath->selected) return;
2611     /** \todo fixme: do it the right way */
2612     while (nodepath->selected) {
2613        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2614         sp_nodepath_node_destroy(node);
2615     }
2618     //clean up the nodepath (such as for trivial subpaths)
2619     sp_nodepath_cleanup(nodepath);
2621     sp_nodepath_update_handles(nodepath);
2623     // if the entire nodepath is removed, delete the selected object.
2624     if (nodepath->subpaths == NULL ||
2625         sp_nodepath_get_node_count(nodepath) < 2) {
2626         SPDocument *document = sp_desktop_document (nodepath->desktop);
2627         sp_selection_delete(nodepath->desktop);
2628         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2629                           _("Delete nodes"));
2630         return;
2631     }
2633     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2635     sp_nodepath_update_statusbar(nodepath);
2638 /**
2639  * Delete one or more segments between two selected nodes.
2640  * This is the code for 'split'.
2641  */
2642 void
2643 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2645    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2646    Inkscape::NodePath::Node *curr, *next;     //Iterators
2648     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2650     if (g_list_length(nodepath->selected) != 2) {
2651         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2652                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2653         return;
2654     }
2656     //Selected nodes, not inclusive
2657    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2658    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2660     if ( ( a==b)                       ||  //same node
2661          (a->subpath  != b->subpath )  ||  //not the same path
2662          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2663          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2664     {
2665         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2666                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2667         return;
2668     }
2670     //###########################################
2671     //# BEGIN EDITS
2672     //###########################################
2673     //##################################
2674     //# CLOSED PATH
2675     //##################################
2676     if (a->subpath->closed) {
2679         gboolean reversed = FALSE;
2681         //Since we can go in a circle, we need to find the shorter distance.
2682         //  a->b or b->a
2683         start = end = NULL;
2684         int distance    = 0;
2685         int minDistance = 0;
2686         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2687             if (curr==b) {
2688                 //printf("a to b:%d\n", distance);
2689                 start = a;//go from a to b
2690                 end   = b;
2691                 minDistance = distance;
2692                 //printf("A to B :\n");
2693                 break;
2694             }
2695             distance++;
2696         }
2698         //try again, the other direction
2699         distance = 0;
2700         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2701             if (curr==a) {
2702                 //printf("b to a:%d\n", distance);
2703                 if (distance < minDistance) {
2704                     start    = b;  //we go from b to a
2705                     end      = a;
2706                     reversed = TRUE;
2707                     //printf("B to A\n");
2708                 }
2709                 break;
2710             }
2711             distance++;
2712         }
2715         //Copy everything from 'end' to 'start' to a new subpath
2716        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2717         for (curr=end ; curr ; curr=curr->n.other) {
2718             NRPathcode code = (NRPathcode) curr->code;
2719             if (curr == end)
2720                 code = NR_MOVETO;
2721             sp_nodepath_node_new(t, NULL,
2722                                  (Inkscape::NodePath::NodeType)curr->type, code,
2723                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2724             if (curr == start)
2725                 break;
2726         }
2727         sp_nodepath_subpath_destroy(a->subpath);
2730     }
2734     //##################################
2735     //# OPEN PATH
2736     //##################################
2737     else {
2739         //We need to get the direction of the list between A and B
2740         //Can we walk from a to b?
2741         start = end = NULL;
2742         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2743             if (curr==b) {
2744                 start = a;  //did it!  we go from a to b
2745                 end   = b;
2746                 //printf("A to B\n");
2747                 break;
2748             }
2749         }
2750         if (!start) {//didn't work?  let's try the other direction
2751             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2752                 if (curr==a) {
2753                     start = b;  //did it!  we go from b to a
2754                     end   = a;
2755                     //printf("B to A\n");
2756                     break;
2757                 }
2758             }
2759         }
2760         if (!start) {
2761             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2762                                                      _("Cannot find path between nodes."));
2763             return;
2764         }
2768         //Copy everything after 'end' to a new subpath
2769        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2770         for (curr=end ; curr ; curr=curr->n.other) {
2771             NRPathcode code = (NRPathcode) curr->code;
2772             if (curr == end)
2773                 code = NR_MOVETO;
2774             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2775                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2776         }
2778         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2779         for (curr = start->n.other ; curr  ; curr=next) {
2780             next = curr->n.other;
2781             sp_nodepath_node_destroy(curr);
2782         }
2784     }
2785     //###########################################
2786     //# END EDITS
2787     //###########################################
2789     //clean up the nodepath (such as for trivial subpaths)
2790     sp_nodepath_cleanup(nodepath);
2792     sp_nodepath_update_handles(nodepath);
2794     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2796     sp_nodepath_update_statusbar(nodepath);
2799 /**
2800  * Call sp_nodepath_set_line() for all selected segments.
2801  */
2802 void
2803 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2805     if (nodepath == NULL) return;
2807     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2808        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2809         g_assert(n->selected);
2810         if (n->p.other && n->p.other->selected) {
2811             sp_nodepath_set_line_type(n, code);
2812         }
2813     }
2815     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2818 /**
2819  * Call sp_nodepath_convert_node_type() for all selected nodes.
2820  */
2821 void
2822 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2824     if (nodepath == NULL) return;
2826     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2828     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2829         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2830     }
2832     sp_nodepath_update_repr(nodepath, _("Change node type"));
2835 /**
2836  * Change select status of node, update its own and neighbour handles.
2837  */
2838 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2840     node->selected = selected;
2842     if (selected) {
2843         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 11 : 9);
2844         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2845         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2846         sp_knot_update_ctrl(node->knot);
2847     } else {
2848         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 9 : 7);
2849         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2850         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2851         sp_knot_update_ctrl(node->knot);
2852     }
2854     sp_node_update_handles(node);
2855     if (node->n.other) sp_node_update_handles(node->n.other);
2856     if (node->p.other) sp_node_update_handles(node->p.other);
2859 /**
2860 \brief Select a node
2861 \param node     The node to select
2862 \param incremental   If true, add to selection, otherwise deselect others
2863 \param override   If true, always select this node, otherwise toggle selected status
2864 */
2865 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2867     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2869     if (incremental) {
2870         if (override) {
2871             if (!g_list_find(nodepath->selected, node)) {
2872                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2873             }
2874             sp_node_set_selected(node, TRUE);
2875         } else { // toggle
2876             if (node->selected) {
2877                 g_assert(g_list_find(nodepath->selected, node));
2878                 nodepath->selected = g_list_remove(nodepath->selected, node);
2879             } else {
2880                 g_assert(!g_list_find(nodepath->selected, node));
2881                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2882             }
2883             sp_node_set_selected(node, !node->selected);
2884         }
2885     } else {
2886         sp_nodepath_deselect(nodepath);
2887         nodepath->selected = g_list_prepend(nodepath->selected, node);
2888         sp_node_set_selected(node, TRUE);
2889     }
2891     sp_nodepath_update_statusbar(nodepath);
2895 /**
2896 \brief Deselect all nodes in the nodepath
2897 */
2898 void
2899 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2901     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2903     while (nodepath->selected) {
2904         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2905         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2906     }
2907     sp_nodepath_update_statusbar(nodepath);
2910 /**
2911 \brief Select or invert selection of all nodes in the nodepath
2912 */
2913 void
2914 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2916     if (!nodepath) return;
2918     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2919        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2920         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2921            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2922            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2923         }
2924     }
2927 /**
2928  * If nothing selected, does the same as sp_nodepath_select_all();
2929  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2930  * (i.e., similar to "select all in layer", with the "selected" subpaths
2931  * being treated as "layers" in the path).
2932  */
2933 void
2934 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2936     if (!nodepath) return;
2938     if (g_list_length (nodepath->selected) == 0) {
2939         sp_nodepath_select_all (nodepath, invert);
2940         return;
2941     }
2943     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2944     GSList *subpaths = NULL;
2946     for (GList *l = copy; l != NULL; l = l->next) {
2947         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2948         Inkscape::NodePath::SubPath *subpath = n->subpath;
2949         if (!g_slist_find (subpaths, subpath))
2950             subpaths = g_slist_prepend (subpaths, subpath);
2951     }
2953     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2954         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2955         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2956             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2957             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2958         }
2959     }
2961     g_slist_free (subpaths);
2962     g_list_free (copy);
2965 /**
2966  * \brief Select the node after the last selected; if none is selected,
2967  * select the first within path.
2968  */
2969 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2971     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2973    Inkscape::NodePath::Node *last = NULL;
2974     if (nodepath->selected) {
2975         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2976            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2977             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2978             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2979                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2980                 if (node->selected) {
2981                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2982                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2983                             if (spl->next) { // there's a next subpath
2984                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2985                                 last = subpath_next->first;
2986                             } else if (spl->prev) { // there's a previous subpath
2987                                 last = NULL; // to be set later to the first node of first subpath
2988                             } else {
2989                                 last = node->n.other;
2990                             }
2991                         } else {
2992                             last = node->n.other;
2993                         }
2994                     } else {
2995                         if (node->n.other) {
2996                             last = node->n.other;
2997                         } else {
2998                             if (spl->next) { // there's a next subpath
2999                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
3000                                 last = subpath_next->first;
3001                             } else if (spl->prev) { // there's a previous subpath
3002                                 last = NULL; // to be set later to the first node of first subpath
3003                             } else {
3004                                 last = (Inkscape::NodePath::Node *) subpath->first;
3005                             }
3006                         }
3007                     }
3008                 }
3009             }
3010         }
3011         sp_nodepath_deselect(nodepath);
3012     }
3014     if (last) { // there's at least one more node after selected
3015         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3016     } else { // no more nodes, select the first one in first subpath
3017        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
3018         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
3019     }
3022 /**
3023  * \brief Select the node before the first selected; if none is selected,
3024  * select the last within path
3025  */
3026 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
3028     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
3030    Inkscape::NodePath::Node *last = NULL;
3031     if (nodepath->selected) {
3032         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
3033            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3034             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
3035                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3036                 if (node->selected) {
3037                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
3038                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
3039                             if (spl->prev) { // there's a prev subpath
3040                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3041                                 last = subpath_prev->last;
3042                             } else if (spl->next) { // there's a next subpath
3043                                 last = NULL; // to be set later to the last node of last subpath
3044                             } else {
3045                                 last = node->p.other;
3046                             }
3047                         } else {
3048                             last = node->p.other;
3049                         }
3050                     } else {
3051                         if (node->p.other) {
3052                             last = node->p.other;
3053                         } else {
3054                             if (spl->prev) { // there's a prev subpath
3055                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3056                                 last = subpath_prev->last;
3057                             } else if (spl->next) { // there's a next subpath
3058                                 last = NULL; // to be set later to the last node of last subpath
3059                             } else {
3060                                 last = (Inkscape::NodePath::Node *) subpath->last;
3061                             }
3062                         }
3063                     }
3064                 }
3065             }
3066         }
3067         sp_nodepath_deselect(nodepath);
3068     }
3070     if (last) { // there's at least one more node before selected
3071         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3072     } else { // no more nodes, select the last one in last subpath
3073         GList *spl = g_list_last(nodepath->subpaths);
3074        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3075         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
3076     }
3079 /**
3080  * \brief Select all nodes that are within the rectangle.
3081  */
3082 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, Geom::Rect const &b, gboolean incremental)
3084     if (!incremental) {
3085         sp_nodepath_deselect(nodepath);
3086     }
3088     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3089        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3090         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3091            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3093             if (b.contains(node->pos)) {
3094                 sp_nodepath_node_select(node, TRUE, TRUE);
3095             }
3096         }
3097     }
3101 void
3102 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3104     g_assert (n);
3105     g_assert (nodepath);
3106     g_assert (n->subpath->nodepath == nodepath);
3108     if (g_list_length (nodepath->selected) == 0) {
3109         if (grow > 0) {
3110             sp_nodepath_node_select(n, TRUE, TRUE);
3111         }
3112         return;
3113     }
3115     if (g_list_length (nodepath->selected) == 1) {
3116         if (grow < 0) {
3117             sp_nodepath_deselect (nodepath);
3118             return;
3119         }
3120     }
3122         double n_sel_range = 0, p_sel_range = 0;
3123             Inkscape::NodePath::Node *farthest_n_node = n;
3124             Inkscape::NodePath::Node *farthest_p_node = n;
3126         // Calculate ranges
3127         {
3128             double n_range = 0, p_range = 0;
3129             bool n_going = true, p_going = true;
3130             Inkscape::NodePath::Node *n_node = n;
3131             Inkscape::NodePath::Node *p_node = n;
3132             do {
3133                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3134                 if (n_node && n_going)
3135                     n_node = n_node->n.other;
3136                 if (n_node == NULL) {
3137                     n_going = false;
3138                 } else {
3139                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3140                     if (n_node->selected) {
3141                         n_sel_range = n_range;
3142                         farthest_n_node = n_node;
3143                     }
3144                     if (n_node == p_node) {
3145                         n_going = false;
3146                         p_going = false;
3147                     }
3148                 }
3149                 if (p_node && p_going)
3150                     p_node = p_node->p.other;
3151                 if (p_node == NULL) {
3152                     p_going = false;
3153                 } else {
3154                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3155                     if (p_node->selected) {
3156                         p_sel_range = p_range;
3157                         farthest_p_node = p_node;
3158                     }
3159                     if (p_node == n_node) {
3160                         n_going = false;
3161                         p_going = false;
3162                     }
3163                 }
3164             } while (n_going || p_going);
3165         }
3167     if (grow > 0) {
3168         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3169                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3170         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3171                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3172         }
3173     } else {
3174         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3175                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3176         } else if (farthest_p_node && farthest_p_node->selected) {
3177                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3178         }
3179     }
3182 void
3183 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3185     g_assert (n);
3186     g_assert (nodepath);
3187     g_assert (n->subpath->nodepath == nodepath);
3189     if (g_list_length (nodepath->selected) == 0) {
3190         if (grow > 0) {
3191             sp_nodepath_node_select(n, TRUE, TRUE);
3192         }
3193         return;
3194     }
3196     if (g_list_length (nodepath->selected) == 1) {
3197         if (grow < 0) {
3198             sp_nodepath_deselect (nodepath);
3199             return;
3200         }
3201     }
3203     Inkscape::NodePath::Node *farthest_selected = NULL;
3204     double farthest_dist = 0;
3206     Inkscape::NodePath::Node *closest_unselected = NULL;
3207     double closest_dist = NR_HUGE;
3209     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3210        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3211         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3212            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3213            if (node == n)
3214                continue;
3215            if (node->selected) {
3216                if (Geom::L2(node->pos - n->pos) > farthest_dist) {
3217                    farthest_dist = Geom::L2(node->pos - n->pos);
3218                    farthest_selected = node;
3219                }
3220            } else {
3221                if (Geom::L2(node->pos - n->pos) < closest_dist) {
3222                    closest_dist = Geom::L2(node->pos - n->pos);
3223                    closest_unselected = node;
3224                }
3225            }
3226         }
3227     }
3229     if (grow > 0) {
3230         if (closest_unselected) {
3231             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3232         }
3233     } else {
3234         if (farthest_selected) {
3235             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3236         }
3237     }
3241 /**
3242 \brief  Saves all nodes' and handles' current positions in their origin members
3243 */
3244 void
3245 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3247     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3248        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3249         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3250            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3251            n->origin = n->pos;
3252            n->p.origin = n->p.pos;
3253            n->n.origin = n->n.pos;
3254         }
3255     }
3258 /**
3259 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3260 */
3261 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3263     GList *r = NULL;
3264     if (nodepath->selected) {
3265         guint i = 0;
3266         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3267             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3268             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3269                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3270                 i++;
3271                 if (node->selected) {
3272                     r = g_list_append(r, GINT_TO_POINTER(i));
3273                 }
3274             }
3275         }
3276     }
3277     return r;
3280 /**
3281 \brief  Restores selection by selecting nodes whose positions are in the list
3282 */
3283 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3285     sp_nodepath_deselect(nodepath);
3287     guint i = 0;
3288     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3289        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3290         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3291            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3292             i++;
3293             if (g_list_find(r, GINT_TO_POINTER(i))) {
3294                 sp_nodepath_node_select(node, TRUE, TRUE);
3295             }
3296         }
3297     }
3301 /**
3302 \brief Adjusts handle according to node type and line code.
3303 */
3304 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3306     g_assert(node);
3308     // nothing to do for auto nodes (sp_node_adjust_handles() does the job)
3309     if (node->type == Inkscape::NodePath::NODE_AUTO)
3310         return;
3312    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3313    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3315    // nothing to do if we are an end node
3316     if (me->other == NULL) return;
3317     if (other->other == NULL) return;
3319     // nothing to do if we are a cusp node
3320     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3322     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3323     NRPathcode mecode;
3324     if (which_adjust == 1) {
3325         mecode = (NRPathcode)me->other->code;
3326     } else {
3327         mecode = (NRPathcode)node->code;
3328     }
3329     if (mecode == NR_LINETO) return;
3331     if (sp_node_side_is_line(node, other)) {
3332         // other is a line, and we are either smooth or symm
3333        Inkscape::NodePath::Node *othernode = other->other;
3334         double len = Geom::L2(me->pos - node->pos);
3335         Geom::Point delta = node->pos - othernode->pos;
3336         double linelen = Geom::L2(delta);
3337         if (linelen < 1e-18)
3338             return;
3339         me->pos = node->pos + (len / linelen)*delta;
3340         return;
3341     }
3343     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3344         // symmetrize
3345         me->pos = 2 * node->pos - other->pos;
3346         return;
3347     } else {
3348         // smoothify
3349         double len = Geom::L2(me->pos - node->pos);
3350         Geom::Point delta = other->pos - node->pos;
3351         double otherlen = Geom::L2(delta);
3352         if (otherlen < 1e-18) return;
3353         me->pos = node->pos - (len / otherlen) * delta;
3354     }
3357 /**
3358  \brief Adjusts both handles according to node type and line code
3359  */
3360 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3362     g_assert(node);
3364     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3366     /* we are either smooth or symm */
3368     if (node->p.other == NULL) return;
3369     if (node->n.other == NULL) return;
3371     if (node->type == Inkscape::NodePath::NODE_AUTO) {
3372         sp_node_adjust_handles_auto(node);
3373         return;
3374     }
3376     if (sp_node_side_is_line(node, &node->p)) {
3377         sp_node_adjust_handle(node, 1);
3378         return;
3379     }
3381     if (sp_node_side_is_line(node, &node->n)) {
3382         sp_node_adjust_handle(node, -1);
3383         return;
3384     }
3386     /* both are curves */
3387     Geom::Point const delta( node->n.pos - node->p.pos );
3389     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3390         node->p.pos = node->pos - delta / 2;
3391         node->n.pos = node->pos + delta / 2;
3392         return;
3393     }
3395     /* We are smooth */
3396     double plen = Geom::L2(node->p.pos - node->pos);
3397     if (plen < 1e-18) return;
3398     double nlen = Geom::L2(node->n.pos - node->pos);
3399     if (nlen < 1e-18) return;
3400     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3401     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3404 static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node)
3406     if (node->p.other == NULL || node->n.other == NULL) {
3407         node->p.pos = node->pos;
3408         node->n.pos = node->pos;
3409         return;
3410     }
3412     Geom::Point leg_prev = to_2geom(node->p.other->pos - node->pos);
3413     Geom::Point leg_next = to_2geom(node->n.other->pos - node->pos);
3415     double norm_leg_prev = Geom::L2(leg_prev);
3416     double norm_leg_next = Geom::L2(leg_next);
3418     Geom::Point delta;
3419     if (norm_leg_next > 0.0) {
3420         delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
3421         delta.normalize();
3422     }
3424     node->p.pos = node->pos - norm_leg_prev / 3 * delta;
3425     node->n.pos = node->pos + norm_leg_next / 3 * delta;
3428 /**
3429  * Node event callback.
3430  */
3431 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3433     gboolean ret = FALSE;
3434     switch (event->type) {
3435         case GDK_ENTER_NOTIFY:
3436             Inkscape::NodePath::Path::active_node = n;
3437             break;
3438         case GDK_LEAVE_NOTIFY:
3439             Inkscape::NodePath::Path::active_node = NULL;
3440             break;
3441         case GDK_SCROLL:
3442             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3443                 switch (event->scroll.direction) {
3444                     case GDK_SCROLL_UP:
3445                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3446                         break;
3447                     case GDK_SCROLL_DOWN:
3448                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3449                         break;
3450                     default:
3451                         break;
3452                 }
3453                 ret = TRUE;
3454             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3455                 switch (event->scroll.direction) {
3456                     case GDK_SCROLL_UP:
3457                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3458                         break;
3459                     case GDK_SCROLL_DOWN:
3460                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3461                         break;
3462                     default:
3463                         break;
3464                 }
3465                 ret = TRUE;
3466             }
3467             break;
3468         case GDK_KEY_PRESS:
3469             switch (get_group0_keyval (&event->key)) {
3470                 case GDK_space:
3471                     if (event->key.state & GDK_BUTTON1_MASK) {
3472                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3473                         stamp_repr(nodepath);
3474                         ret = TRUE;
3475                     }
3476                     break;
3477                 case GDK_Page_Up:
3478                     if (event->key.state & GDK_CONTROL_MASK) {
3479                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3480                     } else {
3481                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3482                     }
3483                     break;
3484                 case GDK_Page_Down:
3485                     if (event->key.state & GDK_CONTROL_MASK) {
3486                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3487                     } else {
3488                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3489                     }
3490                     break;
3491                 default:
3492                     break;
3493             }
3494             break;
3495         default:
3496             break;
3497     }
3499     return ret;
3502 /**
3503  * Handle keypress on node; directly called.
3504  */
3505 gboolean node_key(GdkEvent *event)
3507     Inkscape::NodePath::Path *np;
3509     // there is no way to verify nodes so set active_node to nil when deleting!!
3510     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3512     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3513         gint ret = FALSE;
3514         switch (get_group0_keyval (&event->key)) {
3515             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3516             case GDK_BackSpace:
3517                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3518                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3519                 sp_nodepath_update_repr(np, _("Delete node"));
3520                 Inkscape::NodePath::Path::active_node = NULL;
3521                 ret = TRUE;
3522                 break;
3523             case GDK_c:
3524                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3525                 ret = TRUE;
3526                 break;
3527             case GDK_s:
3528                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3529                 ret = TRUE;
3530                 break;
3531             case GDK_a:
3532                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_AUTO);
3533                 ret = TRUE;
3534                 break;
3535             case GDK_y:
3536                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3537                 ret = TRUE;
3538                 break;
3539             case GDK_b:
3540                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3541                 ret = TRUE;
3542                 break;
3543         }
3544         return ret;
3545     }
3546     return FALSE;
3549 /**
3550  * Mouseclick on node callback.
3551  */
3552 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3554    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3556     if (state & GDK_CONTROL_MASK) {
3557         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3559         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3560             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3561                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3562             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3563                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3564             } else if (n->type == Inkscape::NodePath::NODE_SYMM) {
3565                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_AUTO);
3566             } else {
3567                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3568             }
3569             sp_nodepath_update_repr(nodepath, _("Change node type"));
3570             sp_nodepath_update_statusbar(nodepath);
3572         } else { //ctrl+alt+click: delete node
3573             GList *node_to_delete = NULL;
3574             node_to_delete = g_list_append(node_to_delete, n);
3575             sp_node_delete_preserve(node_to_delete);
3576         }
3578     } else {
3579         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3580     }
3583 /**
3584  * Mouse grabbed node callback.
3585  */
3586 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3588    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3590     if (!n->selected) {
3591         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3592     }
3594     n->is_dragging = true;
3595     // Reconstruct and store the location of the mouse pointer at the time when we started dragging (needed for snapping)
3596     n->subpath->nodepath->drag_origin_mouse = knot->grabbed_rel_pos + knot->drag_origin;
3598     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3600     sp_nodepath_remember_origins (n->subpath->nodepath);
3603 /**
3604  * Mouse ungrabbed node callback.
3605  */
3606 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3608    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3610    n->dragging_out = NULL;
3611    n->is_dragging = false;
3612    n->subpath->nodepath->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
3613    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3615    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3618 /**
3619  * The point on a line, given by its angle, closest to the given point.
3620  * \param p  A point.
3621  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3622  * \param closest  Pointer to the point struct where the result is stored.
3623  * \todo FIXME: use dot product perhaps?
3624  */
3625 static void point_line_closest(Geom::Point *p, double a, Geom::Point *closest)
3627     if (a == HUGE_VAL) { // vertical
3628         *closest = Geom::Point(0, (*p)[Geom::Y]);
3629     } else {
3630         (*closest)[Geom::X] = ( a * (*p)[Geom::Y] + (*p)[Geom::X]) / (a*a + 1);
3631         (*closest)[Geom::Y] = a * (*closest)[Geom::X];
3632     }
3635 /**
3636  * Distance from the point to a line given by its angle.
3637  * \param p  A point.
3638  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3639  */
3640 static double point_line_distance(Geom::Point *p, double a)
3642     Geom::Point c;
3643     point_line_closest(p, a, &c);
3644     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]));
3647 /**
3648  * Callback for node "request" signal.
3649  * \todo fixme: This goes to "moved" event? (lauris)
3650  */
3651 static gboolean
3652 node_request(SPKnot */*knot*/, Geom::Point const &p, guint state, gpointer data)
3654     double yn, xn, yp, xp;
3655     double an, ap, na, pa;
3656     double d_an, d_ap, d_na, d_pa;
3657     gboolean collinear = FALSE;
3658     Geom::Point c;
3659     Geom::Point pr;
3661     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3663     n->subpath->nodepath->desktop->snapindicator->remove_snaptarget();
3665     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3666     if ( (!n->subpath->nodepath->straight_path) &&
3667          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3668            || n->dragging_out ) )
3669     {
3670        Geom::Point mouse = p;
3672        if (!n->dragging_out) {
3673            // This is the first drag-out event; find out which handle to drag out
3674            double appr_n = (n->n.other ? Geom::L2(n->n.other->pos - n->pos) - Geom::L2(n->n.other->pos - p) : -HUGE_VAL);
3675            double appr_p = (n->p.other ? Geom::L2(n->p.other->pos - n->pos) - Geom::L2(n->p.other->pos - p) : -HUGE_VAL);
3677            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3678                return FALSE;
3680            Inkscape::NodePath::NodeSide *opposite;
3681            if (appr_p > appr_n) { // closer to p
3682                n->dragging_out = &n->p;
3683                opposite = &n->n;
3684                n->code = NR_CURVETO;
3685            } else if (appr_p < appr_n) { // closer to n
3686                n->dragging_out = &n->n;
3687                opposite = &n->p;
3688                n->n.other->code = NR_CURVETO;
3689            } else { // p and n nodes are the same
3690                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3691                    n->dragging_out = &n->p;
3692                    opposite = &n->n;
3693                    n->code = NR_CURVETO;
3694                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3695                    n->dragging_out = &n->n;
3696                    opposite = &n->p;
3697                    n->n.other->code = NR_CURVETO;
3698                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3699                    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);
3700                    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);
3701                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3702                        n->dragging_out = &n->n;
3703                        opposite = &n->p;
3704                        n->n.other->code = NR_CURVETO;
3705                    } else { // closer to other's n handle
3706                        n->dragging_out = &n->p;
3707                        opposite = &n->n;
3708                        n->code = NR_CURVETO;
3709                    }
3710                }
3711            }
3713            // if there's another handle, make sure the one we drag out starts parallel to it
3714            if (opposite->pos != n->pos) {
3715                mouse = n->pos - Geom::L2(mouse - n->pos) * Geom::unit_vector(opposite->pos - n->pos);
3716            }
3718            // knots might not be created yet!
3719            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3720            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3721        }
3723        // pass this on to the handle-moved callback
3724        node_handle_moved(n->dragging_out->knot, mouse, state, (gpointer) n);
3725        sp_node_update_handles(n);
3726        return TRUE;
3727    }
3729     if (state & GDK_CONTROL_MASK) { // constrained motion
3731         // calculate relative distances of handles
3732         // n handle:
3733         yn = n->n.pos[Geom::Y] - n->pos[Geom::Y];
3734         xn = n->n.pos[Geom::X] - n->pos[Geom::X];
3735         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3736         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3737             if (n->n.other) { // if there is the next point
3738                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3739                     yn = n->n.other->origin[Geom::Y] - n->origin[Geom::Y]; // use origin because otherwise the direction will change as you drag
3740                     xn = n->n.other->origin[Geom::X] - n->origin[Geom::X];
3741             }
3742         }
3743         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3744         if (yn < 0) { xn = -xn; yn = -yn; }
3746         // p handle:
3747         yp = n->p.pos[Geom::Y] - n->pos[Geom::Y];
3748         xp = n->p.pos[Geom::X] - n->pos[Geom::X];
3749         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3750         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3751             if (n->p.other) {
3752                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3753                     yp = n->p.other->origin[Geom::Y] - n->origin[Geom::Y];
3754                     xp = n->p.other->origin[Geom::X] - n->origin[Geom::X];
3755             }
3756         }
3757         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3758         if (yp < 0) { xp = -xp; yp = -yp; }
3760         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3761             // sliding on handles, only if at least one of the handles is non-vertical
3762             // (otherwise it's the same as ctrl+drag anyway)
3764             // calculate angles of the handles
3765             if (xn == 0) {
3766                 if (yn == 0) { // no handle, consider it the continuation of the other one
3767                     an = 0;
3768                     collinear = TRUE;
3769                 }
3770                 else an = 0; // vertical; set the angle to horizontal
3771             } else an = yn/xn;
3773             if (xp == 0) {
3774                 if (yp == 0) { // no handle, consider it the continuation of the other one
3775                     ap = an;
3776                 }
3777                 else ap = 0; // vertical; set the angle to horizontal
3778             } else  ap = yp/xp;
3780             if (collinear) an = ap;
3782             // angles of the perpendiculars; HUGE_VAL means vertical
3783             if (an == 0) na = HUGE_VAL; else na = -1/an;
3784             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3786             // mouse point relative to the node's original pos
3787             pr = p - n->origin;
3789             // distances to the four lines (two handles and two perpendiculars)
3790             d_an = point_line_distance(&pr, an);
3791             d_na = point_line_distance(&pr, na);
3792             d_ap = point_line_distance(&pr, ap);
3793             d_pa = point_line_distance(&pr, pa);
3795             // find out which line is the closest, save its closest point in c
3796             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3797                 point_line_closest(&pr, an, &c);
3798             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3799                 point_line_closest(&pr, ap, &c);
3800             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3801                 point_line_closest(&pr, na, &c);
3802             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3803                 point_line_closest(&pr, pa, &c);
3804             }
3806             // move the node to the closest point
3807             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3808                                             n->origin[Geom::X] + c[Geom::X] - n->pos[Geom::X],
3809                                             n->origin[Geom::Y] + c[Geom::Y] - n->pos[Geom::Y],
3810                                             true);
3812         } else {  // constraining to hor/vert
3814             if (fabs(p[Geom::X] - n->origin[Geom::X]) > fabs(p[Geom::Y] - n->origin[Geom::Y])) { // snap to hor
3815                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3816                                                 p[Geom::X] - n->pos[Geom::X],
3817                                                 n->origin[Geom::Y] - n->pos[Geom::Y],
3818                                                 true,
3819                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::X]));
3820             } else { // snap to vert
3821                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3822                                                 n->origin[Geom::X] - n->pos[Geom::X],
3823                                                 p[Geom::Y] - n->pos[Geom::Y],
3824                                                 true,
3825                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::Y]));
3826             }
3827         }
3828     } else { // move freely
3829         if (n->is_dragging) {
3830             if (state & GDK_MOD1_MASK) { // sculpt
3831                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, p - n->origin);
3832             } else {
3833                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3834                                             p[Geom::X] - n->pos[Geom::X],
3835                                             p[Geom::Y] - n->pos[Geom::Y],
3836                                             (state & GDK_SHIFT_MASK) == 0);
3837             }
3838         }
3839     }
3841     n->subpath->nodepath->desktop->scroll_to_point(p);
3843     return TRUE;
3846 /**
3847  * Node handle clicked callback.
3848  */
3849 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3851    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3853     if (state & GDK_CONTROL_MASK) { // "delete" handle
3854         if (n->p.knot == knot) {
3855             n->p.pos = n->pos;
3856         } else if (n->n.knot == knot) {
3857             n->n.pos = n->pos;
3858         }
3859         sp_node_update_handles(n);
3860         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3861         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3862         sp_nodepath_update_statusbar(nodepath);
3864     } else { // just select or add to selection, depending in Shift
3865         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3866     }
3869 /**
3870  * Node handle grabbed callback.
3871  */
3872 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3874    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3876     // convert auto -> smooth when dragging handle
3877    if (n->type == Inkscape::NodePath::NODE_AUTO) {
3878         n->type = Inkscape::NodePath::NODE_SMOOTH;
3879         sp_nodepath_update_node_knot (n);
3880    }
3882     if (!n->selected) {
3883         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3884     }
3886     // remember the origin point of the handle
3887     if (n->p.knot == knot) {
3888         n->p.origin_radial = n->p.pos - n->pos;
3889     } else if (n->n.knot == knot) {
3890         n->n.origin_radial = n->n.pos - n->pos;
3891     } else {
3892         g_assert_not_reached();
3893     }
3895     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3898 /**
3899  * Node handle ungrabbed callback.
3900  */
3901 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3903    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3905     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3906     if (n->p.knot == knot) {
3907         n->p.origin_radial.a = 0;
3908         sp_knot_set_position(knot, n->p.pos, state);
3909     } else if (n->n.knot == knot) {
3910         n->n.origin_radial.a = 0;
3911         sp_knot_set_position(knot, n->n.pos, state);
3912     } else {
3913         g_assert_not_reached();
3914     }
3916     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3919 /**
3920  * Node handle "request" signal callback.
3921  */
3922 static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, gpointer data)
3924     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3926     Inkscape::NodePath::NodeSide *me, *opposite;
3927     gint which;
3928     if (n->p.knot == knot) {
3929         me = &n->p;
3930         opposite = &n->n;
3931         which = -1;
3932     } else if (n->n.knot == knot) {
3933         me = &n->n;
3934         opposite = &n->p;
3935         which = 1;
3936     } else {
3937         me = opposite = NULL;
3938         which = 0;
3939         g_assert_not_reached();
3940     }
3942     SPDesktop *desktop = n->subpath->nodepath->desktop;
3943     SnapManager &m = desktop->namedview->snap_manager;
3944     m.setup(desktop, true, n->subpath->nodepath->item);
3945     Inkscape::SnappedPoint s;
3947     if ((state & GDK_SHIFT_MASK) != 0) {
3948         // We will not try to snap when the shift-key is pressed
3949         // so remove the old snap indicator and don't wait for it to time-out
3950         desktop->snapindicator->remove_snaptarget();
3951     }
3953     Inkscape::NodePath::Node *othernode = opposite->other;
3954     Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP);
3955     if (othernode) {
3956         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3957             /* We are smooth node adjacent with line */
3958             Geom::Point const delta = p - n->pos;
3959             Geom::Coord const len = Geom::L2(delta);
3960             Inkscape::NodePath::Node *othernode = opposite->other;
3961             Geom::Point const ndelta = n->pos - othernode->pos;
3962             Geom::Coord const linelen = Geom::L2(ndelta);
3963             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3964                 Geom::Coord const scal = dot(delta, ndelta) / linelen;
3965                 p = n->pos + (scal / linelen) * ndelta;
3966             }
3967             if ((state & GDK_SHIFT_MASK) == 0) {
3968                 s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type, Inkscape::Snapper::ConstraintLine(p, ndelta), false);
3969             }
3970         } else {
3971             if ((state & GDK_SHIFT_MASK) == 0) {
3972                 s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type);
3973             }
3974         }
3975     } else {
3976         if ((state & GDK_SHIFT_MASK) == 0) {
3977             s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type);
3978         }
3979     }
3981     s.getPoint(p);
3983     sp_node_adjust_handle(n, -which);
3985     return FALSE;
3988 /**
3989  * Node handle moved callback.
3990  */
3991 static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data)
3993    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3994    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3996    Inkscape::NodePath::NodeSide *me;
3997    Inkscape::NodePath::NodeSide *other;
3998     if (n->p.knot == knot) {
3999         me = &n->p;
4000         other = &n->n;
4001     } else if (n->n.knot == knot) {
4002         me = &n->n;
4003         other = &n->p;
4004     } else {
4005         me = NULL;
4006         other = NULL;
4007         g_assert_not_reached();
4008     }
4010     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
4011     Radial rme(me->pos - n->pos);
4012     Radial rother(other->pos - n->pos);
4013     Radial rnew(p - n->pos);
4015     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
4016         int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
4017         /* 0 interpreted as "no snapping". */
4019         // 1. Snap to the closest PI/snaps angle, starting from zero.
4020         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
4022         // 2. Snap to the original angle, its opposite and perpendiculars
4023         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
4024             /* The closest PI/2 angle, starting from original angle */
4025             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
4027             // Snap to the closest.
4028             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
4029                        ? a_snapped
4030                        : a_ortho );
4031         }
4033         // 3. Snap to the angle of the opposite line, if any
4034         Inkscape::NodePath::Node *othernode = other->other;
4035         if (othernode) {
4036             Geom::Point other_to_snap(0,0);
4037             if (sp_node_side_is_line(n, other)) {
4038                 other_to_snap = othernode->pos - n->pos;
4039             } else {
4040                 other_to_snap = other->pos - n->pos;
4041             }
4042             if (Geom::L2(other_to_snap) > 1e-3) {
4043                 Radial rother_to_snap(other_to_snap);
4044                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
4045                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
4047                 // Snap to the closest.
4048                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
4049                        ? a_snapped
4050                        : a_oppo );
4051             }
4052         }
4054         rnew.a = a_snapped;
4055     }
4057     if (state & GDK_MOD1_MASK) {
4058         // lock handle length
4059         rnew.r = me->origin_radial.r;
4060     }
4062     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (!n->dragging_out && (state & GDK_SHIFT_MASK)))
4063         && (rme.a != HUGE_VAL) && (rnew.a != HUGE_VAL) && ((fabs(rme.a - rnew.a) > 0.001) || (n->type ==Inkscape::NodePath::NODE_SYMM))) {
4064         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
4065         rother.a += rnew.a - rme.a;
4066         other->pos = Geom::Point(rother) + n->pos;
4067         if (other->knot) {
4068             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
4069             sp_knot_moveto(other->knot, other->pos);
4070         }
4071     }
4073     me->pos = Geom::Point(rnew) + n->pos;
4074     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
4076     // move knot, but without emitting the signal:
4077     // we cannot emit a "moved" signal because we're now processing it
4078     sp_knot_moveto(me->knot, me->pos);
4080     update_object(n->subpath->nodepath);
4082     /* status text */
4083     SPDesktop *desktop = n->subpath->nodepath->desktop;
4084     if (!desktop) return;
4085     SPEventContext *ec = desktop->event_context;
4086     if (!ec) return;
4088     Inkscape::MessageContext *mc = get_message_context(ec);
4090     if (!mc) return;
4092     double degrees = 180 / M_PI * rnew.a;
4093     if (degrees > 180) degrees -= 360;
4094     if (degrees < -180) degrees += 360;
4095     if (prefs->getBool("/options/compassangledisplay/value"))
4096         degrees = angle_to_compass (degrees);
4098     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
4100     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
4101          _("<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);
4103     g_string_free(length, TRUE);
4106 /**
4107  * Node handle event callback.
4108  */
4109 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
4111     gboolean ret = FALSE;
4112     switch (event->type) {
4113         case GDK_KEY_PRESS:
4114             switch (get_group0_keyval (&event->key)) {
4115                 case GDK_space:
4116                     if (event->key.state & GDK_BUTTON1_MASK) {
4117                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
4118                         stamp_repr(nodepath);
4119                         ret = TRUE;
4120                     }
4121                     break;
4122                 default:
4123                     break;
4124             }
4125             break;
4126         case GDK_ENTER_NOTIFY:
4127             // we use an experimentally determined threshold that seems to work fine
4128             if (Geom::L2(n->pos - knot->pos) < 0.75)
4129                 Inkscape::NodePath::Path::active_node = n;
4130             break;
4131         case GDK_LEAVE_NOTIFY:
4132             // we use an experimentally determined threshold that seems to work fine
4133             if (Geom::L2(n->pos - knot->pos) < 0.75)
4134                 Inkscape::NodePath::Path::active_node = NULL;
4135             break;
4136         default:
4137             break;
4138     }
4140     return ret;
4143 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
4144                                  Radial &rme, Radial &rother, gboolean const both)
4146     rme.a += angle;
4147     if ( both
4148          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4149          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4150     {
4151         rother.a += angle;
4152     }
4155 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
4156                                         Radial &rme, Radial &rother, gboolean const both)
4158     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
4160     gdouble r;
4161     if ( both
4162          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4163          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4164     {
4165         r = MAX(rme.r, rother.r);
4166     } else {
4167         r = rme.r;
4168     }
4170     gdouble const weird_angle = atan2(norm_angle, r);
4171 /* Bulia says norm_angle is just the visible distance that the
4172  * object's end must travel on the screen.  Left as 'angle' for want of
4173  * a better name.*/
4175     rme.a += weird_angle;
4176     if ( both
4177          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4178          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4179     {
4180         rother.a += weird_angle;
4181     }
4184 /**
4185  * Rotate one node.
4186  */
4187 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4189     Inkscape::NodePath::NodeSide *me, *other;
4190     bool both = false;
4192     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4193     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4195     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4196         me = &(n->p);
4197         other = &(n->n);
4198     } else if (!n->p.other) {
4199         me = &(n->n);
4200         other = &(n->p);
4201     } else {
4202         if (which > 0) { // right handle
4203             if (xn > xp) {
4204                 me = &(n->n);
4205                 other = &(n->p);
4206             } else {
4207                 me = &(n->p);
4208                 other = &(n->n);
4209             }
4210         } else if (which < 0){ // left handle
4211             if (xn <= xp) {
4212                 me = &(n->n);
4213                 other = &(n->p);
4214             } else {
4215                 me = &(n->p);
4216                 other = &(n->n);
4217             }
4218         } else { // both handles
4219             me = &(n->n);
4220             other = &(n->p);
4221             both = true;
4222         }
4223     }
4225     Radial rme(me->pos - n->pos);
4226     Radial rother(other->pos - n->pos);
4228     if (screen) {
4229         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4230     } else {
4231         node_rotate_one_internal (*n, angle, rme, rother, both);
4232     }
4234     me->pos = n->pos + Geom::Point(rme);
4236     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4237         other->pos =  n->pos + Geom::Point(rother);
4238     }
4240     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4241     // so here we just move all the knots without emitting move signals, for speed
4242     sp_node_update_handles(n, false);
4245 /**
4246  * Rotate selected nodes.
4247  */
4248 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4250     if (!nodepath || !nodepath->selected) return;
4252     if (g_list_length(nodepath->selected) == 1) {
4253        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4254         node_rotate_one (n, angle, which, screen);
4255     } else {
4256        // rotate as an object:
4258         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4259         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4260         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4261             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4262             box.expandTo (n->pos); // contain all selected nodes
4263         }
4265         gdouble rot;
4266         if (screen) {
4267             gdouble const zoom = nodepath->desktop->current_zoom();
4268             gdouble const zmove = angle / zoom;
4269             gdouble const r = Geom::L2(box.max() - box.midpoint());
4270             rot = atan2(zmove, r);
4271         } else {
4272             rot = angle;
4273         }
4275         Geom::Point rot_center;
4276         if (Inkscape::NodePath::Path::active_node == NULL)
4277             rot_center = box.midpoint();
4278         else
4279             rot_center = Inkscape::NodePath::Path::active_node->pos;
4281         Geom::Matrix t =
4282             Geom::Matrix (Geom::Translate(-rot_center)) *
4283             Geom::Matrix (Geom::Rotate(rot)) *
4284             Geom::Matrix (Geom::Translate(rot_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, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4298 /**
4299  * Scale one node.
4300  */
4301 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4303     bool both = false;
4304     Inkscape::NodePath::NodeSide *me, *other;
4306     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4307     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4309     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4310         me = &(n->p);
4311         other = &(n->n);
4312         n->code = NR_CURVETO;
4313     } else if (!n->p.other) {
4314         me = &(n->n);
4315         other = &(n->p);
4316         if (n->n.other)
4317             n->n.other->code = NR_CURVETO;
4318     } else {
4319         if (which > 0) { // right handle
4320             if (xn > xp) {
4321                 me = &(n->n);
4322                 other = &(n->p);
4323                 if (n->n.other)
4324                     n->n.other->code = NR_CURVETO;
4325             } else {
4326                 me = &(n->p);
4327                 other = &(n->n);
4328                 n->code = NR_CURVETO;
4329             }
4330         } else if (which < 0){ // left handle
4331             if (xn <= xp) {
4332                 me = &(n->n);
4333                 other = &(n->p);
4334                 if (n->n.other)
4335                     n->n.other->code = NR_CURVETO;
4336             } else {
4337                 me = &(n->p);
4338                 other = &(n->n);
4339                 n->code = NR_CURVETO;
4340             }
4341         } else { // both handles
4342             me = &(n->n);
4343             other = &(n->p);
4344             both = true;
4345             n->code = NR_CURVETO;
4346             if (n->n.other)
4347                 n->n.other->code = NR_CURVETO;
4348         }
4349     }
4351     Radial rme(me->pos - n->pos);
4352     Radial rother(other->pos - n->pos);
4354     rme.r += grow;
4355     if (rme.r < 0) rme.r = 0;
4356     if (rme.a == HUGE_VAL) {
4357         if (me->other) { // if direction is unknown, initialize it towards the next node
4358             Radial rme_next(me->other->pos - n->pos);
4359             rme.a = rme_next.a;
4360         } else { // if there's no next, initialize to 0
4361             rme.a = 0;
4362         }
4363     }
4364     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4365         rother.r += grow;
4366         if (rother.r < 0) rother.r = 0;
4367         if (rother.a == HUGE_VAL) {
4368             rother.a = rme.a + M_PI;
4369         }
4370     }
4372     me->pos = n->pos + Geom::Point(rme);
4374     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4375         other->pos = n->pos + Geom::Point(rother);
4376     }
4378     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4379     // so here we just move all the knots without emitting move signals, for speed
4380     sp_node_update_handles(n, false);
4383 /**
4384  * Scale selected nodes.
4385  */
4386 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4388     if (!nodepath || !nodepath->selected) return;
4390     if (g_list_length(nodepath->selected) == 1) {
4391         // scale handles of the single selected node
4392         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4393         node_scale_one (n, grow, which);
4394     } else {
4395         // scale nodes as an "object":
4397         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4398         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4399         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4400             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4401             box.expandTo (n->pos); // contain all selected nodes
4402         }
4404         if ( Geom::are_near(box.maxExtent(), 0) ) {
4405             SPEventContext *ec = nodepath->desktop->event_context;
4406             if (!ec) return;
4407             Inkscape::MessageContext *mc = get_message_context(ec);
4408             if (!mc) return;
4409             mc->setF(Inkscape::WARNING_MESSAGE,
4410                              _("Cannot scale nodes when all are at the same location."));
4411             return;
4412         }
4413         double scale = (box.maxExtent() + grow)/box.maxExtent();
4416         Geom::Point scale_center;
4417         if (Inkscape::NodePath::Path::active_node == NULL)
4418             scale_center = box.midpoint();
4419         else
4420             scale_center = Inkscape::NodePath::Path::active_node->pos;
4422         Geom::Matrix t =
4423             Geom::Translate(-scale_center) *
4424             Geom::Scale(scale, scale) *
4425             Geom::Translate(scale_center);
4427         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4428             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4429             n->pos *= t;
4430             n->n.pos *= t;
4431             n->p.pos *= t;
4432             sp_node_update_handles(n, false);
4433         }
4434     }
4436     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4439 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4441     if (!nodepath) return;
4442     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4445 /**
4446  * Flip selected nodes horizontally/vertically.
4447  */
4448 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis, boost::optional<Geom::Point> center)
4450     if (!nodepath || !nodepath->selected) return;
4452     if (g_list_length(nodepath->selected) == 1 && !center) {
4453         // flip handles of the single selected node
4454         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4455         double temp = n->p.pos[axis];
4456         n->p.pos[axis] = n->n.pos[axis];
4457         n->n.pos[axis] = temp;
4458         sp_node_update_handles(n, false);
4459     } else {
4460         // scale nodes as an "object":
4462         Geom::Rect box = sp_node_selected_bbox (nodepath);
4463         if (!center) {
4464             center = box.midpoint();
4465         }
4466         Geom::Matrix t =
4467             Geom::Matrix (Geom::Translate(- *center)) *
4468             Geom::Matrix ((axis == Geom::X)? Geom::Scale(-1, 1) : Geom::Scale(1, -1)) *
4469             Geom::Matrix (Geom::Translate(*center));
4471         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4472             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4473             n->pos *= t;
4474             n->n.pos *= t;
4475             n->p.pos *= t;
4476             sp_node_update_handles(n, false);
4477         }
4478     }
4480     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4483 Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4485     g_assert (nodepath->selected);
4487     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4488     Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4489     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4490         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4491         box.expandTo (n->pos); // contain all selected nodes
4492     }
4493     return box;
4496 //-----------------------------------------------
4497 /**
4498  * Return new subpath under given nodepath.
4499  */
4500 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4502     g_assert(nodepath);
4503     g_assert(nodepath->desktop);
4505    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4507     s->nodepath = nodepath;
4508     s->closed = FALSE;
4509     s->nodes = NULL;
4510     s->first = NULL;
4511     s->last = NULL;
4513     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4514     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4515     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4517     return s;
4520 /**
4521  * Destroy nodes in subpath, then subpath itself.
4522  */
4523 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4525     g_assert(subpath);
4526     g_assert(subpath->nodepath);
4527     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4529     while (subpath->nodes) {
4530         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4531     }
4533     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4535     g_free(subpath);
4538 /**
4539  * Link head to tail in subpath.
4540  */
4541 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4543     g_assert(!sp->closed);
4544     g_assert(sp->last != sp->first);
4545     g_assert(sp->first->code == NR_MOVETO);
4547     sp->closed = TRUE;
4549     //Link the head to the tail
4550     sp->first->p.other = sp->last;
4551     sp->last->n.other  = sp->first;
4552     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4553     sp->first          = sp->last;
4555     //Remove the extra end node
4556     sp_nodepath_node_destroy(sp->last->n.other);
4559 /**
4560  * Open closed (loopy) subpath at node.
4561  */
4562 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4564     g_assert(sp->closed);
4565     g_assert(n->subpath == sp);
4566     g_assert(sp->first == sp->last);
4568     /* We create new startpoint, current node will become last one */
4570    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4571                                                 &n->pos, &n->pos, &n->n.pos);
4574     sp->closed        = FALSE;
4576     //Unlink to make a head and tail
4577     sp->first         = new_path;
4578     sp->last          = n;
4579     n->n.other        = NULL;
4580     new_path->p.other = NULL;
4583 /**
4584  * Return new node in subpath with given properties.
4585  * \param pos Position of node.
4586  * \param ppos Handle position in previous direction
4587  * \param npos Handle position in previous direction
4588  */
4589 Inkscape::NodePath::Node *
4590 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)
4592     g_assert(sp);
4593     g_assert(sp->nodepath);
4594     g_assert(sp->nodepath->desktop);
4596     if (nodechunk == NULL)
4597         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4599     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4601     n->subpath  = sp;
4603     if (type != Inkscape::NodePath::NODE_NONE) {
4604         // use the type from sodipodi:nodetypes
4605         n->type = type;
4606     } else {
4607         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4608             // points are (almost) collinear
4609             if (Geom::L2(*pos - *ppos) < 1e-6 || Geom::L2(*pos - *npos) < 1e-6) {
4610                 // endnode, or a node with a retracted handle
4611                 n->type = Inkscape::NodePath::NODE_CUSP;
4612             } else {
4613                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4614             }
4615         } else {
4616             n->type = Inkscape::NodePath::NODE_CUSP;
4617         }
4618     }
4620     n->code     = code;
4621     n->selected = FALSE;
4622     n->pos      = *pos;
4623     n->p.pos    = *ppos;
4624     n->n.pos    = *npos;
4626     n->dragging_out = NULL;
4628     Inkscape::NodePath::Node *prev;
4629     if (next) {
4630         //g_assert(g_list_find(sp->nodes, next));
4631         prev = next->p.other;
4632     } else {
4633         prev = sp->last;
4634     }
4636     if (prev)
4637         prev->n.other = n;
4638     else
4639         sp->first = n;
4641     if (next)
4642         next->p.other = n;
4643     else
4644         sp->last = n;
4646     n->p.other = prev;
4647     n->n.other = next;
4649     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"));
4650     sp_knot_set_position(n->knot, *pos, 0);
4652     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4653     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4654     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4656     sp_nodepath_update_node_knot(n);
4658     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4659     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4660     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4661     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4662     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4663     sp_knot_show(n->knot);
4665     // We only create handle knots and lines on demand
4666     n->p.knot = NULL;
4667     n->p.line = NULL;
4668     n->n.knot = NULL;
4669     n->n.line = NULL;
4671     sp->nodes = g_list_prepend(sp->nodes, n);
4673     return n;
4676 /**
4677  * Destroy node and its knots, link neighbors in subpath.
4678  */
4679 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4681     g_assert(node);
4682     g_assert(node->subpath);
4683     g_assert(SP_IS_KNOT(node->knot));
4685    Inkscape::NodePath::SubPath *sp = node->subpath;
4687     if (node->selected) { // first, deselect
4688         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4689         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4690     }
4692     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4694     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4695     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4696     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4697     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4698     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4699     g_object_unref(G_OBJECT(node->knot));
4701     if (node->p.knot) {
4702         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4703         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4704         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4705         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4706         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4707         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4708         g_object_unref(G_OBJECT(node->p.knot));
4709         node->p.knot = NULL;
4710     }
4712     if (node->n.knot) {
4713         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4714         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4715         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4716         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4717         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4718         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4719         g_object_unref(G_OBJECT(node->n.knot));
4720         node->n.knot = NULL;
4721     }
4723     if (node->p.line)
4724         gtk_object_destroy(GTK_OBJECT(node->p.line));
4725     if (node->n.line)
4726         gtk_object_destroy(GTK_OBJECT(node->n.line));
4728     if (sp->nodes) { // there are others nodes on the subpath
4729         if (sp->closed) {
4730             if (sp->first == node) {
4731                 g_assert(sp->last == node);
4732                 sp->first = node->n.other;
4733                 sp->last = sp->first;
4734             }
4735             node->p.other->n.other = node->n.other;
4736             node->n.other->p.other = node->p.other;
4737         } else {
4738             if (sp->first == node) {
4739                 sp->first = node->n.other;
4740                 sp->first->code = NR_MOVETO;
4741             }
4742             if (sp->last == node) sp->last = node->p.other;
4743             if (node->p.other) node->p.other->n.other = node->n.other;
4744             if (node->n.other) node->n.other->p.other = node->p.other;
4745         }
4746     } else { // this was the last node on subpath
4747         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4748     }
4750     g_mem_chunk_free(nodechunk, node);
4753 /**
4754  * Returns one of the node's two sides.
4755  * \param which Indicates which side.
4756  * \return Pointer to previous node side if which==-1, next if which==1.
4757  */
4758 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4760     g_assert(node);
4761     Inkscape::NodePath::NodeSide * result = 0;
4762     switch (which) {
4763         case -1:
4764             result = &node->p;
4765             break;
4766         case 1:
4767             result = &node->n;
4768             break;
4769         default:
4770             g_assert_not_reached();
4771     }
4773     return result;
4776 /**
4777  * Return the other side of the node, given one of its sides.
4778  */
4779 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4781     g_assert(node);
4782     Inkscape::NodePath::NodeSide *result = 0;
4784     if (me == &node->p) {
4785         result = &node->n;
4786     } else if (me == &node->n) {
4787         result = &node->p;
4788     } else {
4789         g_assert_not_reached();
4790     }
4792     return result;
4795 /**
4796  * Return NRPathcode on the given side of the node.
4797  */
4798 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4800     g_assert(node);
4802     NRPathcode result = NR_END;
4803     if (me == &node->p) {
4804         if (node->p.other) {
4805             result = (NRPathcode)node->code;
4806         } else {
4807             result = NR_MOVETO;
4808         }
4809     } else if (me == &node->n) {
4810         if (node->n.other) {
4811             result = (NRPathcode)node->n.other->code;
4812         } else {
4813             result = NR_MOVETO;
4814         }
4815     } else {
4816         g_assert_not_reached();
4817     }
4819     return result;
4822 /**
4823  * Return node with the given index
4824  */
4825 Inkscape::NodePath::Node *
4826 sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index)
4828     Inkscape::NodePath::Node *e = NULL;
4830     if (!nodepath) {
4831         return e;
4832     }
4834     //find segment
4835     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4837         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4838         int n = g_list_length(sp->nodes);
4839         if (sp->closed) {
4840             n++;
4841         }
4843         //if the piece belongs to this subpath grab it
4844         //otherwise move onto the next subpath
4845         if (index < n) {
4846             e = sp->first;
4847             for (int i = 0; i < index; ++i) {
4848                 e = e->n.other;
4849             }
4850             break;
4851         } else {
4852             if (sp->closed) {
4853                 index -= (n+1);
4854             } else {
4855                 index -= n;
4856             }
4857         }
4858     }
4860     return e;
4863 /**
4864  * Returns plain text meaning of node type.
4865  */
4866 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4868     unsigned retracted = 0;
4869     bool endnode = false;
4871     for (int which = -1; which <= 1; which += 2) {
4872         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4873         if (side->other && Geom::L2(side->pos - node->pos) < 1e-6)
4874             retracted ++;
4875         if (!side->other)
4876             endnode = true;
4877     }
4879     if (retracted == 0) {
4880         if (endnode) {
4881                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4882                 return _("end node");
4883         } else {
4884             switch (node->type) {
4885                 case Inkscape::NodePath::NODE_CUSP:
4886                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4887                     return _("cusp");
4888                 case Inkscape::NodePath::NODE_SMOOTH:
4889                     // TRANSLATORS: "smooth" is an adjective here
4890                     return _("smooth");
4891                 case Inkscape::NodePath::NODE_AUTO:
4892                     return _("auto");
4893                 case Inkscape::NodePath::NODE_SYMM:
4894                     return _("symmetric");
4895             }
4896         }
4897     } else if (retracted == 1) {
4898         if (endnode) {
4899             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4900             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4901         } else {
4902             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4903         }
4904     } else {
4905         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4906     }
4908     return NULL;
4911 /**
4912  * Handles content of statusbar as long as node tool is active.
4913  */
4914 void
4915 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4917     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");
4918     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4920     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4921     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4922     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4923     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4925     SPDesktop *desktop = NULL;
4926     if (nodepath) {
4927         desktop = nodepath->desktop;
4928     } else {
4929         desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above
4930     }
4932     SPEventContext *ec = desktop->event_context;
4933     if (!ec) return;
4935     Inkscape::MessageContext *mc = get_message_context(ec);
4936     if (!mc) return;
4938     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4940     if (selected_nodes == 0) {
4941         Inkscape::Selection *sel = desktop->selection;
4942         if (!sel || sel->isEmpty()) {
4943             mc->setF(Inkscape::NORMAL_MESSAGE,
4944                      _("Select a single object to edit its nodes or handles."));
4945         } else {
4946             if (nodepath) {
4947             mc->setF(Inkscape::NORMAL_MESSAGE,
4948                      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.",
4949                               "<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.",
4950                               total_nodes),
4951                      total_nodes);
4952             } else {
4953                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4954                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4955                 } else {
4956                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4957                 }
4958             }
4959         }
4960     } else if (nodepath && selected_nodes == 1) {
4961         mc->setF(Inkscape::NORMAL_MESSAGE,
4962                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4963                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4964                           total_nodes),
4965                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4966     } else {
4967         if (selected_subpaths > 1) {
4968             mc->setF(Inkscape::NORMAL_MESSAGE,
4969                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4970                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4971                               total_nodes),
4972                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4973         } else {
4974             mc->setF(Inkscape::NORMAL_MESSAGE,
4975                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4976                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4977                               total_nodes),
4978                      selected_nodes, total_nodes, when_selected);
4979         }
4980     }
4983 /*
4984  * returns a *copy* of the curve of that object.
4985  */
4986 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4987     if (!object)
4988         return NULL;
4990     SPCurve *curve = NULL;
4991     if (SP_IS_PATH(object)) {
4992         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4993         curve = curve_new->copy();
4994     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4995         const gchar *svgd = object->repr->attribute(key);
4996         if (svgd) {
4997             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4998             SPCurve *curve_new = new SPCurve(pv);
4999             if (curve_new) {
5000                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
5001             }
5002         }
5003     }
5005     return curve;
5008 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
5009     if (!np || !np->object || !curve)
5010         return;
5012     if (SP_IS_PATH(np->object)) {
5013         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
5014             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
5015         } else {
5016             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
5017         }
5018     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
5019         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(np->object)->get_lpe();
5020         if (lpe) {
5021             Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( lpe->getParameter(np->repr_key) );
5022             if (pathparam) {
5023                 pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
5024                 np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
5025             }
5026         }
5027     }
5030 /*
5031 SPCanvasItem *
5032 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
5033     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
5035 */
5038 /// \todo this code to generate a helper canvasitem from an spcurve should be moved to different file
5039 SPCanvasItem *
5040 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const Geom::Matrix & i2d, guint32 color = 0xff0000ff) {
5041     SPCurve *flash_curve = curve->copy();
5042     flash_curve->transform(i2d);
5043     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
5044     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
5045     // unless we also flash the nodes...
5046     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
5047     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
5048     sp_canvas_item_show(canvasitem);
5049     flash_curve->unref();
5050     return canvasitem;
5053 SPCanvasItem *
5054 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item) {
5055     if (!item || !desktop) {
5056         return NULL;
5057     }
5059     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5060     guint32 color = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
5062     Geom::Matrix i2d = sp_item_i2d_affine(item);
5064     SPCurve *curve = NULL;
5065     if (SP_IS_PATH(item)) {
5066         curve = sp_path_get_curve_for_edit(SP_PATH(item));
5067     } else if ( SP_IS_SHAPE(item) && SP_SHAPE(item)->curve ) {
5068         curve = sp_shape_get_curve (SP_SHAPE(item));
5069     } else if ( SP_IS_TEXT(item) ) {
5070         // do not display helperpath for text - we cannot do anything with it in Node tool anyway
5071         // curve = SP_TEXT(item)->getNormalizedBpath();
5072         return NULL;
5073     } else {
5074         g_warning ("-----> sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item): TODO: generate the helper path for this item type!\n");
5075         return NULL;
5076     }
5078     SPCanvasItem * helperpath = sp_nodepath_generate_helperpath(desktop, curve, i2d, color);
5080     curve->unref();
5082     return helperpath;
5086 // TODO: Merge this with sp_nodepath_make_helper_item()!
5087 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
5088     np->show_helperpath = show;
5090     if (show) {
5091         SPCurve *helper_curve = np->curve->copy();
5092         helper_curve->transform(np->i2d);
5093         if (!np->helper_path) {
5094             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
5096             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
5097             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);
5098             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
5099             sp_canvas_item_move_to_z(np->helper_path, 0);
5100             sp_canvas_item_show(np->helper_path);
5101         } else {
5102             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
5103         }
5104         helper_curve->unref();
5105     } else {
5106         if (np->helper_path) {
5107             GtkObject *temp = np->helper_path;
5108             np->helper_path = NULL;
5109             gtk_object_destroy(temp);
5110         }
5111     }
5114 /* sp_nodepath_make_straight_path:
5115  *   Prevents user from curving the path by dragging a segment or activating handles etc.
5116  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
5117  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
5118  */
5119 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
5120     np->straight_path = true;
5121     np->show_handles = false;
5122     g_message("add code to make the path straight.");
5123     // do sp_nodepath_convert_node_type on all nodes?
5124     // coding tip: search for this text : "Make selected segments lines"
5127 /*
5128   Local Variables:
5129   mode:c++
5130   c-file-style:"stroustrup"
5131   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5132   indent-tabs-mode:nil
5133   fill-column:99
5134   End:
5135 */
5136 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :