Code

f9a615583fa438c8689eaec09dfeabe9a88dc656
[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.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1223                 } else {
1224                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1225                 }
1226             } else if (n_has_handle && p_is_line) {
1227                 Radial line (node->p.other->pos - node->pos);
1228                 Radial handle (node->pos - node->n.pos);
1229                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1230                     // already half-smooth; pull opposite handle too making it fully smooth
1231                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1232                 } else {
1233                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1234                 }
1235             } else if (p_has_handle && node->n.other) {
1236                 // pull n handle
1237                 node->n.other->code = NR_CURVETO;
1238                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1239                     Geom::L2(node->p.pos - node->pos) :
1240                     Geom::L2(node->n.other->pos - node->pos) / 3;
1241                 node->n.pos = node->pos - (len / Geom::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1242             } else if (n_has_handle && node->p.other) {
1243                 // pull p handle
1244                 node->code = NR_CURVETO;
1245                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1246                     Geom::L2(node->n.pos - node->pos) :
1247                     Geom::L2(node->p.other->pos - node->pos) / 3;
1248                 node->p.pos = node->pos - (len / Geom::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1249             }
1250         } else if (!p_has_handle && !n_has_handle) {
1251             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1252                 // no handles, but both segments are either lnes or curves:
1253                 //pull both handles
1255                 // convert both to curves:
1256                 node->code = NR_CURVETO;
1257                 node->n.other->code = NR_CURVETO;
1259                 sp_node_adjust_handles_auto(node);
1260             } else {
1261                 // pull the handle opposite to line segment, making it half-smooth
1262                 if (p_is_line && node->n.other) {
1263                     if (type != Inkscape::NodePath::NODE_SYMM) {
1264                         // pull n handle
1265                         node->n.other->code = NR_CURVETO;
1266                         double len =  Geom::L2(node->n.other->pos - node->pos) / 3;
1267                         node->n.pos = node->pos + (len / Geom::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1268                     }
1269                 } else if (n_is_line && node->p.other) {
1270                     if (type != Inkscape::NodePath::NODE_SYMM) {
1271                         // pull p handle
1272                         node->code = NR_CURVETO;
1273                         double len =  Geom::L2(node->p.other->pos - node->pos) / 3;
1274                         node->p.pos = node->pos + (len / Geom::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1275                     }
1276                 }
1277             }
1278         }
1279     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1280         // cusping a cusp: retract nodes
1281         node->p.pos = node->pos;
1282         node->n.pos = node->pos;
1283     }
1285     sp_nodepath_set_node_type (node, type);
1288 /**
1289  * Move node to point, and adjust its and neighbouring handles.
1290  */
1291 void sp_node_moveto(Inkscape::NodePath::Node *node, Geom::Point p)
1293     if (node->type == Inkscape::NodePath::NODE_AUTO) {
1294         node->pos = p;
1295         sp_node_adjust_handles_auto(node);
1296     } else {
1297         Geom::Point delta = p - node->pos;
1298         node->pos = p;
1300         node->p.pos += delta;
1301         node->n.pos += delta;
1302     }
1304     Inkscape::NodePath::Node *node_p = NULL;
1305     Inkscape::NodePath::Node *node_n = NULL;
1307     if (node->p.other) {
1308         if (node->code == NR_LINETO) {
1309             sp_node_adjust_handle(node, 1);
1310             sp_node_adjust_handle(node->p.other, -1);
1311             node_p = node->p.other;
1312         }
1313         if (!node->p.other->selected && node->p.other->type == Inkscape::NodePath::NODE_AUTO) {
1314             sp_node_adjust_handles_auto(node->p.other);
1315             node_p = node->p.other;
1316         }
1317     }
1318     if (node->n.other) {
1319         if (node->n.other->code == NR_LINETO) {
1320             sp_node_adjust_handle(node, -1);
1321             sp_node_adjust_handle(node->n.other, 1);
1322             node_n = node->n.other;
1323         }
1324         if (!node->n.other->selected && node->n.other->type == Inkscape::NodePath::NODE_AUTO) {
1325             sp_node_adjust_handles_auto(node->n.other);
1326             node_n = node->n.other;
1327         }
1328     }
1330     // this function is only called from batch movers that will update display at the end
1331     // themselves, so here we just move all the knots without emitting move signals, for speed
1332     sp_node_update_handles(node, false);
1333     if (node_n) {
1334         sp_node_update_handles(node_n, false);
1335     }
1336     if (node_p) {
1337         sp_node_update_handles(node_p, false);
1338     }
1341 /**
1342  * Call sp_node_moveto() for node selection and handle possible snapping.
1343  */
1344 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, Geom::Coord dx, Geom::Coord dy,
1345                                             bool const snap, bool constrained = false,
1346                                             Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point())
1348     Geom::Point delta(dx, dy);
1349     Geom::Point best_pt = delta;
1350     Inkscape::SnappedPoint best;
1352     if (snap) {
1353         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1354          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we
1355          * must provide that information. */
1357         // Build a list of the unselected nodes to which the snapper should snap
1358         std::vector<std::pair<Geom::Point, int> > unselected_nodes;
1359         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1360             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1361             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1362                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1363                 if (!node->selected) {
1364                     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));
1365                 }
1366             }
1367         }
1369         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1371         // When only the node closest to the mouse pointer is to be snapped
1372         // then we will not even try to snap to other points and discard those immediately
1373         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1374         bool closest_only = prefs->getBool("/options/snapclosestonly/value", false);
1376         Inkscape::NodePath::Node *closest_node = NULL;
1377         Geom::Coord closest_dist = NR_HUGE;
1379         if (closest_only) {
1380                 for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1381                         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1382                         Geom::Coord dist = Geom::L2(nodepath->drag_origin_mouse - n->origin);
1383                         if (dist < closest_dist) {
1384                                 closest_node = n;
1385                                 closest_dist = dist;
1386                         }
1387                 }
1388         }
1390         // Iterate through all selected nodes
1391         m.setup(nodepath->desktop, false, nodepath->item, &unselected_nodes);
1392         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1393             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1394             if (!closest_only || n == closest_node) { //try to snap either all selected nodes or only the closest one
1395                     Inkscape::SnappedPoint s;
1396                     Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP);
1397                     if (constrained) {
1398                         Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1399                         dedicated_constraint.setPoint(n->pos);
1400                         s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type, dedicated_constraint, false);
1401                     } else {
1402                         s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type);
1403                     }
1405                     if (s.getSnapped()) {
1406                         s.setPointerDistance(Geom::L2(nodepath->drag_origin_mouse - n->origin));
1407                         if (!s.isOtherSnapBetter(best, true)) {
1408                                 best = s;
1409                                 best_pt = from_2geom(s.getPoint()) - n->pos;
1410                         }
1411                     }
1412             }
1413         }
1415         if (best.getSnapped()) {
1416             nodepath->desktop->snapindicator->set_new_snaptarget(best);
1417         } else {
1418             nodepath->desktop->snapindicator->remove_snaptarget();
1419         }
1420     }
1422     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1423         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1424         sp_node_moveto(n, n->pos + best_pt);
1425     }
1427     // do not update repr here so that node dragging is acceptably fast
1428     update_object(nodepath);
1431 /**
1432 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1433 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1434 near x = 0.
1435  */
1436 double
1437 sculpt_profile (double x, double alpha, guint profile)
1439     double result = 1;
1441     if (x >= 1) {
1442         result = 0;
1443     } else if (x <= 0) {
1444         result = 1;
1445     } else {
1446         switch (profile) {
1447             case SCULPT_PROFILE_LINEAR:
1448                 result = 1 - x;
1449                 break;
1450             case SCULPT_PROFILE_BELL:
1451                 result = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1452                 break;
1453             case SCULPT_PROFILE_ELLIPTIC:
1454                 result = sqrt(1 - x*x);
1455                 break;
1456             default:
1457                 g_assert_not_reached();
1458         }
1459     }
1461     return result;
1464 double
1465 bezier_length (Geom::Point a, Geom::Point ah, Geom::Point bh, Geom::Point b)
1467     // extremely primitive for now, don't have time to look for the real one
1468     double lower = Geom::L2(b - a);
1469     double upper = Geom::L2(ah - a) + Geom::L2(bh - ah) + Geom::L2(bh - b);
1470     return (lower + upper)/2;
1473 void
1474 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, Geom::Point delta, Geom::Point delta_n, Geom::Point delta_p)
1476     n->pos = n->origin + delta;
1477     n->n.pos = n->n.origin + delta_n;
1478     n->p.pos = n->p.origin + delta_p;
1479     sp_node_adjust_handles(n);
1480     sp_node_update_handles(n, false);
1483 /**
1484  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1485  * on how far they are from the dragged node n.
1486  */
1487 static void
1488 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, Geom::Point delta)
1490     g_assert (n);
1491     g_assert (nodepath);
1492     g_assert (n->subpath->nodepath == nodepath);
1494     double pressure = n->knot->pressure;
1495     if (pressure == 0)
1496         pressure = 0.5; // default
1497     pressure = CLAMP (pressure, 0.2, 0.8);
1499     // map pressure to alpha = 1/5 ... 5
1500     double alpha = 1 - 2 * fabs(pressure - 0.5);
1501     if (pressure > 0.5)
1502         alpha = 1/alpha;
1504     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1505     guint profile = prefs->getInt("/tools/nodes/sculpting_profile", SCULPT_PROFILE_BELL);
1507     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1508         // Only one subpath has selected nodes:
1509         // use linear mode, where the distance from n to node being dragged is calculated along the path
1511         double n_sel_range = 0, p_sel_range = 0;
1512         guint n_nodes = 0, p_nodes = 0;
1513         guint n_sel_nodes = 0, p_sel_nodes = 0;
1515         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1516         {
1517             double n_range = 0, p_range = 0;
1518             bool n_going = true, p_going = true;
1519             Inkscape::NodePath::Node *n_node = n;
1520             Inkscape::NodePath::Node *p_node = n;
1521             do {
1522                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1523                 if (n_node && n_going)
1524                     n_node = n_node->n.other;
1525                 if (n_node == NULL) {
1526                     n_going = false;
1527                 } else {
1528                     n_nodes ++;
1529                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1530                     if (n_node->selected) {
1531                         n_sel_nodes ++;
1532                         n_sel_range = n_range;
1533                     }
1534                     if (n_node == p_node) {
1535                         n_going = false;
1536                         p_going = false;
1537                     }
1538                 }
1539                 if (p_node && p_going)
1540                     p_node = p_node->p.other;
1541                 if (p_node == NULL) {
1542                     p_going = false;
1543                 } else {
1544                     p_nodes ++;
1545                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1546                     if (p_node->selected) {
1547                         p_sel_nodes ++;
1548                         p_sel_range = p_range;
1549                     }
1550                     if (p_node == n_node) {
1551                         n_going = false;
1552                         p_going = false;
1553                     }
1554                 }
1555             } while (n_going || p_going);
1556         }
1558         // Second pass: actually move nodes in this subpath
1559         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1560         {
1561             double n_range = 0, p_range = 0;
1562             bool n_going = true, p_going = true;
1563             Inkscape::NodePath::Node *n_node = n;
1564             Inkscape::NodePath::Node *p_node = n;
1565             do {
1566                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1567                 if (n_node && n_going)
1568                     n_node = n_node->n.other;
1569                 if (n_node == NULL) {
1570                     n_going = false;
1571                 } else {
1572                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1573                     if (n_node->selected) {
1574                         sp_nodepath_move_node_and_handles (n_node,
1575                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1576                                                            sculpt_profile ((n_range + Geom::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1577                                                            sculpt_profile ((n_range - Geom::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1578                     }
1579                     if (n_node == p_node) {
1580                         n_going = false;
1581                         p_going = false;
1582                     }
1583                 }
1584                 if (p_node && p_going)
1585                     p_node = p_node->p.other;
1586                 if (p_node == NULL) {
1587                     p_going = false;
1588                 } else {
1589                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1590                     if (p_node->selected) {
1591                         sp_nodepath_move_node_and_handles (p_node,
1592                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1593                                                            sculpt_profile ((p_range - Geom::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1594                                                            sculpt_profile ((p_range + Geom::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1595                     }
1596                     if (p_node == n_node) {
1597                         n_going = false;
1598                         p_going = false;
1599                     }
1600                 }
1601             } while (n_going || p_going);
1602         }
1604     } else {
1605         // Multiple subpaths have selected nodes:
1606         // use spatial mode, where the distance from n to node being dragged is measured directly as Geom::L2.
1607         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1608         // fix the pear-like shape when sculpting e.g. a ring
1610         // First pass: calculate range
1611         gdouble direct_range = 0;
1612         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1613             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1614             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1615                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1616                 if (node->selected) {
1617                     direct_range = MAX(direct_range, Geom::L2(node->origin - n->origin));
1618                 }
1619             }
1620         }
1622         // Second pass: actually move nodes
1623         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1624             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1625             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1626                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1627                 if (node->selected) {
1628                     if (direct_range > 1e-6) {
1629                         sp_nodepath_move_node_and_handles (node,
1630                                                        sculpt_profile (Geom::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1631                                                        sculpt_profile (Geom::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1632                                                        sculpt_profile (Geom::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1633                     } else {
1634                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1635                     }
1637                 }
1638             }
1639         }
1640     }
1642     // do not update repr here so that node dragging is acceptably fast
1643     update_object(nodepath);
1647 /**
1648  * Move node selection to point, adjust its and neighbouring handles,
1649  * handle possible snapping, and commit the change with possible undo.
1650  */
1651 void
1652 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1654     if (!nodepath) return;
1656     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1658     if (dx == 0) {
1659         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1660     } else if (dy == 0) {
1661         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1662     } else {
1663         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1664     }
1667 /**
1668  * Move node selection off screen and commit the change.
1669  */
1670 void
1671 sp_node_selected_move_screen(SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1673     // borrowed from sp_selection_move_screen in selection-chemistry.c
1674     // we find out the current zoom factor and divide deltas by it
1676     gdouble zoom = desktop->current_zoom();
1677     gdouble zdx = dx / zoom;
1678     gdouble zdy = dy / zoom;
1680     if (!nodepath) return;
1682     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1684     if (dx == 0) {
1685         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1686     } else if (dy == 0) {
1687         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1688     } else {
1689         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1690     }
1693 /**
1694  * Move selected nodes to the absolute position given
1695  */
1696 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis)
1698     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1699         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1700         Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]);
1701         sp_node_moveto(n, npos);
1702     }
1704     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1707 /**
1708  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return Geom::Nothing
1709  */
1710 boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1712     boost::optional<Geom::Coord> no_coord;
1713     g_return_val_if_fail(nodepath->selected, no_coord);
1715     // determine coordinate of first selected node
1716     GList *nsel = nodepath->selected;
1717     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1718     Geom::Coord coord = n->pos[axis];
1719     bool coincide = true;
1721     // compare it to the coordinates of all the other selected nodes
1722     for (GList *l = nsel->next; l != NULL; l = l->next) {
1723         n = (Inkscape::NodePath::Node *) l->data;
1724         if (n->pos[axis] != coord) {
1725             coincide = false;
1726         }
1727     }
1728     if (coincide) {
1729         return coord;
1730     } else {
1731         Geom::Rect bbox = sp_node_selected_bbox(nodepath);
1732         // currently we return the coordinate of the bounding box midpoint because I don't know how
1733         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1734         return bbox.midpoint()[axis];
1735     }
1738 /** If they don't yet exist, creates knot and line for the given side of the node */
1739 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1741     if (!side->knot) {
1742         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"));
1744         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1745         side->knot->setSize (7);
1746         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1747         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1748         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1749         sp_knot_update_ctrl(side->knot);
1751         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1752         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1753         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1754         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1755         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1756         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1757     }
1759     if (!side->line) {
1760         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1761                                         SP_TYPE_CTRLLINE, NULL);
1762     }
1765 /**
1766  * Ensure the given handle of the node is visible/invisible, update its screen position
1767  */
1768 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1770     g_assert(node != NULL);
1772    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1773     NRPathcode code = sp_node_path_code_from_side(node, side);
1775     show_handle = show_handle && (code == NR_CURVETO) && (Geom::L2(side->pos - node->pos) > 1e-6);
1777     if (show_handle) {
1778         if (!side->knot) { // No handle knot at all
1779             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1780             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1781             side->knot->pos = side->pos;
1782             if (side->knot->item)
1783                 SP_CTRL(side->knot->item)->moveto(side->pos);
1784             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1785             sp_knot_show(side->knot);
1786         } else {
1787             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1788                 if (fire_move_signals) {
1789                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1790                 } else {
1791                     sp_knot_moveto(side->knot, side->pos);
1792                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1793                 }
1794             }
1795             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1796                 sp_knot_show(side->knot);
1797             }
1798         }
1799         sp_canvas_item_show(side->line);
1800     } else {
1801         if (side->knot) {
1802             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1803                 sp_knot_hide(side->knot);
1804             }
1805         }
1806         if (side->line) {
1807             sp_canvas_item_hide(side->line);
1808         }
1809     }
1812 /**
1813  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1814  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1815  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1816  * updated; otherwise, just move the knots silently (used in batch moves).
1817  */
1818 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1820     g_assert(node != NULL);
1822     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1823         sp_knot_show(node->knot);
1824     }
1826     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1827         if (fire_move_signals)
1828             sp_knot_set_position(node->knot, node->pos, 0);
1829         else
1830             sp_knot_moveto(node->knot, node->pos);
1831     }
1833     gboolean show_handles = node->selected;
1834     if (node->p.other != NULL) {
1835         if (node->p.other->selected) show_handles = TRUE;
1836     }
1837     if (node->n.other != NULL) {
1838         if (node->n.other->selected) show_handles = TRUE;
1839     }
1841     if (node->subpath->nodepath->show_handles == false)
1842         show_handles = FALSE;
1844     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1845     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1848 /**
1849  * Call sp_node_update_handles() for all nodes on subpath.
1850  */
1851 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1853     g_assert(subpath != NULL);
1855     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1856         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1857     }
1860 /**
1861  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1862  */
1863 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1865     g_assert(nodepath != NULL);
1867     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1868         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1869     }
1872 void
1873 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1875     if (nodepath) {
1876         nodepath->show_handles = show;
1877         sp_nodepath_update_handles(nodepath);
1878     }
1881 /**
1882  * Adds all selected nodes in nodepath to list.
1883  */
1884 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1886     StlConv<Node *>::list(l, selected);
1887 /// \todo this adds a copying, rework when the selection becomes a stl list
1890 /**
1891  * Align selected nodes on the specified axis.
1892  */
1893 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1895     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1896         return;
1897     }
1899     if ( !nodepath->selected->next ) { // only one node selected
1900         return;
1901     }
1902    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1903     Geom::Point dest(pNode->pos);
1904     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1905         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1906         if (pNode) {
1907             dest[axis] = pNode->pos[axis];
1908             sp_node_moveto(pNode, dest);
1909         }
1910     }
1912     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1915 /// Helper struct.
1916 struct NodeSort
1918    Inkscape::NodePath::Node *_node;
1919     Geom::Coord _coord;
1920     /// \todo use vectorof pointers instead of calling copy ctor
1921     NodeSort(Inkscape::NodePath::Node *node, Geom::Dim2 axis) :
1922         _node(node), _coord(node->pos[axis])
1923     {}
1925 };
1927 static bool operator<(NodeSort const &a, NodeSort const &b)
1929     return (a._coord < b._coord);
1932 /**
1933  * Distribute selected nodes on the specified axis.
1934  */
1935 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1937     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1938         return;
1939     }
1941     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1942         return;
1943     }
1945    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1946     std::vector<NodeSort> sorted;
1947     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1948         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1949         if (pNode) {
1950             NodeSort n(pNode, axis);
1951             sorted.push_back(n);
1952             //dest[axis] = pNode->pos[axis];
1953             //sp_node_moveto(pNode, dest);
1954         }
1955     }
1956     std::sort(sorted.begin(), sorted.end());
1957     unsigned int len = sorted.size();
1958     //overall bboxes span
1959     float dist = (sorted.back()._coord -
1960                   sorted.front()._coord);
1961     //new distance between each bbox
1962     float step = (dist) / (len - 1);
1963     float pos = sorted.front()._coord;
1964     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1965           it < sorted.end();
1966           it ++ )
1967     {
1968         Geom::Point dest((*it)._node->pos);
1969         dest[axis] = pos;
1970         sp_node_moveto((*it)._node, dest);
1971         pos += step;
1972     }
1974     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1978 /**
1979  * Call sp_nodepath_line_add_node() for all selected segments.
1980  */
1981 void
1982 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1984     if (!nodepath) {
1985         return;
1986     }
1988     GList *nl = NULL;
1990     int n_added = 0;
1992     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1993        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1994         g_assert(t->selected);
1995         if (t->p.other && t->p.other->selected) {
1996             nl = g_list_prepend(nl, t);
1997         }
1998     }
2000     while (nl) {
2001        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
2002        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
2003        sp_nodepath_node_select(n, TRUE, FALSE);
2004        n_added ++;
2005        nl = g_list_remove(nl, t);
2006     }
2008     /** \todo fixme: adjust ? */
2009     sp_nodepath_update_handles(nodepath);
2011     if (n_added > 1) {
2012         sp_nodepath_update_repr(nodepath, _("Add nodes"));
2013     } else if (n_added > 0) {
2014         sp_nodepath_update_repr(nodepath, _("Add node"));
2015     }
2017     sp_nodepath_update_statusbar(nodepath);
2020 /**
2021  * Select segment nearest to point
2022  */
2023 void
2024 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p, bool toggle)
2026     if (!nodepath) {
2027         return;
2028     }
2030     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2031     Geom::PathVector const &pathv = curve->get_pathvector();
2032     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2033     if (!pvpos) {
2034         g_print ("Possible error?\n");
2035         return;
2036     }
2038     // calculate index for nodepath's representation.
2039     unsigned int segment_index = floor(pvpos->t) + 1;
2040     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2041         segment_index += pathv[i].size() + 1;
2042         if (pathv[i].closed()) {
2043             segment_index += 1;
2044         }
2045     }
2047     curve->unref();
2049     //find segment to segment
2050     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2052     //fixme: this can return NULL, so check before proceeding.
2053     g_return_if_fail(e != NULL);
2055     gboolean force = FALSE;
2056     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
2057         force = TRUE;
2058     }
2059     sp_nodepath_node_select(e, (gboolean) toggle, force);
2060     if (e->p.other)
2061         sp_nodepath_node_select(e->p.other, TRUE, force);
2063     sp_nodepath_update_handles(nodepath);
2065     sp_nodepath_update_statusbar(nodepath);
2068 /**
2069  * Add a node nearest to point
2070  */
2071 void
2072 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p)
2074     if (!nodepath) {
2075         return;
2076     }
2078     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2079     Geom::PathVector const &pathv = curve->get_pathvector();
2080     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2081     if (!pvpos) {
2082         g_print ("Possible error?\n");
2083         return;
2084     }
2086     // calculate index for nodepath's representation.
2087     double int_part;
2088     double t = std::modf(pvpos->t, &int_part);
2089     unsigned int segment_index = (unsigned int)int_part + 1;
2090     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2091         segment_index += pathv[i].size() + 1;
2092         if (pathv[i].closed()) {
2093             segment_index += 1;
2094         }
2095     }
2097     curve->unref();
2099     //find segment to split
2100     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2101     if (!e) {
2102         return;
2103     }
2105     //don't know why but t seems to flip for lines
2106     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2107         t = 1.0 - t;
2108     }
2110     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2111     sp_nodepath_node_select(n, FALSE, TRUE);
2113     /* fixme: adjust ? */
2114     sp_nodepath_update_handles(nodepath);
2116     sp_nodepath_update_repr(nodepath, _("Add node"));
2118     sp_nodepath_update_statusbar(nodepath);
2121 /*
2122  * Adjusts a segment so that t moves by a certain delta for dragging
2123  * converts lines to curves
2124  *
2125  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2126  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2127  */
2128 void
2129 sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, Geom::Point delta)
2131     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node);
2133     //fixme: e and e->p can be NULL, so check for those before proceeding
2134     g_return_if_fail(e != NULL);
2135     g_return_if_fail(&e->p != NULL);
2137     if (e->type == Inkscape::NodePath::NODE_AUTO) {
2138         e->type = Inkscape::NodePath::NODE_SMOOTH;
2139         sp_nodepath_update_node_knot (e);
2140     }
2141     if (e->p.other->type == Inkscape::NodePath::NODE_AUTO) {
2142         e->p.other->type = Inkscape::NodePath::NODE_SMOOTH;
2143         sp_nodepath_update_node_knot (e->p.other);
2144     }
2146     /* feel good is an arbitrary parameter that distributes the delta between handles
2147      * if t of the drag point is less than 1/6 distance form the endpoint only
2148      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2149      */
2150     double feel_good;
2151     if (t <= 1.0 / 6.0)
2152         feel_good = 0;
2153     else if (t <= 0.5)
2154         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2155     else if (t <= 5.0 / 6.0)
2156         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2157     else
2158         feel_good = 1;
2160     //if we're dragging a line convert it to a curve
2161     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2162         sp_nodepath_set_line_type(e, NR_CURVETO);
2163     }
2165     Geom::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2166     Geom::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2167     e->p.other->n.pos += offsetcoord0;
2168     e->p.pos += offsetcoord1;
2170     // adjust handles of adjacent nodes where necessary
2171     sp_node_adjust_handle(e,1);
2172     sp_node_adjust_handle(e->p.other,-1);
2174     sp_nodepath_update_handles(e->subpath->nodepath);
2176     update_object(e->subpath->nodepath);
2178     sp_nodepath_update_statusbar(e->subpath->nodepath);
2182 /**
2183  * Call sp_nodepath_break() for all selected segments.
2184  */
2185 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2187     if (!nodepath) return;
2189     GList *tempin = g_list_copy(nodepath->selected);
2190     GList *temp = NULL;
2191     for (GList *l = tempin; l != NULL; l = l->next) {
2192        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2193        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2194         if (nn == NULL) continue; // no break, no new node
2195         temp = g_list_prepend(temp, nn);
2196     }
2197     g_list_free(tempin);
2199     if (temp) {
2200         sp_nodepath_deselect(nodepath);
2201     }
2202     for (GList *l = temp; l != NULL; l = l->next) {
2203         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2204     }
2206     sp_nodepath_update_handles(nodepath);
2208     sp_nodepath_update_repr(nodepath, _("Break path"));
2211 /**
2212  * Duplicate the selected node(s).
2213  */
2214 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2216     if (!nodepath) {
2217         return;
2218     }
2220     GList *temp = NULL;
2221     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2222        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2223        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2224         if (nn == NULL) continue; // could not duplicate
2225         temp = g_list_prepend(temp, nn);
2226     }
2228     if (temp) {
2229         sp_nodepath_deselect(nodepath);
2230     }
2231     for (GList *l = temp; l != NULL; l = l->next) {
2232         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2233     }
2235     sp_nodepath_update_handles(nodepath);
2237     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2240 /**
2241  *  Internal function to join two nodes by merging them into one.
2242  */
2243 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2245     /* a and b are endpoints */
2247     // if one of the two nodes is mouseovered, fix its position
2248     Geom::Point c;
2249     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2250         c = a->pos;
2251     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2252         c = b->pos;
2253     } else {
2254         // otherwise, move joined node to the midpoint
2255         c = (a->pos + b->pos) / 2;
2256     }
2258     if (a->subpath == b->subpath) {
2259        Inkscape::NodePath::SubPath *sp = a->subpath;
2260         sp_nodepath_subpath_close(sp);
2261         sp_node_moveto (sp->first, c);
2263         sp_nodepath_update_handles(sp->nodepath);
2264         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2265         return;
2266     }
2268     /* a and b are separate subpaths */
2269     Inkscape::NodePath::SubPath *sa = a->subpath;
2270     Inkscape::NodePath::SubPath *sb = b->subpath;
2271     Geom::Point p;
2272     Inkscape::NodePath::Node *n;
2273     NRPathcode code;
2274     if (a == sa->first) {
2275         // we will now reverse sa, so that a is its last node, not first, and drop that node
2276         p = sa->first->n.pos;
2277         code = (NRPathcode)sa->first->n.other->code;
2278         // create new subpath
2279        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2280        // create a first moveto node on it
2281         n = sa->last;
2282         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2283         n = n->p.other;
2284         if (n == sa->first) n = NULL;
2285         while (n) {
2286             // copy the rest of the nodes from sa to t, going backwards
2287             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2288             n = n->p.other;
2289             if (n == sa->first) n = NULL;
2290         }
2291         // replace sa with t
2292         sp_nodepath_subpath_destroy(sa);
2293         sa = t;
2294     } else if (a == sa->last) {
2295         // a is already last, just drop it
2296         p = sa->last->p.pos;
2297         code = (NRPathcode)sa->last->code;
2298         sp_nodepath_node_destroy(sa->last);
2299     } else {
2300         code = NR_END;
2301         g_assert_not_reached();
2302     }
2304     if (b == sb->first) {
2305         // copy all nodes from b to a, forward
2306         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2307         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2308             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2309         }
2310     } else if (b == sb->last) {
2311         // copy all nodes from b to a, backward
2312         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2313         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2314             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2315         }
2316     } else {
2317         g_assert_not_reached();
2318     }
2319     /* and now destroy sb */
2321     sp_nodepath_subpath_destroy(sb);
2323     sp_nodepath_update_handles(sa->nodepath);
2325     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2327     sp_nodepath_update_statusbar(nodepath);
2330 /**
2331  *  Internal function to join two nodes by adding a segment between them.
2332  */
2333 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2335     if (a->subpath == b->subpath) {
2336        Inkscape::NodePath::SubPath *sp = a->subpath;
2338         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2339         sp->closed = TRUE;
2341         sp->first->p.other = sp->last;
2342         sp->last->n.other  = sp->first;
2344         sp_node_handle_mirror_p_to_n(sp->last);
2345         sp_node_handle_mirror_n_to_p(sp->first);
2347         sp->first->code = sp->last->code;
2348         sp->first       = sp->last;
2350         sp_nodepath_update_handles(sp->nodepath);
2352         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2354         return;
2355     }
2357     /* a and b are separate subpaths */
2358     Inkscape::NodePath::SubPath *sa = a->subpath;
2359     Inkscape::NodePath::SubPath *sb = b->subpath;
2361     Inkscape::NodePath::Node *n;
2362     Geom::Point p;
2363     NRPathcode code;
2364     if (a == sa->first) {
2365         code = (NRPathcode) sa->first->n.other->code;
2366        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2367         n = sa->last;
2368         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2369         for (n = n->p.other; n != NULL; n = n->p.other) {
2370             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2371         }
2372         sp_nodepath_subpath_destroy(sa);
2373         sa = t;
2374     } else if (a == sa->last) {
2375         code = (NRPathcode)sa->last->code;
2376     } else {
2377         code = NR_END;
2378         g_assert_not_reached();
2379     }
2381     if (b == sb->first) {
2382         n = sb->first;
2383         sp_node_handle_mirror_p_to_n(sa->last);
2384         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2385         sp_node_handle_mirror_n_to_p(sa->last);
2386         for (n = n->n.other; n != NULL; n = n->n.other) {
2387             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2388         }
2389     } else if (b == sb->last) {
2390         n = sb->last;
2391         sp_node_handle_mirror_p_to_n(sa->last);
2392         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2393         sp_node_handle_mirror_n_to_p(sa->last);
2394         for (n = n->p.other; n != NULL; n = n->p.other) {
2395             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2396         }
2397     } else {
2398         g_assert_not_reached();
2399     }
2400     /* and now destroy sb */
2402     sp_nodepath_subpath_destroy(sb);
2404     sp_nodepath_update_handles(sa->nodepath);
2406     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2409 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2411 /**
2412  * Internal function to handle joining two nodes.
2413  */
2414 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2416     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2418     if (g_list_length(nodepath->selected) != 2) {
2419         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2420         return;
2421     }
2423     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2424     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2426     g_assert(a != b);
2427     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2428         // someone tried to join an orphan node (i.e. a single-node subpath).
2429         // this is not worth an error message, just fail silently.
2430         return;
2431     }
2433     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2434         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2435         return;
2436     }
2438     switch(mode) {
2439         case NODE_JOIN_ENDPOINTS:
2440             do_node_selected_join(nodepath, a, b);
2441             break;
2442         case NODE_JOIN_SEGMENT:
2443             do_node_selected_join_segment(nodepath, a, b);
2444             break;
2445     }
2448 /**
2449  *  Join two nodes by merging them into one.
2450  */
2451 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2453     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2456 /**
2457  *  Join two nodes by adding a segment between them.
2458  */
2459 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2461     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2464 /**
2465  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2466  */
2467 void sp_node_delete_preserve(GList *nodes_to_delete)
2469     GSList *nodepaths = NULL;
2471     while (nodes_to_delete) {
2472         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2473         Inkscape::NodePath::SubPath *sp = node->subpath;
2474         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2475         Inkscape::NodePath::Node *sample_cursor = NULL;
2476         Inkscape::NodePath::Node *sample_end = NULL;
2477         Inkscape::NodePath::Node *delete_cursor = node;
2478         bool just_delete = false;
2480         //find the start of this contiguous selection
2481         //move left to the first node that is not selected
2482         //or the start of the non-closed path
2483         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2484             delete_cursor = curr;
2485         }
2487         //just delete at the beginning of an open path
2488         if (!delete_cursor->p.other) {
2489             sample_cursor = delete_cursor;
2490             just_delete = true;
2491         } else {
2492             sample_cursor = delete_cursor->p.other;
2493         }
2495         //calculate points for each segment
2496         int rate = 5;
2497         float period = 1.0 / rate;
2498         std::vector<Geom::Point> data;
2499         if (!just_delete) {
2500             data.push_back(sample_cursor->pos);
2501             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2502                 //just delete at the end of an open path
2503                 if (!sp->closed && curr == sp->last) {
2504                     just_delete = true;
2505                     break;
2506                 }
2508                 //sample points on the contiguous selected segment
2509                 Geom::Point *bez;
2510                 bez = new Geom::Point [4];
2511                 bez[0] = curr->pos;
2512                 bez[1] = curr->n.pos;
2513                 bez[2] = curr->n.other->p.pos;
2514                 bez[3] = curr->n.other->pos;
2515                 for (int i=1; i<rate; i++) {
2516                     gdouble t = i * period;
2517                     Geom::Point p = bezier_pt(3, bez, t);
2518                     data.push_back(p);
2519                 }
2520                 data.push_back(curr->n.other->pos);
2522                 sample_end = curr->n.other;
2523                 //break if we've come full circle or hit the end of the selection
2524                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2525                     break;
2526                 }
2527             }
2528         }
2530         if (!just_delete) {
2531             //calculate the best fitting single segment and adjust the endpoints
2532             Geom::Point *adata;
2533             adata = new Geom::Point [data.size()];
2534             copy(data.begin(), data.end(), adata);
2536             Geom::Point *bez;
2537             bez = new Geom::Point [4];
2538             //would decreasing error create a better fitting approximation?
2539             gdouble error = 1.0;
2540             gint ret;
2541             ret = Geom::bezier_fit_cubic (bez, adata, data.size(), error);
2543             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2544             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2545             //the resulting nodes behave as expected.
2546             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2547                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2548             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2549                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2551             //adjust endpoints
2552             sample_cursor->n.pos = bez[1];
2553             sample_end->p.pos = bez[2];
2554         }
2556         //destroy this contiguous selection
2557         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2558             Inkscape::NodePath::Node *temp = delete_cursor;
2559             if (delete_cursor->n.other == delete_cursor) {
2560                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2561                 delete_cursor = NULL;
2562             } else {
2563                 delete_cursor = delete_cursor->n.other;
2564             }
2565             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2566             sp_nodepath_node_destroy(temp);
2567         }
2569         sp_nodepath_update_handles(nodepath);
2571         if (!g_slist_find(nodepaths, nodepath))
2572             nodepaths = g_slist_prepend (nodepaths, nodepath);
2573     }
2575     for (GSList *i = nodepaths; i; i = i->next) {
2576         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2577         // different nodepaths will give us one undo event per nodepath
2578         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2580         // if the entire nodepath is removed, delete the selected object.
2581         if (nodepath->subpaths == NULL ||
2582             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2583             //at least 2
2584             sp_nodepath_get_node_count(nodepath) < 2) {
2585             SPDocument *document = sp_desktop_document (nodepath->desktop);
2586             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2587             //delete this nodepath's object, not the entire selection! (though at this time, this
2588             //does not matter)
2589             sp_selection_delete(nodepath->desktop);
2590             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2591                               _("Delete nodes"));
2592         } else {
2593             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2594             sp_nodepath_update_statusbar(nodepath);
2595         }
2596     }
2598     g_slist_free (nodepaths);
2601 /**
2602  * Delete one or more selected nodes.
2603  */
2604 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2606     if (!nodepath) return;
2607     if (!nodepath->selected) return;
2609     /** \todo fixme: do it the right way */
2610     while (nodepath->selected) {
2611        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2612         sp_nodepath_node_destroy(node);
2613     }
2616     //clean up the nodepath (such as for trivial subpaths)
2617     sp_nodepath_cleanup(nodepath);
2619     sp_nodepath_update_handles(nodepath);
2621     // if the entire nodepath is removed, delete the selected object.
2622     if (nodepath->subpaths == NULL ||
2623         sp_nodepath_get_node_count(nodepath) < 2) {
2624         SPDocument *document = sp_desktop_document (nodepath->desktop);
2625         sp_selection_delete(nodepath->desktop);
2626         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2627                           _("Delete nodes"));
2628         return;
2629     }
2631     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2633     sp_nodepath_update_statusbar(nodepath);
2636 /**
2637  * Delete one or more segments between two selected nodes.
2638  * This is the code for 'split'.
2639  */
2640 void
2641 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2643    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2644    Inkscape::NodePath::Node *curr, *next;     //Iterators
2646     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2648     if (g_list_length(nodepath->selected) != 2) {
2649         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2650                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2651         return;
2652     }
2654     //Selected nodes, not inclusive
2655    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2656    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2658     if ( ( a==b)                       ||  //same node
2659          (a->subpath  != b->subpath )  ||  //not the same path
2660          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2661          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2662     {
2663         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2664                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2665         return;
2666     }
2668     //###########################################
2669     //# BEGIN EDITS
2670     //###########################################
2671     //##################################
2672     //# CLOSED PATH
2673     //##################################
2674     if (a->subpath->closed) {
2677         gboolean reversed = FALSE;
2679         //Since we can go in a circle, we need to find the shorter distance.
2680         //  a->b or b->a
2681         start = end = NULL;
2682         int distance    = 0;
2683         int minDistance = 0;
2684         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2685             if (curr==b) {
2686                 //printf("a to b:%d\n", distance);
2687                 start = a;//go from a to b
2688                 end   = b;
2689                 minDistance = distance;
2690                 //printf("A to B :\n");
2691                 break;
2692             }
2693             distance++;
2694         }
2696         //try again, the other direction
2697         distance = 0;
2698         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2699             if (curr==a) {
2700                 //printf("b to a:%d\n", distance);
2701                 if (distance < minDistance) {
2702                     start    = b;  //we go from b to a
2703                     end      = a;
2704                     reversed = TRUE;
2705                     //printf("B to A\n");
2706                 }
2707                 break;
2708             }
2709             distance++;
2710         }
2713         //Copy everything from 'end' to 'start' to a new subpath
2714        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2715         for (curr=end ; curr ; curr=curr->n.other) {
2716             NRPathcode code = (NRPathcode) curr->code;
2717             if (curr == end)
2718                 code = NR_MOVETO;
2719             sp_nodepath_node_new(t, NULL,
2720                                  (Inkscape::NodePath::NodeType)curr->type, code,
2721                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2722             if (curr == start)
2723                 break;
2724         }
2725         sp_nodepath_subpath_destroy(a->subpath);
2728     }
2732     //##################################
2733     //# OPEN PATH
2734     //##################################
2735     else {
2737         //We need to get the direction of the list between A and B
2738         //Can we walk from a to b?
2739         start = end = NULL;
2740         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2741             if (curr==b) {
2742                 start = a;  //did it!  we go from a to b
2743                 end   = b;
2744                 //printf("A to B\n");
2745                 break;
2746             }
2747         }
2748         if (!start) {//didn't work?  let's try the other direction
2749             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2750                 if (curr==a) {
2751                     start = b;  //did it!  we go from b to a
2752                     end   = a;
2753                     //printf("B to A\n");
2754                     break;
2755                 }
2756             }
2757         }
2758         if (!start) {
2759             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2760                                                      _("Cannot find path between nodes."));
2761             return;
2762         }
2766         //Copy everything after 'end' to a new subpath
2767        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2768         for (curr=end ; curr ; curr=curr->n.other) {
2769             NRPathcode code = (NRPathcode) curr->code;
2770             if (curr == end)
2771                 code = NR_MOVETO;
2772             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2773                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2774         }
2776         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2777         for (curr = start->n.other ; curr  ; curr=next) {
2778             next = curr->n.other;
2779             sp_nodepath_node_destroy(curr);
2780         }
2782     }
2783     //###########################################
2784     //# END EDITS
2785     //###########################################
2787     //clean up the nodepath (such as for trivial subpaths)
2788     sp_nodepath_cleanup(nodepath);
2790     sp_nodepath_update_handles(nodepath);
2792     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2794     sp_nodepath_update_statusbar(nodepath);
2797 /**
2798  * Call sp_nodepath_set_line() for all selected segments.
2799  */
2800 void
2801 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2803     if (nodepath == NULL) return;
2805     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2806        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2807         g_assert(n->selected);
2808         if (n->p.other && n->p.other->selected) {
2809             sp_nodepath_set_line_type(n, code);
2810         }
2811     }
2813     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2816 /**
2817  * Call sp_nodepath_convert_node_type() for all selected nodes.
2818  */
2819 void
2820 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2822     if (nodepath == NULL) return;
2824     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2826     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2827         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2828     }
2830     sp_nodepath_update_repr(nodepath, _("Change node type"));
2833 /**
2834  * Change select status of node, update its own and neighbour handles.
2835  */
2836 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2838     node->selected = selected;
2840     if (selected) {
2841         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 11 : 9);
2842         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2843         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2844         sp_knot_update_ctrl(node->knot);
2845     } else {
2846         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 9 : 7);
2847         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2848         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2849         sp_knot_update_ctrl(node->knot);
2850     }
2852     sp_node_update_handles(node);
2853     if (node->n.other) sp_node_update_handles(node->n.other);
2854     if (node->p.other) sp_node_update_handles(node->p.other);
2857 /**
2858 \brief Select a node
2859 \param node     The node to select
2860 \param incremental   If true, add to selection, otherwise deselect others
2861 \param override   If true, always select this node, otherwise toggle selected status
2862 */
2863 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2865     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2867     if (incremental) {
2868         if (override) {
2869             if (!g_list_find(nodepath->selected, node)) {
2870                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2871             }
2872             sp_node_set_selected(node, TRUE);
2873         } else { // toggle
2874             if (node->selected) {
2875                 g_assert(g_list_find(nodepath->selected, node));
2876                 nodepath->selected = g_list_remove(nodepath->selected, node);
2877             } else {
2878                 g_assert(!g_list_find(nodepath->selected, node));
2879                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2880             }
2881             sp_node_set_selected(node, !node->selected);
2882         }
2883     } else {
2884         sp_nodepath_deselect(nodepath);
2885         nodepath->selected = g_list_prepend(nodepath->selected, node);
2886         sp_node_set_selected(node, TRUE);
2887     }
2889     sp_nodepath_update_statusbar(nodepath);
2893 /**
2894 \brief Deselect all nodes in the nodepath
2895 */
2896 void
2897 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2899     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2901     while (nodepath->selected) {
2902         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2903         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2904     }
2905     sp_nodepath_update_statusbar(nodepath);
2908 /**
2909 \brief Select or invert selection of all nodes in the nodepath
2910 */
2911 void
2912 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2914     if (!nodepath) return;
2916     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2917        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2918         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2919            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2920            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2921         }
2922     }
2925 /**
2926  * If nothing selected, does the same as sp_nodepath_select_all();
2927  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2928  * (i.e., similar to "select all in layer", with the "selected" subpaths
2929  * being treated as "layers" in the path).
2930  */
2931 void
2932 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2934     if (!nodepath) return;
2936     if (g_list_length (nodepath->selected) == 0) {
2937         sp_nodepath_select_all (nodepath, invert);
2938         return;
2939     }
2941     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2942     GSList *subpaths = NULL;
2944     for (GList *l = copy; l != NULL; l = l->next) {
2945         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2946         Inkscape::NodePath::SubPath *subpath = n->subpath;
2947         if (!g_slist_find (subpaths, subpath))
2948             subpaths = g_slist_prepend (subpaths, subpath);
2949     }
2951     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2952         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2953         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2954             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2955             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2956         }
2957     }
2959     g_slist_free (subpaths);
2960     g_list_free (copy);
2963 /**
2964  * \brief Select the node after the last selected; if none is selected,
2965  * select the first within path.
2966  */
2967 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2969     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2971    Inkscape::NodePath::Node *last = NULL;
2972     if (nodepath->selected) {
2973         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2974            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2975             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2976             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2977                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2978                 if (node->selected) {
2979                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2980                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2981                             if (spl->next) { // there's a next subpath
2982                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2983                                 last = subpath_next->first;
2984                             } else if (spl->prev) { // there's a previous subpath
2985                                 last = NULL; // to be set later to the first node of first subpath
2986                             } else {
2987                                 last = node->n.other;
2988                             }
2989                         } else {
2990                             last = node->n.other;
2991                         }
2992                     } else {
2993                         if (node->n.other) {
2994                             last = node->n.other;
2995                         } else {
2996                             if (spl->next) { // there's a next subpath
2997                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2998                                 last = subpath_next->first;
2999                             } else if (spl->prev) { // there's a previous subpath
3000                                 last = NULL; // to be set later to the first node of first subpath
3001                             } else {
3002                                 last = (Inkscape::NodePath::Node *) subpath->first;
3003                             }
3004                         }
3005                     }
3006                 }
3007             }
3008         }
3009         sp_nodepath_deselect(nodepath);
3010     }
3012     if (last) { // there's at least one more node after selected
3013         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3014     } else { // no more nodes, select the first one in first subpath
3015        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
3016         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
3017     }
3020 /**
3021  * \brief Select the node before the first selected; if none is selected,
3022  * select the last within path
3023  */
3024 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
3026     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
3028    Inkscape::NodePath::Node *last = NULL;
3029     if (nodepath->selected) {
3030         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
3031            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3032             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
3033                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3034                 if (node->selected) {
3035                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
3036                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
3037                             if (spl->prev) { // there's a prev subpath
3038                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3039                                 last = subpath_prev->last;
3040                             } else if (spl->next) { // there's a next subpath
3041                                 last = NULL; // to be set later to the last node of last subpath
3042                             } else {
3043                                 last = node->p.other;
3044                             }
3045                         } else {
3046                             last = node->p.other;
3047                         }
3048                     } else {
3049                         if (node->p.other) {
3050                             last = node->p.other;
3051                         } else {
3052                             if (spl->prev) { // there's a prev subpath
3053                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3054                                 last = subpath_prev->last;
3055                             } else if (spl->next) { // there's a next subpath
3056                                 last = NULL; // to be set later to the last node of last subpath
3057                             } else {
3058                                 last = (Inkscape::NodePath::Node *) subpath->last;
3059                             }
3060                         }
3061                     }
3062                 }
3063             }
3064         }
3065         sp_nodepath_deselect(nodepath);
3066     }
3068     if (last) { // there's at least one more node before selected
3069         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3070     } else { // no more nodes, select the last one in last subpath
3071         GList *spl = g_list_last(nodepath->subpaths);
3072        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3073         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
3074     }
3077 /**
3078  * \brief Select all nodes that are within the rectangle.
3079  */
3080 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, Geom::Rect const &b, gboolean incremental)
3082     if (!incremental) {
3083         sp_nodepath_deselect(nodepath);
3084     }
3086     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3087        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3088         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3089            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3091             if (b.contains(node->pos)) {
3092                 sp_nodepath_node_select(node, TRUE, TRUE);
3093             }
3094         }
3095     }
3099 void
3100 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3102     g_assert (n);
3103     g_assert (nodepath);
3104     g_assert (n->subpath->nodepath == nodepath);
3106     if (g_list_length (nodepath->selected) == 0) {
3107         if (grow > 0) {
3108             sp_nodepath_node_select(n, TRUE, TRUE);
3109         }
3110         return;
3111     }
3113     if (g_list_length (nodepath->selected) == 1) {
3114         if (grow < 0) {
3115             sp_nodepath_deselect (nodepath);
3116             return;
3117         }
3118     }
3120         double n_sel_range = 0, p_sel_range = 0;
3121             Inkscape::NodePath::Node *farthest_n_node = n;
3122             Inkscape::NodePath::Node *farthest_p_node = n;
3124         // Calculate ranges
3125         {
3126             double n_range = 0, p_range = 0;
3127             bool n_going = true, p_going = true;
3128             Inkscape::NodePath::Node *n_node = n;
3129             Inkscape::NodePath::Node *p_node = n;
3130             do {
3131                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3132                 if (n_node && n_going)
3133                     n_node = n_node->n.other;
3134                 if (n_node == NULL) {
3135                     n_going = false;
3136                 } else {
3137                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3138                     if (n_node->selected) {
3139                         n_sel_range = n_range;
3140                         farthest_n_node = n_node;
3141                     }
3142                     if (n_node == p_node) {
3143                         n_going = false;
3144                         p_going = false;
3145                     }
3146                 }
3147                 if (p_node && p_going)
3148                     p_node = p_node->p.other;
3149                 if (p_node == NULL) {
3150                     p_going = false;
3151                 } else {
3152                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3153                     if (p_node->selected) {
3154                         p_sel_range = p_range;
3155                         farthest_p_node = p_node;
3156                     }
3157                     if (p_node == n_node) {
3158                         n_going = false;
3159                         p_going = false;
3160                     }
3161                 }
3162             } while (n_going || p_going);
3163         }
3165     if (grow > 0) {
3166         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3167                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3168         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3169                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3170         }
3171     } else {
3172         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3173                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3174         } else if (farthest_p_node && farthest_p_node->selected) {
3175                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3176         }
3177     }
3180 void
3181 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3183     g_assert (n);
3184     g_assert (nodepath);
3185     g_assert (n->subpath->nodepath == nodepath);
3187     if (g_list_length (nodepath->selected) == 0) {
3188         if (grow > 0) {
3189             sp_nodepath_node_select(n, TRUE, TRUE);
3190         }
3191         return;
3192     }
3194     if (g_list_length (nodepath->selected) == 1) {
3195         if (grow < 0) {
3196             sp_nodepath_deselect (nodepath);
3197             return;
3198         }
3199     }
3201     Inkscape::NodePath::Node *farthest_selected = NULL;
3202     double farthest_dist = 0;
3204     Inkscape::NodePath::Node *closest_unselected = NULL;
3205     double closest_dist = NR_HUGE;
3207     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3208        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3209         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3210            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3211            if (node == n)
3212                continue;
3213            if (node->selected) {
3214                if (Geom::L2(node->pos - n->pos) > farthest_dist) {
3215                    farthest_dist = Geom::L2(node->pos - n->pos);
3216                    farthest_selected = node;
3217                }
3218            } else {
3219                if (Geom::L2(node->pos - n->pos) < closest_dist) {
3220                    closest_dist = Geom::L2(node->pos - n->pos);
3221                    closest_unselected = node;
3222                }
3223            }
3224         }
3225     }
3227     if (grow > 0) {
3228         if (closest_unselected) {
3229             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3230         }
3231     } else {
3232         if (farthest_selected) {
3233             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3234         }
3235     }
3239 /**
3240 \brief  Saves all nodes' and handles' current positions in their origin members
3241 */
3242 void
3243 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3245     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3246        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3247         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3248            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3249            n->origin = n->pos;
3250            n->p.origin = n->p.pos;
3251            n->n.origin = n->n.pos;
3252         }
3253     }
3256 /**
3257 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3258 */
3259 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3261     GList *r = NULL;
3262     if (nodepath->selected) {
3263         guint i = 0;
3264         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3265             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3266             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3267                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3268                 i++;
3269                 if (node->selected) {
3270                     r = g_list_append(r, GINT_TO_POINTER(i));
3271                 }
3272             }
3273         }
3274     }
3275     return r;
3278 /**
3279 \brief  Restores selection by selecting nodes whose positions are in the list
3280 */
3281 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3283     sp_nodepath_deselect(nodepath);
3285     guint i = 0;
3286     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3287        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3288         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3289            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3290             i++;
3291             if (g_list_find(r, GINT_TO_POINTER(i))) {
3292                 sp_nodepath_node_select(node, TRUE, TRUE);
3293             }
3294         }
3295     }
3299 /**
3300 \brief Adjusts handle according to node type and line code.
3301 */
3302 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3304     g_assert(node);
3306     // nothing to do for auto nodes (sp_node_adjust_handles() does the job)
3307     if (node->type == Inkscape::NodePath::NODE_AUTO)
3308         return;
3310    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3311    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3313    // nothing to do if we are an end node
3314     if (me->other == NULL) return;
3315     if (other->other == NULL) return;
3317     // nothing to do if we are a cusp node
3318     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3320     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3321     NRPathcode mecode;
3322     if (which_adjust == 1) {
3323         mecode = (NRPathcode)me->other->code;
3324     } else {
3325         mecode = (NRPathcode)node->code;
3326     }
3327     if (mecode == NR_LINETO) return;
3329     if (sp_node_side_is_line(node, other)) {
3330         // other is a line, and we are either smooth or symm
3331        Inkscape::NodePath::Node *othernode = other->other;
3332         double len = Geom::L2(me->pos - node->pos);
3333         Geom::Point delta = node->pos - othernode->pos;
3334         double linelen = Geom::L2(delta);
3335         if (linelen < 1e-18)
3336             return;
3337         me->pos = node->pos + (len / linelen)*delta;
3338         return;
3339     }
3341     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3342         // symmetrize
3343         me->pos = 2 * node->pos - other->pos;
3344         return;
3345     } else {
3346         // smoothify
3347         double len = Geom::L2(me->pos - node->pos);
3348         Geom::Point delta = other->pos - node->pos;
3349         double otherlen = Geom::L2(delta);
3350         if (otherlen < 1e-18) return;
3351         me->pos = node->pos - (len / otherlen) * delta;
3352     }
3355 /**
3356  \brief Adjusts both handles according to node type and line code
3357  */
3358 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3360     g_assert(node);
3362     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3364     /* we are either smooth or symm */
3366     if (node->p.other == NULL) return;
3367     if (node->n.other == NULL) return;
3369     if (node->type == Inkscape::NodePath::NODE_AUTO) {
3370         sp_node_adjust_handles_auto(node);
3371         return;
3372     }
3374     if (sp_node_side_is_line(node, &node->p)) {
3375         sp_node_adjust_handle(node, 1);
3376         return;
3377     }
3379     if (sp_node_side_is_line(node, &node->n)) {
3380         sp_node_adjust_handle(node, -1);
3381         return;
3382     }
3384     /* both are curves */
3385     Geom::Point const delta( node->n.pos - node->p.pos );
3387     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3388         node->p.pos = node->pos - delta / 2;
3389         node->n.pos = node->pos + delta / 2;
3390         return;
3391     }
3393     /* We are smooth */
3394     double plen = Geom::L2(node->p.pos - node->pos);
3395     if (plen < 1e-18) return;
3396     double nlen = Geom::L2(node->n.pos - node->pos);
3397     if (nlen < 1e-18) return;
3398     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3399     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3402 static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node)
3404     if (node->p.other == NULL || node->n.other == NULL) {
3405         node->p.pos = node->pos;
3406         node->n.pos = node->pos;
3407         return;
3408     }
3410     Geom::Point leg_prev = to_2geom(node->p.other->pos - node->pos);
3411     Geom::Point leg_next = to_2geom(node->n.other->pos - node->pos);
3413     double norm_leg_prev = Geom::L2(leg_prev);
3414     double norm_leg_next = Geom::L2(leg_next);
3416     Geom::Point delta;
3417     if (norm_leg_next > 0.0) {
3418         delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
3419         delta.normalize();
3420     }
3422     node->p.pos = node->pos - norm_leg_prev / 3 * delta;
3423     node->n.pos = node->pos + norm_leg_next / 3 * delta;
3426 /**
3427  * Node event callback.
3428  */
3429 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3431     gboolean ret = FALSE;
3432     switch (event->type) {
3433         case GDK_ENTER_NOTIFY:
3434             Inkscape::NodePath::Path::active_node = n;
3435             break;
3436         case GDK_LEAVE_NOTIFY:
3437             Inkscape::NodePath::Path::active_node = NULL;
3438             break;
3439         case GDK_SCROLL:
3440             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3441                 switch (event->scroll.direction) {
3442                     case GDK_SCROLL_UP:
3443                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3444                         break;
3445                     case GDK_SCROLL_DOWN:
3446                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3447                         break;
3448                     default:
3449                         break;
3450                 }
3451                 ret = TRUE;
3452             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3453                 switch (event->scroll.direction) {
3454                     case GDK_SCROLL_UP:
3455                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3456                         break;
3457                     case GDK_SCROLL_DOWN:
3458                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3459                         break;
3460                     default:
3461                         break;
3462                 }
3463                 ret = TRUE;
3464             }
3465             break;
3466         case GDK_KEY_PRESS:
3467             switch (get_group0_keyval (&event->key)) {
3468                 case GDK_space:
3469                     if (event->key.state & GDK_BUTTON1_MASK) {
3470                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3471                         stamp_repr(nodepath);
3472                         ret = TRUE;
3473                     }
3474                     break;
3475                 case GDK_Page_Up:
3476                     if (event->key.state & GDK_CONTROL_MASK) {
3477                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3478                     } else {
3479                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3480                     }
3481                     break;
3482                 case GDK_Page_Down:
3483                     if (event->key.state & GDK_CONTROL_MASK) {
3484                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3485                     } else {
3486                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3487                     }
3488                     break;
3489                 default:
3490                     break;
3491             }
3492             break;
3493         default:
3494             break;
3495     }
3497     return ret;
3500 /**
3501  * Handle keypress on node; directly called.
3502  */
3503 gboolean node_key(GdkEvent *event)
3505     Inkscape::NodePath::Path *np;
3507     // there is no way to verify nodes so set active_node to nil when deleting!!
3508     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3510     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3511         gint ret = FALSE;
3512         switch (get_group0_keyval (&event->key)) {
3513             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3514             case GDK_BackSpace:
3515                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3516                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3517                 sp_nodepath_update_repr(np, _("Delete node"));
3518                 Inkscape::NodePath::Path::active_node = NULL;
3519                 ret = TRUE;
3520                 break;
3521             case GDK_c:
3522                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3523                 ret = TRUE;
3524                 break;
3525             case GDK_s:
3526                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3527                 ret = TRUE;
3528                 break;
3529             case GDK_a:
3530                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_AUTO);
3531                 ret = TRUE;
3532                 break;
3533             case GDK_y:
3534                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3535                 ret = TRUE;
3536                 break;
3537             case GDK_b:
3538                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3539                 ret = TRUE;
3540                 break;
3541         }
3542         return ret;
3543     }
3544     return FALSE;
3547 /**
3548  * Mouseclick on node callback.
3549  */
3550 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3552    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3554     if (state & GDK_CONTROL_MASK) {
3555         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3557         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3558             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3559                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3560             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3561                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3562             } else if (n->type == Inkscape::NodePath::NODE_SYMM) {
3563                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_AUTO);
3564             } else {
3565                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3566             }
3567             sp_nodepath_update_repr(nodepath, _("Change node type"));
3568             sp_nodepath_update_statusbar(nodepath);
3570         } else { //ctrl+alt+click: delete node
3571             GList *node_to_delete = NULL;
3572             node_to_delete = g_list_append(node_to_delete, n);
3573             sp_node_delete_preserve(node_to_delete);
3574         }
3576     } else {
3577         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3578     }
3581 /**
3582  * Mouse grabbed node callback.
3583  */
3584 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3586    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3588     if (!n->selected) {
3589         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3590     }
3592     n->is_dragging = true;
3593     // Reconstruct and store the location of the mouse pointer at the time when we started dragging (needed for snapping)
3594     n->subpath->nodepath->drag_origin_mouse = knot->grabbed_rel_pos + knot->drag_origin;
3596     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3598     sp_nodepath_remember_origins (n->subpath->nodepath);
3601 /**
3602  * Mouse ungrabbed node callback.
3603  */
3604 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3606    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3608    n->dragging_out = NULL;
3609    n->is_dragging = false;
3610    n->subpath->nodepath->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
3611    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3613    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3616 /**
3617  * The point on a line, given by its angle, closest to the given point.
3618  * \param p  A point.
3619  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3620  * \param closest  Pointer to the point struct where the result is stored.
3621  * \todo FIXME: use dot product perhaps?
3622  */
3623 static void point_line_closest(Geom::Point *p, double a, Geom::Point *closest)
3625     if (a == HUGE_VAL) { // vertical
3626         *closest = Geom::Point(0, (*p)[Geom::Y]);
3627     } else {
3628         (*closest)[Geom::X] = ( a * (*p)[Geom::Y] + (*p)[Geom::X]) / (a*a + 1);
3629         (*closest)[Geom::Y] = a * (*closest)[Geom::X];
3630     }
3633 /**
3634  * Distance from the point to a line given by its angle.
3635  * \param p  A point.
3636  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3637  */
3638 static double point_line_distance(Geom::Point *p, double a)
3640     Geom::Point c;
3641     point_line_closest(p, a, &c);
3642     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]));
3645 /**
3646  * Callback for node "request" signal.
3647  * \todo fixme: This goes to "moved" event? (lauris)
3648  */
3649 static gboolean
3650 node_request(SPKnot */*knot*/, Geom::Point const &p, guint state, gpointer data)
3652     double yn, xn, yp, xp;
3653     double an, ap, na, pa;
3654     double d_an, d_ap, d_na, d_pa;
3655     gboolean collinear = FALSE;
3656     Geom::Point c;
3657     Geom::Point pr;
3659     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3661     n->subpath->nodepath->desktop->snapindicator->remove_snaptarget();
3663     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3664     if ( (!n->subpath->nodepath->straight_path) &&
3665          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3666            || n->dragging_out ) )
3667     {
3668        Geom::Point mouse = p;
3670        if (!n->dragging_out) {
3671            // This is the first drag-out event; find out which handle to drag out
3672            double appr_n = (n->n.other ? Geom::L2(n->n.other->pos - n->pos) - Geom::L2(n->n.other->pos - p) : -HUGE_VAL);
3673            double appr_p = (n->p.other ? Geom::L2(n->p.other->pos - n->pos) - Geom::L2(n->p.other->pos - p) : -HUGE_VAL);
3675            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3676                return FALSE;
3678            Inkscape::NodePath::NodeSide *opposite;
3679            if (appr_p > appr_n) { // closer to p
3680                n->dragging_out = &n->p;
3681                opposite = &n->n;
3682                n->code = NR_CURVETO;
3683            } else if (appr_p < appr_n) { // closer to n
3684                n->dragging_out = &n->n;
3685                opposite = &n->p;
3686                n->n.other->code = NR_CURVETO;
3687            } else { // p and n nodes are the same
3688                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3689                    n->dragging_out = &n->p;
3690                    opposite = &n->n;
3691                    n->code = NR_CURVETO;
3692                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3693                    n->dragging_out = &n->n;
3694                    opposite = &n->p;
3695                    n->n.other->code = NR_CURVETO;
3696                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3697                    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);
3698                    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);
3699                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3700                        n->dragging_out = &n->n;
3701                        opposite = &n->p;
3702                        n->n.other->code = NR_CURVETO;
3703                    } else { // closer to other's n handle
3704                        n->dragging_out = &n->p;
3705                        opposite = &n->n;
3706                        n->code = NR_CURVETO;
3707                    }
3708                }
3709            }
3711            // if there's another handle, make sure the one we drag out starts parallel to it
3712            if (opposite->pos != n->pos) {
3713                mouse = n->pos - Geom::L2(mouse - n->pos) * Geom::unit_vector(opposite->pos - n->pos);
3714            }
3716            // knots might not be created yet!
3717            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3718            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3719        }
3721        // pass this on to the handle-moved callback
3722        node_handle_moved(n->dragging_out->knot, mouse, state, (gpointer) n);
3723        sp_node_update_handles(n);
3724        return TRUE;
3725    }
3727     if (state & GDK_CONTROL_MASK) { // constrained motion
3729         // calculate relative distances of handles
3730         // n handle:
3731         yn = n->n.pos[Geom::Y] - n->pos[Geom::Y];
3732         xn = n->n.pos[Geom::X] - n->pos[Geom::X];
3733         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3734         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3735             if (n->n.other) { // if there is the next point
3736                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3737                     yn = n->n.other->origin[Geom::Y] - n->origin[Geom::Y]; // use origin because otherwise the direction will change as you drag
3738                     xn = n->n.other->origin[Geom::X] - n->origin[Geom::X];
3739             }
3740         }
3741         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3742         if (yn < 0) { xn = -xn; yn = -yn; }
3744         // p handle:
3745         yp = n->p.pos[Geom::Y] - n->pos[Geom::Y];
3746         xp = n->p.pos[Geom::X] - n->pos[Geom::X];
3747         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3748         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3749             if (n->p.other) {
3750                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3751                     yp = n->p.other->origin[Geom::Y] - n->origin[Geom::Y];
3752                     xp = n->p.other->origin[Geom::X] - n->origin[Geom::X];
3753             }
3754         }
3755         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3756         if (yp < 0) { xp = -xp; yp = -yp; }
3758         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3759             // sliding on handles, only if at least one of the handles is non-vertical
3760             // (otherwise it's the same as ctrl+drag anyway)
3762             // calculate angles of the handles
3763             if (xn == 0) {
3764                 if (yn == 0) { // no handle, consider it the continuation of the other one
3765                     an = 0;
3766                     collinear = TRUE;
3767                 }
3768                 else an = 0; // vertical; set the angle to horizontal
3769             } else an = yn/xn;
3771             if (xp == 0) {
3772                 if (yp == 0) { // no handle, consider it the continuation of the other one
3773                     ap = an;
3774                 }
3775                 else ap = 0; // vertical; set the angle to horizontal
3776             } else  ap = yp/xp;
3778             if (collinear) an = ap;
3780             // angles of the perpendiculars; HUGE_VAL means vertical
3781             if (an == 0) na = HUGE_VAL; else na = -1/an;
3782             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3784             // mouse point relative to the node's original pos
3785             pr = p - n->origin;
3787             // distances to the four lines (two handles and two perpendiculars)
3788             d_an = point_line_distance(&pr, an);
3789             d_na = point_line_distance(&pr, na);
3790             d_ap = point_line_distance(&pr, ap);
3791             d_pa = point_line_distance(&pr, pa);
3793             // find out which line is the closest, save its closest point in c
3794             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3795                 point_line_closest(&pr, an, &c);
3796             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3797                 point_line_closest(&pr, ap, &c);
3798             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3799                 point_line_closest(&pr, na, &c);
3800             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3801                 point_line_closest(&pr, pa, &c);
3802             }
3804             // move the node to the closest point
3805             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3806                                             n->origin[Geom::X] + c[Geom::X] - n->pos[Geom::X],
3807                                             n->origin[Geom::Y] + c[Geom::Y] - n->pos[Geom::Y],
3808                                             true);
3810         } else {  // constraining to hor/vert
3812             if (fabs(p[Geom::X] - n->origin[Geom::X]) > fabs(p[Geom::Y] - n->origin[Geom::Y])) { // snap to hor
3813                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3814                                                 p[Geom::X] - n->pos[Geom::X],
3815                                                 n->origin[Geom::Y] - n->pos[Geom::Y],
3816                                                 true,
3817                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::X]));
3818             } else { // snap to vert
3819                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3820                                                 n->origin[Geom::X] - n->pos[Geom::X],
3821                                                 p[Geom::Y] - n->pos[Geom::Y],
3822                                                 true,
3823                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::Y]));
3824             }
3825         }
3826     } else { // move freely
3827         if (n->is_dragging) {
3828             if (state & GDK_MOD1_MASK) { // sculpt
3829                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, p - n->origin);
3830             } else {
3831                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3832                                             p[Geom::X] - n->pos[Geom::X],
3833                                             p[Geom::Y] - n->pos[Geom::Y],
3834                                             (state & GDK_SHIFT_MASK) == 0);
3835             }
3836         }
3837     }
3839     n->subpath->nodepath->desktop->scroll_to_point(p);
3841     return TRUE;
3844 /**
3845  * Node handle clicked callback.
3846  */
3847 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3849    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3851     if (state & GDK_CONTROL_MASK) { // "delete" handle
3852         if (n->p.knot == knot) {
3853             n->p.pos = n->pos;
3854         } else if (n->n.knot == knot) {
3855             n->n.pos = n->pos;
3856         }
3857         sp_node_update_handles(n);
3858         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3859         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3860         sp_nodepath_update_statusbar(nodepath);
3862     } else { // just select or add to selection, depending in Shift
3863         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3864     }
3867 /**
3868  * Node handle grabbed callback.
3869  */
3870 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3872    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3874     // convert auto -> smooth when dragging handle
3875    if (n->type == Inkscape::NodePath::NODE_AUTO) {
3876         n->type = Inkscape::NodePath::NODE_SMOOTH;
3877         sp_nodepath_update_node_knot (n);
3878    }
3880     if (!n->selected) {
3881         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3882     }
3884     // remember the origin point of the handle
3885     if (n->p.knot == knot) {
3886         n->p.origin_radial = n->p.pos - n->pos;
3887     } else if (n->n.knot == knot) {
3888         n->n.origin_radial = n->n.pos - n->pos;
3889     } else {
3890         g_assert_not_reached();
3891     }
3893     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3896 /**
3897  * Node handle ungrabbed callback.
3898  */
3899 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3901    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3903     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3904     if (n->p.knot == knot) {
3905         n->p.origin_radial.a = 0;
3906         sp_knot_set_position(knot, n->p.pos, state);
3907     } else if (n->n.knot == knot) {
3908         n->n.origin_radial.a = 0;
3909         sp_knot_set_position(knot, n->n.pos, state);
3910     } else {
3911         g_assert_not_reached();
3912     }
3914     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3917 /**
3918  * Node handle "request" signal callback.
3919  */
3920 static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, gpointer data)
3922     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3924     Inkscape::NodePath::NodeSide *me, *opposite;
3925     gint which;
3926     if (n->p.knot == knot) {
3927         me = &n->p;
3928         opposite = &n->n;
3929         which = -1;
3930     } else if (n->n.knot == knot) {
3931         me = &n->n;
3932         opposite = &n->p;
3933         which = 1;
3934     } else {
3935         me = opposite = NULL;
3936         which = 0;
3937         g_assert_not_reached();
3938     }
3940     SPDesktop *desktop = n->subpath->nodepath->desktop;
3941     SnapManager &m = desktop->namedview->snap_manager;
3942     m.setup(desktop, true, n->subpath->nodepath->item);
3943     Inkscape::SnappedPoint s;
3945     if ((state & GDK_SHIFT_MASK) != 0) {
3946         // We will not try to snap when the shift-key is pressed
3947         // so remove the old snap indicator and don't wait for it to time-out
3948         desktop->snapindicator->remove_snaptarget();
3949     }
3951     Inkscape::NodePath::Node *othernode = opposite->other;
3952     Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP);
3953     if (othernode) {
3954         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3955             /* We are smooth node adjacent with line */
3956             Geom::Point const delta = p - n->pos;
3957             Geom::Coord const len = Geom::L2(delta);
3958             Inkscape::NodePath::Node *othernode = opposite->other;
3959             Geom::Point const ndelta = n->pos - othernode->pos;
3960             Geom::Coord const linelen = Geom::L2(ndelta);
3961             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3962                 Geom::Coord const scal = dot(delta, ndelta) / linelen;
3963                 p = n->pos + (scal / linelen) * ndelta;
3964             }
3965             if ((state & GDK_SHIFT_MASK) == 0) {
3966                 s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type, Inkscape::Snapper::ConstraintLine(p, ndelta), false);
3967             }
3968         } else {
3969             if ((state & GDK_SHIFT_MASK) == 0) {
3970                 s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type);
3971             }
3972         }
3973     } else {
3974         if ((state & GDK_SHIFT_MASK) == 0) {
3975             s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type);
3976         }
3977     }
3979     s.getPoint(p);
3981     sp_node_adjust_handle(n, -which);
3983     return FALSE;
3986 /**
3987  * Node handle moved callback.
3988  */
3989 static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data)
3991    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3992    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3994    Inkscape::NodePath::NodeSide *me;
3995    Inkscape::NodePath::NodeSide *other;
3996     if (n->p.knot == knot) {
3997         me = &n->p;
3998         other = &n->n;
3999     } else if (n->n.knot == knot) {
4000         me = &n->n;
4001         other = &n->p;
4002     } else {
4003         me = NULL;
4004         other = NULL;
4005         g_assert_not_reached();
4006     }
4008     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
4009     Radial rme(me->pos - n->pos);
4010     Radial rother(other->pos - n->pos);
4011     Radial rnew(p - n->pos);
4013     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
4014         int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
4015         /* 0 interpreted as "no snapping". */
4017         // 1. Snap to the closest PI/snaps angle, starting from zero.
4018         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
4020         // 2. Snap to the original angle, its opposite and perpendiculars
4021         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
4022             /* The closest PI/2 angle, starting from original angle */
4023             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
4025             // Snap to the closest.
4026             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
4027                        ? a_snapped
4028                        : a_ortho );
4029         }
4031         // 3. Snap to the angle of the opposite line, if any
4032         Inkscape::NodePath::Node *othernode = other->other;
4033         if (othernode) {
4034             Geom::Point other_to_snap(0,0);
4035             if (sp_node_side_is_line(n, other)) {
4036                 other_to_snap = othernode->pos - n->pos;
4037             } else {
4038                 other_to_snap = other->pos - n->pos;
4039             }
4040             if (Geom::L2(other_to_snap) > 1e-3) {
4041                 Radial rother_to_snap(other_to_snap);
4042                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
4043                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
4045                 // Snap to the closest.
4046                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
4047                        ? a_snapped
4048                        : a_oppo );
4049             }
4050         }
4052         rnew.a = a_snapped;
4053     }
4055     if (state & GDK_MOD1_MASK) {
4056         // lock handle length
4057         rnew.r = me->origin_radial.r;
4058     }
4060     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (!n->dragging_out && (state & GDK_SHIFT_MASK)))
4061         && (rme.a != HUGE_VAL) && (rnew.a != HUGE_VAL) && ((fabs(rme.a - rnew.a) > 0.001) || (n->type ==Inkscape::NodePath::NODE_SYMM))) {
4062         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
4063         rother.a += rnew.a - rme.a;
4064         other->pos = Geom::Point(rother) + n->pos;
4065         if (other->knot) {
4066             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
4067             sp_knot_moveto(other->knot, other->pos);
4068         }
4069     }
4071     me->pos = Geom::Point(rnew) + n->pos;
4072     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
4074     // move knot, but without emitting the signal:
4075     // we cannot emit a "moved" signal because we're now processing it
4076     sp_knot_moveto(me->knot, me->pos);
4078     update_object(n->subpath->nodepath);
4080     /* status text */
4081     SPDesktop *desktop = n->subpath->nodepath->desktop;
4082     if (!desktop) return;
4083     SPEventContext *ec = desktop->event_context;
4084     if (!ec) return;
4086     Inkscape::MessageContext *mc = get_message_context(ec);
4088     if (!mc) return;
4090     double degrees = 180 / M_PI * rnew.a;
4091     if (degrees > 180) degrees -= 360;
4092     if (degrees < -180) degrees += 360;
4093     if (prefs->getBool("/options/compassangledisplay/value"))
4094         degrees = angle_to_compass (degrees);
4096     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
4098     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
4099          _("<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);
4101     g_string_free(length, TRUE);
4104 /**
4105  * Node handle event callback.
4106  */
4107 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
4109     gboolean ret = FALSE;
4110     switch (event->type) {
4111         case GDK_KEY_PRESS:
4112             switch (get_group0_keyval (&event->key)) {
4113                 case GDK_space:
4114                     if (event->key.state & GDK_BUTTON1_MASK) {
4115                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
4116                         stamp_repr(nodepath);
4117                         ret = TRUE;
4118                     }
4119                     break;
4120                 default:
4121                     break;
4122             }
4123             break;
4124         case GDK_ENTER_NOTIFY:
4125             // we use an experimentally determined threshold that seems to work fine
4126             if (Geom::L2(n->pos - knot->pos) < 0.75)
4127                 Inkscape::NodePath::Path::active_node = n;
4128             break;
4129         case GDK_LEAVE_NOTIFY:
4130             // we use an experimentally determined threshold that seems to work fine
4131             if (Geom::L2(n->pos - knot->pos) < 0.75)
4132                 Inkscape::NodePath::Path::active_node = NULL;
4133             break;
4134         default:
4135             break;
4136     }
4138     return ret;
4141 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
4142                                  Radial &rme, Radial &rother, gboolean const both)
4144     rme.a += angle;
4145     if ( both
4146          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4147          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4148     {
4149         rother.a += angle;
4150     }
4153 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
4154                                         Radial &rme, Radial &rother, gboolean const both)
4156     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
4158     gdouble r;
4159     if ( both
4160          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4161          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4162     {
4163         r = MAX(rme.r, rother.r);
4164     } else {
4165         r = rme.r;
4166     }
4168     gdouble const weird_angle = atan2(norm_angle, r);
4169 /* Bulia says norm_angle is just the visible distance that the
4170  * object's end must travel on the screen.  Left as 'angle' for want of
4171  * a better name.*/
4173     rme.a += weird_angle;
4174     if ( both
4175          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4176          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4177     {
4178         rother.a += weird_angle;
4179     }
4182 /**
4183  * Rotate one node.
4184  */
4185 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4187     Inkscape::NodePath::NodeSide *me, *other;
4188     bool both = false;
4190     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4191     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4193     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4194         me = &(n->p);
4195         other = &(n->n);
4196     } else if (!n->p.other) {
4197         me = &(n->n);
4198         other = &(n->p);
4199     } else {
4200         if (which > 0) { // right handle
4201             if (xn > xp) {
4202                 me = &(n->n);
4203                 other = &(n->p);
4204             } else {
4205                 me = &(n->p);
4206                 other = &(n->n);
4207             }
4208         } else if (which < 0){ // left handle
4209             if (xn <= xp) {
4210                 me = &(n->n);
4211                 other = &(n->p);
4212             } else {
4213                 me = &(n->p);
4214                 other = &(n->n);
4215             }
4216         } else { // both handles
4217             me = &(n->n);
4218             other = &(n->p);
4219             both = true;
4220         }
4221     }
4223     Radial rme(me->pos - n->pos);
4224     Radial rother(other->pos - n->pos);
4226     if (screen) {
4227         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4228     } else {
4229         node_rotate_one_internal (*n, angle, rme, rother, both);
4230     }
4232     me->pos = n->pos + Geom::Point(rme);
4234     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4235         other->pos =  n->pos + Geom::Point(rother);
4236     }
4238     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4239     // so here we just move all the knots without emitting move signals, for speed
4240     sp_node_update_handles(n, false);
4243 /**
4244  * Rotate selected nodes.
4245  */
4246 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4248     if (!nodepath || !nodepath->selected) return;
4250     if (g_list_length(nodepath->selected) == 1) {
4251        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4252         node_rotate_one (n, angle, which, screen);
4253     } else {
4254        // rotate as an object:
4256         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4257         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4258         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4259             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4260             box.expandTo (n->pos); // contain all selected nodes
4261         }
4263         gdouble rot;
4264         if (screen) {
4265             gdouble const zoom = nodepath->desktop->current_zoom();
4266             gdouble const zmove = angle / zoom;
4267             gdouble const r = Geom::L2(box.max() - box.midpoint());
4268             rot = atan2(zmove, r);
4269         } else {
4270             rot = angle;
4271         }
4273         Geom::Point rot_center;
4274         if (Inkscape::NodePath::Path::active_node == NULL)
4275             rot_center = box.midpoint();
4276         else
4277             rot_center = Inkscape::NodePath::Path::active_node->pos;
4279         Geom::Matrix t =
4280             Geom::Matrix (Geom::Translate(-rot_center)) *
4281             Geom::Matrix (Geom::Rotate(rot)) *
4282             Geom::Matrix (Geom::Translate(rot_center));
4284         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4285             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4286             n->pos *= t;
4287             n->n.pos *= t;
4288             n->p.pos *= t;
4289             sp_node_update_handles(n, false);
4290         }
4291     }
4293     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4296 /**
4297  * Scale one node.
4298  */
4299 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4301     bool both = false;
4302     Inkscape::NodePath::NodeSide *me, *other;
4304     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4305     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4307     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4308         me = &(n->p);
4309         other = &(n->n);
4310         n->code = NR_CURVETO;
4311     } else if (!n->p.other) {
4312         me = &(n->n);
4313         other = &(n->p);
4314         if (n->n.other)
4315             n->n.other->code = NR_CURVETO;
4316     } else {
4317         if (which > 0) { // right handle
4318             if (xn > xp) {
4319                 me = &(n->n);
4320                 other = &(n->p);
4321                 if (n->n.other)
4322                     n->n.other->code = NR_CURVETO;
4323             } else {
4324                 me = &(n->p);
4325                 other = &(n->n);
4326                 n->code = NR_CURVETO;
4327             }
4328         } else if (which < 0){ // left handle
4329             if (xn <= xp) {
4330                 me = &(n->n);
4331                 other = &(n->p);
4332                 if (n->n.other)
4333                     n->n.other->code = NR_CURVETO;
4334             } else {
4335                 me = &(n->p);
4336                 other = &(n->n);
4337                 n->code = NR_CURVETO;
4338             }
4339         } else { // both handles
4340             me = &(n->n);
4341             other = &(n->p);
4342             both = true;
4343             n->code = NR_CURVETO;
4344             if (n->n.other)
4345                 n->n.other->code = NR_CURVETO;
4346         }
4347     }
4349     Radial rme(me->pos - n->pos);
4350     Radial rother(other->pos - n->pos);
4352     rme.r += grow;
4353     if (rme.r < 0) rme.r = 0;
4354     if (rme.a == HUGE_VAL) {
4355         if (me->other) { // if direction is unknown, initialize it towards the next node
4356             Radial rme_next(me->other->pos - n->pos);
4357             rme.a = rme_next.a;
4358         } else { // if there's no next, initialize to 0
4359             rme.a = 0;
4360         }
4361     }
4362     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4363         rother.r += grow;
4364         if (rother.r < 0) rother.r = 0;
4365         if (rother.a == HUGE_VAL) {
4366             rother.a = rme.a + M_PI;
4367         }
4368     }
4370     me->pos = n->pos + Geom::Point(rme);
4372     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4373         other->pos = n->pos + Geom::Point(rother);
4374     }
4376     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4377     // so here we just move all the knots without emitting move signals, for speed
4378     sp_node_update_handles(n, false);
4381 /**
4382  * Scale selected nodes.
4383  */
4384 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4386     if (!nodepath || !nodepath->selected) return;
4388     if (g_list_length(nodepath->selected) == 1) {
4389         // scale handles of the single selected node
4390         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4391         node_scale_one (n, grow, which);
4392     } else {
4393         // scale nodes as an "object":
4395         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4396         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4397         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4398             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4399             box.expandTo (n->pos); // contain all selected nodes
4400         }
4402         if ( Geom::are_near(box.maxExtent(), 0) ) {
4403             SPEventContext *ec = nodepath->desktop->event_context;
4404             if (!ec) return;
4405             Inkscape::MessageContext *mc = get_message_context(ec);
4406             if (!mc) return;
4407             mc->setF(Inkscape::WARNING_MESSAGE,
4408                              _("Cannot scale nodes when all are at the same location."));
4409             return;
4410         }
4411         double scale = (box.maxExtent() + grow)/box.maxExtent();
4414         Geom::Point scale_center;
4415         if (Inkscape::NodePath::Path::active_node == NULL)
4416             scale_center = box.midpoint();
4417         else
4418             scale_center = Inkscape::NodePath::Path::active_node->pos;
4420         Geom::Matrix t =
4421             Geom::Translate(-scale_center) *
4422             Geom::Scale(scale, scale) *
4423             Geom::Translate(scale_center);
4425         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4426             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4427             n->pos *= t;
4428             n->n.pos *= t;
4429             n->p.pos *= t;
4430             sp_node_update_handles(n, false);
4431         }
4432     }
4434     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4437 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4439     if (!nodepath) return;
4440     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4443 /**
4444  * Flip selected nodes horizontally/vertically.
4445  */
4446 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis, boost::optional<Geom::Point> center)
4448     if (!nodepath || !nodepath->selected) return;
4450     if (g_list_length(nodepath->selected) == 1 && !center) {
4451         // flip handles of the single selected node
4452         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4453         double temp = n->p.pos[axis];
4454         n->p.pos[axis] = n->n.pos[axis];
4455         n->n.pos[axis] = temp;
4456         sp_node_update_handles(n, false);
4457     } else {
4458         // scale nodes as an "object":
4460         Geom::Rect box = sp_node_selected_bbox (nodepath);
4461         if (!center) {
4462             center = box.midpoint();
4463         }
4464         Geom::Matrix t =
4465             Geom::Matrix (Geom::Translate(- *center)) *
4466             Geom::Matrix ((axis == Geom::X)? Geom::Scale(-1, 1) : Geom::Scale(1, -1)) *
4467             Geom::Matrix (Geom::Translate(*center));
4469         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4470             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4471             n->pos *= t;
4472             n->n.pos *= t;
4473             n->p.pos *= t;
4474             sp_node_update_handles(n, false);
4475         }
4476     }
4478     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4481 Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4483     g_assert (nodepath->selected);
4485     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4486     Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4487     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4488         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4489         box.expandTo (n->pos); // contain all selected nodes
4490     }
4491     return box;
4494 //-----------------------------------------------
4495 /**
4496  * Return new subpath under given nodepath.
4497  */
4498 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4500     g_assert(nodepath);
4501     g_assert(nodepath->desktop);
4503    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4505     s->nodepath = nodepath;
4506     s->closed = FALSE;
4507     s->nodes = NULL;
4508     s->first = NULL;
4509     s->last = NULL;
4511     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4512     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4513     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4515     return s;
4518 /**
4519  * Destroy nodes in subpath, then subpath itself.
4520  */
4521 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4523     g_assert(subpath);
4524     g_assert(subpath->nodepath);
4525     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4527     while (subpath->nodes) {
4528         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4529     }
4531     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4533     g_free(subpath);
4536 /**
4537  * Link head to tail in subpath.
4538  */
4539 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4541     g_assert(!sp->closed);
4542     g_assert(sp->last != sp->first);
4543     g_assert(sp->first->code == NR_MOVETO);
4545     sp->closed = TRUE;
4547     //Link the head to the tail
4548     sp->first->p.other = sp->last;
4549     sp->last->n.other  = sp->first;
4550     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4551     sp->first          = sp->last;
4553     //Remove the extra end node
4554     sp_nodepath_node_destroy(sp->last->n.other);
4557 /**
4558  * Open closed (loopy) subpath at node.
4559  */
4560 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4562     g_assert(sp->closed);
4563     g_assert(n->subpath == sp);
4564     g_assert(sp->first == sp->last);
4566     /* We create new startpoint, current node will become last one */
4568    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4569                                                 &n->pos, &n->pos, &n->n.pos);
4572     sp->closed        = FALSE;
4574     //Unlink to make a head and tail
4575     sp->first         = new_path;
4576     sp->last          = n;
4577     n->n.other        = NULL;
4578     new_path->p.other = NULL;
4581 /**
4582  * Return new node in subpath with given properties.
4583  * \param pos Position of node.
4584  * \param ppos Handle position in previous direction
4585  * \param npos Handle position in previous direction
4586  */
4587 Inkscape::NodePath::Node *
4588 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)
4590     g_assert(sp);
4591     g_assert(sp->nodepath);
4592     g_assert(sp->nodepath->desktop);
4594     if (nodechunk == NULL)
4595         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4597     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4599     n->subpath  = sp;
4601     if (type != Inkscape::NodePath::NODE_NONE) {
4602         // use the type from sodipodi:nodetypes
4603         n->type = type;
4604     } else {
4605         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4606             // points are (almost) collinear
4607             if (Geom::L2(*pos - *ppos) < 1e-6 || Geom::L2(*pos - *npos) < 1e-6) {
4608                 // endnode, or a node with a retracted handle
4609                 n->type = Inkscape::NodePath::NODE_CUSP;
4610             } else {
4611                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4612             }
4613         } else {
4614             n->type = Inkscape::NodePath::NODE_CUSP;
4615         }
4616     }
4618     n->code     = code;
4619     n->selected = FALSE;
4620     n->pos      = *pos;
4621     n->p.pos    = *ppos;
4622     n->n.pos    = *npos;
4624     n->dragging_out = NULL;
4626     Inkscape::NodePath::Node *prev;
4627     if (next) {
4628         //g_assert(g_list_find(sp->nodes, next));
4629         prev = next->p.other;
4630     } else {
4631         prev = sp->last;
4632     }
4634     if (prev)
4635         prev->n.other = n;
4636     else
4637         sp->first = n;
4639     if (next)
4640         next->p.other = n;
4641     else
4642         sp->last = n;
4644     n->p.other = prev;
4645     n->n.other = next;
4647     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"));
4648     sp_knot_set_position(n->knot, *pos, 0);
4650     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4651     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4652     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4654     sp_nodepath_update_node_knot(n);
4656     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4657     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4658     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4659     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4660     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4661     sp_knot_show(n->knot);
4663     // We only create handle knots and lines on demand
4664     n->p.knot = NULL;
4665     n->p.line = NULL;
4666     n->n.knot = NULL;
4667     n->n.line = NULL;
4669     sp->nodes = g_list_prepend(sp->nodes, n);
4671     return n;
4674 /**
4675  * Destroy node and its knots, link neighbors in subpath.
4676  */
4677 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4679     g_assert(node);
4680     g_assert(node->subpath);
4681     g_assert(SP_IS_KNOT(node->knot));
4683    Inkscape::NodePath::SubPath *sp = node->subpath;
4685     if (node->selected) { // first, deselect
4686         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4687         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4688     }
4690     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4692     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4693     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4694     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4695     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4696     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4697     g_object_unref(G_OBJECT(node->knot));
4699     if (node->p.knot) {
4700         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4701         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4702         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4703         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4704         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4705         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4706         g_object_unref(G_OBJECT(node->p.knot));
4707         node->p.knot = NULL;
4708     }
4710     if (node->n.knot) {
4711         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4712         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4713         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4714         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4715         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4716         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4717         g_object_unref(G_OBJECT(node->n.knot));
4718         node->n.knot = NULL;
4719     }
4721     if (node->p.line)
4722         gtk_object_destroy(GTK_OBJECT(node->p.line));
4723     if (node->n.line)
4724         gtk_object_destroy(GTK_OBJECT(node->n.line));
4726     if (sp->nodes) { // there are others nodes on the subpath
4727         if (sp->closed) {
4728             if (sp->first == node) {
4729                 g_assert(sp->last == node);
4730                 sp->first = node->n.other;
4731                 sp->last = sp->first;
4732             }
4733             node->p.other->n.other = node->n.other;
4734             node->n.other->p.other = node->p.other;
4735         } else {
4736             if (sp->first == node) {
4737                 sp->first = node->n.other;
4738                 sp->first->code = NR_MOVETO;
4739             }
4740             if (sp->last == node) sp->last = node->p.other;
4741             if (node->p.other) node->p.other->n.other = node->n.other;
4742             if (node->n.other) node->n.other->p.other = node->p.other;
4743         }
4744     } else { // this was the last node on subpath
4745         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4746     }
4748     g_mem_chunk_free(nodechunk, node);
4751 /**
4752  * Returns one of the node's two sides.
4753  * \param which Indicates which side.
4754  * \return Pointer to previous node side if which==-1, next if which==1.
4755  */
4756 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4758     g_assert(node);
4759     Inkscape::NodePath::NodeSide * result = 0;
4760     switch (which) {
4761         case -1:
4762             result = &node->p;
4763             break;
4764         case 1:
4765             result = &node->n;
4766             break;
4767         default:
4768             g_assert_not_reached();
4769     }
4771     return result;
4774 /**
4775  * Return the other side of the node, given one of its sides.
4776  */
4777 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4779     g_assert(node);
4780     Inkscape::NodePath::NodeSide *result = 0;
4782     if (me == &node->p) {
4783         result = &node->n;
4784     } else if (me == &node->n) {
4785         result = &node->p;
4786     } else {
4787         g_assert_not_reached();
4788     }
4790     return result;
4793 /**
4794  * Return NRPathcode on the given side of the node.
4795  */
4796 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4798     g_assert(node);
4800     NRPathcode result = NR_END;
4801     if (me == &node->p) {
4802         if (node->p.other) {
4803             result = (NRPathcode)node->code;
4804         } else {
4805             result = NR_MOVETO;
4806         }
4807     } else if (me == &node->n) {
4808         if (node->n.other) {
4809             result = (NRPathcode)node->n.other->code;
4810         } else {
4811             result = NR_MOVETO;
4812         }
4813     } else {
4814         g_assert_not_reached();
4815     }
4817     return result;
4820 /**
4821  * Return node with the given index
4822  */
4823 Inkscape::NodePath::Node *
4824 sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index)
4826     Inkscape::NodePath::Node *e = NULL;
4828     if (!nodepath) {
4829         return e;
4830     }
4832     //find segment
4833     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4835         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4836         int n = g_list_length(sp->nodes);
4837         if (sp->closed) {
4838             n++;
4839         }
4841         //if the piece belongs to this subpath grab it
4842         //otherwise move onto the next subpath
4843         if (index < n) {
4844             e = sp->first;
4845             for (int i = 0; i < index; ++i) {
4846                 e = e->n.other;
4847             }
4848             break;
4849         } else {
4850             if (sp->closed) {
4851                 index -= (n+1);
4852             } else {
4853                 index -= n;
4854             }
4855         }
4856     }
4858     return e;
4861 /**
4862  * Returns plain text meaning of node type.
4863  */
4864 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4866     unsigned retracted = 0;
4867     bool endnode = false;
4869     for (int which = -1; which <= 1; which += 2) {
4870         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4871         if (side->other && Geom::L2(side->pos - node->pos) < 1e-6)
4872             retracted ++;
4873         if (!side->other)
4874             endnode = true;
4875     }
4877     if (retracted == 0) {
4878         if (endnode) {
4879                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4880                 return _("end node");
4881         } else {
4882             switch (node->type) {
4883                 case Inkscape::NodePath::NODE_CUSP:
4884                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4885                     return _("cusp");
4886                 case Inkscape::NodePath::NODE_SMOOTH:
4887                     // TRANSLATORS: "smooth" is an adjective here
4888                     return _("smooth");
4889                 case Inkscape::NodePath::NODE_AUTO:
4890                     return _("auto");
4891                 case Inkscape::NodePath::NODE_SYMM:
4892                     return _("symmetric");
4893             }
4894         }
4895     } else if (retracted == 1) {
4896         if (endnode) {
4897             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4898             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4899         } else {
4900             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4901         }
4902     } else {
4903         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4904     }
4906     return NULL;
4909 /**
4910  * Handles content of statusbar as long as node tool is active.
4911  */
4912 void
4913 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4915     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");
4916     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4918     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4919     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4920     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4921     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4923     SPDesktop *desktop = NULL;
4924     if (nodepath) {
4925         desktop = nodepath->desktop;
4926     } else {
4927         desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above
4928     }
4930     SPEventContext *ec = desktop->event_context;
4931     if (!ec) return;
4933     Inkscape::MessageContext *mc = get_message_context(ec);
4934     if (!mc) return;
4936     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4938     if (selected_nodes == 0) {
4939         Inkscape::Selection *sel = desktop->selection;
4940         if (!sel || sel->isEmpty()) {
4941             mc->setF(Inkscape::NORMAL_MESSAGE,
4942                      _("Select a single object to edit its nodes or handles."));
4943         } else {
4944             if (nodepath) {
4945             mc->setF(Inkscape::NORMAL_MESSAGE,
4946                      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.",
4947                               "<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.",
4948                               total_nodes),
4949                      total_nodes);
4950             } else {
4951                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4952                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4953                 } else {
4954                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4955                 }
4956             }
4957         }
4958     } else if (nodepath && selected_nodes == 1) {
4959         mc->setF(Inkscape::NORMAL_MESSAGE,
4960                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4961                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4962                           total_nodes),
4963                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4964     } else {
4965         if (selected_subpaths > 1) {
4966             mc->setF(Inkscape::NORMAL_MESSAGE,
4967                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4968                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4969                               total_nodes),
4970                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4971         } else {
4972             mc->setF(Inkscape::NORMAL_MESSAGE,
4973                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4974                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4975                               total_nodes),
4976                      selected_nodes, total_nodes, when_selected);
4977         }
4978     }
4981 /*
4982  * returns a *copy* of the curve of that object.
4983  */
4984 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4985     if (!object)
4986         return NULL;
4988     SPCurve *curve = NULL;
4989     if (SP_IS_PATH(object)) {
4990         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4991         curve = curve_new->copy();
4992     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4993         const gchar *svgd = object->repr->attribute(key);
4994         if (svgd) {
4995             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4996             SPCurve *curve_new = new SPCurve(pv);
4997             if (curve_new) {
4998                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4999             }
5000         }
5001     }
5003     return curve;
5006 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
5007     if (!np || !np->object || !curve)
5008         return;
5010     if (SP_IS_PATH(np->object)) {
5011         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
5012             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
5013         } else {
5014             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
5015         }
5016     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
5017         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(np->object)->get_lpe();
5018         if (lpe) {
5019             Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( lpe->getParameter(np->repr_key) );
5020             if (pathparam) {
5021                 pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
5022                 np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
5023             }
5024         }
5025     }
5028 /*
5029 SPCanvasItem *
5030 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
5031     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
5033 */
5036 /// \todo this code to generate a helper canvasitem from an spcurve should be moved to different file
5037 SPCanvasItem *
5038 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const Geom::Matrix & i2d, guint32 color = 0xff0000ff) {
5039     SPCurve *flash_curve = curve->copy();
5040     flash_curve->transform(i2d);
5041     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
5042     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
5043     // unless we also flash the nodes...
5044     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
5045     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
5046     sp_canvas_item_show(canvasitem);
5047     flash_curve->unref();
5048     return canvasitem;
5051 SPCanvasItem *
5052 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item) {
5053     if (!item || !desktop) {
5054         return NULL;
5055     }
5057     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5058     guint32 color = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
5060     Geom::Matrix i2d = sp_item_i2d_affine(item);
5062     SPCurve *curve = NULL;
5063     if (SP_IS_PATH(item)) {
5064         curve = sp_path_get_curve_for_edit(SP_PATH(item));
5065     } else if ( SP_IS_SHAPE(item) && SP_SHAPE(item)->curve ) {
5066         curve = sp_shape_get_curve (SP_SHAPE(item));
5067     } else if ( SP_IS_TEXT(item) ) {
5068         // do not display helperpath for text - we cannot do anything with it in Node tool anyway
5069         // curve = SP_TEXT(item)->getNormalizedBpath();
5070         return NULL;
5071     } else {
5072         g_warning ("-----> sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item): TODO: generate the helper path for this item type!\n");
5073         return NULL;
5074     }
5076     SPCanvasItem * helperpath = sp_nodepath_generate_helperpath(desktop, curve, i2d, color);
5078     curve->unref();
5080     return helperpath;
5084 // TODO: Merge this with sp_nodepath_make_helper_item()!
5085 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
5086     np->show_helperpath = show;
5088     if (show) {
5089         SPCurve *helper_curve = np->curve->copy();
5090         helper_curve->transform(np->i2d);
5091         if (!np->helper_path) {
5092             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
5094             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
5095             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);
5096             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
5097             sp_canvas_item_move_to_z(np->helper_path, 0);
5098             sp_canvas_item_show(np->helper_path);
5099         } else {
5100             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
5101         }
5102         helper_curve->unref();
5103     } else {
5104         if (np->helper_path) {
5105             GtkObject *temp = np->helper_path;
5106             np->helper_path = NULL;
5107             gtk_object_destroy(temp);
5108         }
5109     }
5112 /* sp_nodepath_make_straight_path:
5113  *   Prevents user from curving the path by dragging a segment or activating handles etc.
5114  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
5115  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
5116  */
5117 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
5118     np->straight_path = true;
5119     np->show_handles = false;
5120     g_message("add code to make the path straight.");
5121     // do sp_nodepath_convert_node_type on all nodes?
5122     // coding tip: search for this text : "Make selected segments lines"
5125 /*
5126   Local Variables:
5127   mode:c++
5128   c-file-style:"stroustrup"
5129   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5130   indent-tabs-mode:nil
5131   fill-column:99
5132   End:
5133 */
5134 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :