Code

Fix #338838, in which zooming and panning with the middle mouse button made Inkscape...
[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) {
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), np->helperpath_rgba, np->helperpath_width, 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 SPCanvasItem *
179 canvasitem_from_pathvec(Inkscape::NodePath::Path *np, Geom::PathVector const &pathv, bool show) {
180     SPCurve *helper_curve = new SPCurve(pathv);
181     return sp_nodepath_make_helper_item(np, helper_curve, show);
184 static void
185 sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) {
186     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> > helper_path_vec;
187     if (!SP_IS_LPE_ITEM(np->item)) {
188         g_print ("Only LPEItems can have helperpaths!\n");
189         return;
190     }
192     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
193     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
194     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
195         Inkscape::LivePathEffect::LPEObjectReference *lperef = (*i);
196         Inkscape::LivePathEffect::Effect *lpe = lperef->lpeobject->get_lpe();
197         if (lpe) {
198             // create new canvas items from the effect's helper paths
199             std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
200             for (std::vector<Geom::PathVector>::iterator j = hpaths.begin(); j != hpaths.end(); ++j) {
201                 np->helper_path_vec[lpe].push_back(canvasitem_from_pathvec(np, *j, true));
202             }
203         }
204     }
207 void
208 sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np) {
209     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> > helper_path_vec;
210     if (!SP_IS_LPE_ITEM(np->item)) {
211         g_print ("Only LPEItems can have helperpaths!\n");
212         return;
213     }
215     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
216     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
217     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
218         Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->get_lpe();
219         if (lpe) {
220             /* update canvas items from the effect's helper paths; note that this code relies on the
221              * fact that getHelperPaths() will always return the same number of helperpaths in the same
222              * order as during their creation in sp_nodepath_create_helperpaths
223              */
224             std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
225             for (unsigned int j = 0; j < hpaths.size(); ++j) {
226                 SPCurve *curve = new SPCurve(hpaths[j]);
227                 curve->transform(np->i2d);
228                 sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH((np->helper_path_vec[lpe])[j]), curve);
229                 curve = curve->unref();
230             }
231         }
232     }
235 static void
236 sp_nodepath_destroy_helperpaths(Inkscape::NodePath::Path *np) {
237     for (HelperPathList::iterator i = np->helper_path_vec.begin(); i != np->helper_path_vec.end(); ++i) {
238         for (std::vector<SPCanvasItem *>::iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) {
239             GtkObject *temp = *j;
240             *j = NULL;
241             gtk_object_destroy(temp);
242         }
243     }
244     np->helper_path_vec.clear();
248 /**
249  * \brief Creates new nodepath from item
250  *
251  * \todo create proper constructor for nodepath::path, this method returns null a constructor cannot so this cannot be simply converted to constructor.
252  */
253 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
255     Inkscape::XML::Node *repr = object->repr;
257     /** \todo
258      * FIXME: remove this. We don't want to edit paths inside flowtext.
259      * Instead we will build our flowtext with cloned paths, so that the
260      * real paths are outside the flowtext and thus editable as usual.
261      */
262     if (SP_IS_FLOWTEXT(object)) {
263         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
264             if SP_IS_FLOWREGION(child) {
265                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
266                 if (grandchild && SP_IS_PATH(grandchild)) {
267                     object = SP_ITEM(grandchild);
268                     break;
269                 }
270             }
271         }
272     }
274     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
276     if (curve == NULL) {
277         return NULL;
278     }
280     if (curve->get_segment_count() < 1) {
281         curve->unref();
282         return NULL; // prevent crash for one-node paths
283     }
285     //Create new nodepath
286     Inkscape::NodePath::Path *np = new Inkscape::NodePath::Path();
287     if (!np) {
288         curve->unref();
289         return NULL;
290     }
292     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
294     // Set defaults
295     np->desktop     = desktop;
296     np->object      = object;
297     np->subpaths    = NULL;
298     np->selected    = NULL;
299     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
300     np->local_change = 0;
301     np->show_handles = show_handles;
302     np->helper_path = NULL;
303     np->helperpath_rgba = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
304     np->helperpath_width = 1.0;
305     np->curve = curve->copy();
306     np->show_helperpath = prefs->getBool("/tools/nodes/show_helperpath");
307     if (SP_IS_LPE_ITEM(object)) {
308         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
309         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
310             np->show_helperpath = true;
311         }
312     }
313     np->straight_path = false;
314     if (IS_LIVEPATHEFFECT(object) && item) {
315         np->item = item;
316     } else {
317         np->item = SP_ITEM(object);
318     }
320     np->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
322     // we need to update item's transform from the repr here,
323     // because they may be out of sync when we respond
324     // to a change in repr by regenerating nodepath     --bb
325     sp_object_read_attr(SP_OBJECT(np->item), "transform");
327     np->i2d  = sp_item_i2d_affine(np->item);
328     np->d2i  = np->i2d.inverse();
330     np->repr = repr;
331     if (repr_key_in) { // apparently the object is an LPEObject (this is a dirty check, hopefully nobody tries feeding non-lpeobjects into this method with non-null repr_key_in)
332         np->repr_key = g_strdup(repr_key_in);
333         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
334         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(object)->get_lpe();
335         if (!lpe) {
336             g_error("sp_nodepath_new: lpeobject without real lpe passed as argument!");
337             delete np;
338         }
339         Inkscape::LivePathEffect::Parameter *lpeparam = lpe->getParameter(repr_key_in);
340         if (lpeparam) {
341             lpeparam->param_setup_nodepath(np);
342         }
343     } else {
344         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
345         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
346             np->repr_key = g_strdup("inkscape:original-d");
348             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
349             if (lpe) {
350                 lpe->setup_nodepath(np);
351             }
352         } else {
353             np->repr_key = g_strdup("d");
354         }
355     }
357     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
358      * So for example a closed rectangle has a nodetypestring of length 5.
359      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
360     Geom::PathVector pathv_sanitized = pathv_to_linear_and_cubic_beziers(np->curve->get_pathvector());
361     np->curve->set_pathvector(pathv_sanitized);
362     guint length = np->curve->get_segment_count();
363     for (Geom::PathVector::const_iterator pit = pathv_sanitized.begin(); pit != pathv_sanitized.end(); ++pit) {
364         length += pit->empty() ? 0 : 1;
365     }
367     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
368     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
370     // create the subpath(s) from the bpath
371     subpaths_from_pathvector(np, pathv_sanitized, typestr);
373     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
374     np->subpaths = g_list_reverse(np->subpaths);
376     delete[] typestr;
377     curve->unref();
379     // Draw helper curve
380     if (np->show_helperpath) {
381         np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true);
382     }
384     sp_nodepath_create_helperpaths(np);
386     return np;
389 /**
390  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
391  */
392 Inkscape::NodePath::Path::~Path() {
393     while (this->subpaths) {
394         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) this->subpaths->data);
395     }
397     //Inform the ShapeEditor that made me, if any, that I am gone.
398     if (this->shape_editor)
399         this->shape_editor->nodepath_destroyed();
401     g_assert(!this->selected);
403     if (this->helper_path) {
404         GtkObject *temp = this->helper_path;
405         this->helper_path = NULL;
406         gtk_object_destroy(temp);
407     }
408     if (this->curve) {
409         this->curve->unref();
410         this->curve = NULL;
411     }
413     if (this->repr_key) {
414         g_free(this->repr_key);
415         this->repr_key = NULL;
416     }
417     if (this->repr_nodetypes_key) {
418         g_free(this->repr_nodetypes_key);
419         this->repr_nodetypes_key = NULL;
420     }
422     sp_nodepath_destroy_helperpaths(this);
424     this->desktop = NULL;
427 /**
428  *  Return the node count of a given NodeSubPath.
429  */
430 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
432     int nodeCount = 0;
434     if (subpath) {
435         nodeCount = g_list_length(subpath->nodes);
436     }
438     return nodeCount;
441 /**
442  *  Return the node count of a given NodePath.
443  */
444 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
446     gint nodeCount = 0;
447     if (np) {
448         for (GList *item = np->subpaths ; item ; item=item->next) {
449             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
450             nodeCount += g_list_length(subpath->nodes);
451         }
452     }
453     return nodeCount;
456 /**
457  *  Return the subpath count of a given NodePath.
458  */
459 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
461     gint nodeCount = 0;
462     if (np) {
463         nodeCount = g_list_length(np->subpaths);
464     }
465     return nodeCount;
468 /**
469  *  Return the selected node count of a given NodePath.
470  */
471 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
473     gint nodeCount = 0;
474     if (np) {
475         nodeCount = g_list_length(np->selected);
476     }
477     return nodeCount;
480 /**
481  *  Return the number of subpaths where nodes are selected in a given NodePath.
482  */
483 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
485     gint nodeCount = 0;
486     if (np && np->selected) {
487         if (!np->selected->next) {
488             nodeCount = 1;
489         } else {
490             for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
491                 Inkscape::NodePath::SubPath *subpath = static_cast<Inkscape::NodePath::SubPath *>(spl->data);
492                 for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
493                     Inkscape::NodePath::Node *node = static_cast<Inkscape::NodePath::Node *>(nl->data);
494                     if (node->selected) {
495                         nodeCount++;
496                         break;
497                     }
498                 }
499             }
500         }
501     }
502     return nodeCount;
505 /**
506  * Clean up a nodepath after editing.
507  *
508  * Currently we are deleting trivial subpaths.
509  */
510 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
512     GList *badSubPaths = NULL;
514     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
515     for (GList *l = nodepath->subpaths; l ; l=l->next) {
516        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
517        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
518             badSubPaths = g_list_append(badSubPaths, sp);
519     }
521     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
522     //also removes the subpath from nodepath->subpaths
523     for (GList *l = badSubPaths; l ; l=l->next) {
524        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
525         sp_nodepath_subpath_destroy(sp);
526     }
528     g_list_free(badSubPaths);
531 /**
532  * Create new nodepaths from pathvector, make it subpaths of np.
533  * \param t The node type array.
534  */
535 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
537     guint i = 0;  // index into node type array
538     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
539         if (pit->empty())
540             continue;  // don't add single knot paths
542         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
544         Geom::Point ppos = pit->initialPoint() * np->i2d;
545         NRPathcode pcode = NR_MOVETO;
547         /* 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)*/
548         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
549             if( dynamic_cast<Geom::LineSegment const*>(&*cit) ||
550                 dynamic_cast<Geom::HLineSegment const*>(&*cit) ||
551                 dynamic_cast<Geom::VLineSegment const*>(&*cit) )
552             {
553                 Geom::Point pos = cit->initialPoint() * (Geom::Matrix)np->i2d;
554                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
556                 ppos = cit->finalPoint() * (Geom::Matrix)np->i2d;
557                 pcode = NR_LINETO;
558             }
559             else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
560                 std::vector<Geom::Point> points = cubic_bezier->points();
561                 Geom::Point pos = points[0] * (Geom::Matrix)np->i2d;
562                 Geom::Point npos = points[1] * (Geom::Matrix)np->i2d;
563                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
565                 ppos = points[2] * (Geom::Matrix)np->i2d;
566                 pcode = NR_CURVETO;
567             }
568         }
570         if (pit->closed()) {
571             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
572             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
573              * If the length is zero, don't add it to the nodepath. */
574             Geom::Curve const &closing_seg = pit->back_closed();
575             // Don't use !closing_seg.isDegenerate() as it is too precise, and does not account for floating point rounding probs (LP bug #257289)
576             if ( ! are_near(closing_seg.initialPoint(), closing_seg.finalPoint()) ) {
577                 Geom::Point pos = closing_seg.finalPoint() * (Geom::Matrix)np->i2d;
578                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
579             }
581             sp_nodepath_subpath_close(sp);
582         }
583     }
586 /**
587  * Convert from sodipodi:nodetypes to new style type array.
588  */
589 static
590 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
592     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
594     guint pos = 0;
596     if (types) {
597         for (guint i = 0; types[i] && ( i < length ); i++) {
598             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
599             if (types[i] != '\0') {
600                 switch (types[i]) {
601                     case 's':
602                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
603                         break;
604                     case 'a':
605                         typestr[pos++] =Inkscape::NodePath::NODE_AUTO;
606                         break;
607                     case 'z':
608                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
609                         break;
610                     case 'c':
611                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
612                         break;
613                     default:
614                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
615                         break;
616                 }
617             }
618         }
619     }
621     while (pos < length) {
622         typestr[pos++] = Inkscape::NodePath::NODE_NONE;
623     }
625     return typestr;
628 /**
629  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
630  * updated but repr is not (for speed). Used during curve and node drag.
631  */
632 static void update_object(Inkscape::NodePath::Path *np)
634     g_assert(np);
636     np->curve->unref();
637     np->curve = create_curve(np);
639     sp_nodepath_set_curve(np, np->curve);
641     if (np->show_helperpath) {
642         SPCurve * helper_curve = np->curve->copy();
643         helper_curve->transform(np->i2d);
644         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
645         helper_curve->unref();
646     }
648     // updating helperpaths of LPEItems is now done in sp_lpe_item_update();
649     //sp_nodepath_update_helperpaths(np);
651     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
652     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
653     np->shape_editor->update_knotholder();
656 /**
657  * Update XML path node with data from path object.
658  */
659 static void update_repr_internal(Inkscape::NodePath::Path *np)
661     g_assert(np);
663     Inkscape::XML::Node *repr = np->object->repr;
665     np->curve->unref();
666     np->curve = create_curve(np);
668     gchar *typestr = create_typestr(np);
669     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
671     // determine if path has an effect applied and write to correct "d" attribute.
672     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
673         np->local_change++;
674         repr->setAttribute(np->repr_key, svgpath);
675     }
677     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
678         np->local_change++;
679         repr->setAttribute(np->repr_nodetypes_key, typestr);
680     }
682     g_free(svgpath);
683     g_free(typestr);
685     if (np->show_helperpath) {
686         SPCurve * helper_curve = np->curve->copy();
687         helper_curve->transform(np->i2d);
688         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
689         helper_curve->unref();
690     }
692     // TODO: do we need this call here? after all, update_object() should have been called just before
693     //sp_nodepath_update_helperpaths(np);
696 /**
697  * Update XML path node with data from path object, commit changes forever.
698  */
699 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
701     //fixme: np can be NULL, so check before proceeding
702     g_return_if_fail(np != NULL);
704     update_repr_internal(np);
705     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
707     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
708                      annotation);
711 /**
712  * Update XML path node with data from path object, commit changes with undo.
713  */
714 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
716     update_repr_internal(np);
717     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
718                            annotation);
721 /**
722  * Make duplicate of path, replace corresponding XML node in tree, commit.
723  */
724 static void stamp_repr(Inkscape::NodePath::Path *np)
726     g_assert(np);
728     Inkscape::XML::Node *old_repr = np->object->repr;
729     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
731     // remember the position of the item
732     gint pos = old_repr->position();
733     // remember parent
734     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
736     SPCurve *curve = create_curve(np);
737     gchar *typestr = create_typestr(np);
739     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
741     new_repr->setAttribute(np->repr_key, svgpath);
742     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
744     // add the new repr to the parent
745     parent->appendChild(new_repr);
746     // move to the saved position
747     new_repr->setPosition(pos > 0 ? pos : 0);
749     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
750                      _("Stamp"));
752     Inkscape::GC::release(new_repr);
753     g_free(svgpath);
754     g_free(typestr);
755     curve->unref();
758 /**
759  * Create curve from path.
760  */
761 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
763     SPCurve *curve = new SPCurve();
765     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
766        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
767        curve->moveto(sp->first->pos * np->d2i);
768        Inkscape::NodePath::Node *n = sp->first->n.other;
769         while (n) {
770             Geom::Point const end_pt = n->pos * np->d2i;
771             if (!IS_FINITE(n->pos[0]) || !IS_FINITE(n->pos[1])){
772                 g_message("niet finite");
773             }
774             switch (n->code) {
775                 case NR_LINETO:
776                     curve->lineto(end_pt);
777                     break;
778                 case NR_CURVETO:
779                     curve->curveto(n->p.other->n.pos * np->d2i,
780                                      n->p.pos * np->d2i,
781                                      end_pt);
782                     break;
783                 default:
784                     g_assert_not_reached();
785                     break;
786             }
787             if (n != sp->last) {
788                 n = n->n.other;
789             } else {
790                 n = NULL;
791             }
792         }
793         if (sp->closed) {
794             curve->closepath();
795         }
796     }
798     return curve;
801 /**
802  * Convert path type string to sodipodi:nodetypes style.
803  */
804 static gchar *create_typestr(Inkscape::NodePath::Path *np)
806     gchar *typestr = g_new(gchar, 32);
807     gint len = 32;
808     gint pos = 0;
810     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
811        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
813         if (pos >= len) {
814             typestr = g_renew(gchar, typestr, len + 32);
815             len += 32;
816         }
818         typestr[pos++] = 'c';
820        Inkscape::NodePath::Node *n;
821         n = sp->first->n.other;
822         while (n) {
823             gchar code;
825             switch (n->type) {
826                 case Inkscape::NodePath::NODE_CUSP:
827                     code = 'c';
828                     break;
829                 case Inkscape::NodePath::NODE_SMOOTH:
830                     code = 's';
831                     break;
832                 case Inkscape::NodePath::NODE_AUTO:
833                     code = 'a';
834                     break;
835                 case Inkscape::NodePath::NODE_SYMM:
836                     code = 'z';
837                     break;
838                 default:
839                     g_assert_not_reached();
840                     code = '\0';
841                     break;
842             }
844             if (pos >= len) {
845                 typestr = g_renew(gchar, typestr, len + 32);
846                 len += 32;
847             }
849             typestr[pos++] = code;
851             if (n != sp->last) {
852                 n = n->n.other;
853             } else {
854                 n = NULL;
855             }
856         }
857     }
859     if (pos >= len) {
860         typestr = g_renew(gchar, typestr, len + 1);
861         len += 1;
862     }
864     typestr[pos++] = '\0';
866     return typestr;
869 // Returns different message contexts depending on the current context. This function should only
870 // be called when ec is either a SPNodeContext or SPLPEToolContext, thus we return NULL in all
871 // other cases.
872 static Inkscape::MessageContext *
873 get_message_context(SPEventContext *ec)
875     Inkscape::MessageContext *mc = 0;
877     if (SP_IS_NODE_CONTEXT(ec)) {
878         mc = SP_NODE_CONTEXT(ec)->_node_message_context;
879     } else if (SP_IS_LPETOOL_CONTEXT(ec)) {
880         mc = SP_LPETOOL_CONTEXT(ec)->_lpetool_message_context;
881     } else {
882         g_warning ("Nodepath should only be present in Node tool or Geometric tool.");
883     }
885     return mc;
888 /**
889  \brief Fills node and handle positions for three nodes, splitting line
890   marked by end at distance t.
891  */
892 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
894     g_assert(new_path != NULL);
895     g_assert(end      != NULL);
897     g_assert(end->p.other == new_path);
898    Inkscape::NodePath::Node *start = new_path->p.other;
899     g_assert(start);
901     if (end->code == NR_LINETO) {
902         new_path->type =Inkscape::NodePath::NODE_CUSP;
903         new_path->code = NR_LINETO;
904         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
905     } else {
906         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
907         new_path->code = NR_CURVETO;
908         gdouble s      = 1 - t;
909         for (int dim = 0; dim < 2; dim++) {
910             Geom::Coord const f000 = start->pos[dim];
911             Geom::Coord const f001 = start->n.pos[dim];
912             Geom::Coord const f011 = end->p.pos[dim];
913             Geom::Coord const f111 = end->pos[dim];
914             Geom::Coord const f00t = s * f000 + t * f001;
915             Geom::Coord const f01t = s * f001 + t * f011;
916             Geom::Coord const f11t = s * f011 + t * f111;
917             Geom::Coord const f0tt = s * f00t + t * f01t;
918             Geom::Coord const f1tt = s * f01t + t * f11t;
919             Geom::Coord const fttt = s * f0tt + t * f1tt;
920             start->n.pos[dim]    = f00t;
921             new_path->p.pos[dim] = f0tt;
922             new_path->pos[dim]   = fttt;
923             new_path->n.pos[dim] = f1tt;
924             end->p.pos[dim]      = f11t;
925         }
926     }
929 /**
930  * Adds new node on direct line between two nodes, activates handles of all
931  * three nodes.
932  */
933 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
935     g_assert(end);
936     g_assert(end->subpath);
937     g_assert(g_list_find(end->subpath->nodes, end));
939    Inkscape::NodePath::Node *start = end->p.other;
940     g_assert( start->n.other == end );
941    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
942                                                end,
943                                                (NRPathcode)end->code == NR_LINETO?
944                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
945                                                (NRPathcode)end->code,
946                                                &start->pos, &start->pos, &start->n.pos);
947     sp_nodepath_line_midpoint(newnode, end, t);
949     sp_node_adjust_handles(start);
950     sp_node_update_handles(start);
951     sp_node_update_handles(newnode);
952     sp_node_adjust_handles(end);
953     sp_node_update_handles(end);
955     return newnode;
958 /**
959 \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
960 */
961 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
963     g_assert(node);
964     g_assert(node->subpath);
965     g_assert(g_list_find(node->subpath->nodes, node));
967     Inkscape::NodePath::Node* result = 0;
968     Inkscape::NodePath::SubPath *sp = node->subpath;
969     Inkscape::NodePath::Path *np    = sp->nodepath;
971     if (sp->closed) {
972         sp_nodepath_subpath_open(sp, node);
973         result = sp->first;
974     } else if ( (node == sp->first) || (node == sp->last ) ){
975         // no break for end nodes
976         result = 0;
977     } else {
978         // create a new subpath
979         Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
981         // duplicate the break node as start of the new subpath
982         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL,
983                                                                  static_cast<Inkscape::NodePath::NodeType>(node->type),
984                                                                  NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
986         // attach rest of curve to new node
987         g_assert(node->n.other);
988         newnode->n.other = node->n.other; node->n.other = NULL;
989         newnode->n.other->p.other = newnode;
990         newsubpath->last = sp->last;
991         sp->last = node;
992         node = newnode;
993         while (node->n.other) {
994             node = node->n.other;
995             node->subpath = newsubpath;
996             sp->nodes = g_list_remove(sp->nodes, node);
997             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
998         }
1001         result = newnode;
1002     }
1003     return result;
1006 /**
1007  * Duplicate node and connect to neighbours.
1008  */
1009 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
1011     g_assert(node);
1012     g_assert(node->subpath);
1013     g_assert(g_list_find(node->subpath->nodes, node));
1015    Inkscape::NodePath::SubPath *sp = node->subpath;
1017     NRPathcode code = (NRPathcode) node->code;
1018     if (code == NR_MOVETO) { // if node is the endnode,
1019         node->code = NR_LINETO; // new one is inserted before it, so change that to line
1020     }
1022     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
1024     if (!node->n.other || !node->p.other) { // if node is an endnode, select it
1025         return node;
1026     } else {
1027         return newnode; // otherwise select the newly created node
1028     }
1031 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
1033     node->p.pos = (node->pos + (node->pos - node->n.pos));
1036 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
1038     node->n.pos = (node->pos + (node->pos - node->p.pos));
1041 /**
1042  * Change line type at node, with side effects on neighbours.
1043  */
1044 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
1046     g_assert(end);
1047     g_assert(end->subpath);
1048     g_assert(end->p.other);
1050     if (end->code != static_cast<guint>(code) ) {
1051         Inkscape::NodePath::Node *start = end->p.other;
1053         end->code = code;
1055         if (code == NR_LINETO) {
1056             if (start->code == NR_LINETO) {
1057                 sp_nodepath_set_node_type(start, Inkscape::NodePath::NODE_CUSP);
1058             }
1059             if (end->n.other) {
1060                 if (end->n.other->code == NR_LINETO) {
1061                     sp_nodepath_set_node_type(end, Inkscape::NodePath::NODE_CUSP);
1062                 }
1063             }
1065             if (start->type == Inkscape::NodePath::NODE_AUTO)
1066                 start->type = Inkscape::NodePath::NODE_SMOOTH;
1067             if (end->type == Inkscape::NodePath::NODE_AUTO)
1068                 end->type = Inkscape::NodePath::NODE_SMOOTH;
1070             start->n.pos = start->pos;
1071             end->p.pos = end->pos;
1073             sp_node_adjust_handle(start, -1);
1074             sp_node_adjust_handle(end, 1);
1076         } else {
1077             Geom::Point delta = end->pos - start->pos;
1078             start->n.pos = start->pos + delta / 3;
1079             end->p.pos = end->pos - delta / 3;
1080             sp_node_adjust_handle(start, 1);
1081             sp_node_adjust_handle(end, -1);
1082         }
1084         sp_node_update_handles(start);
1085         sp_node_update_handles(end);
1086     }
1089 static void
1090 sp_nodepath_update_node_knot(Inkscape::NodePath::Node *node)
1092     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1093         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1094         node->knot->setSize (node->selected? 11 : 9);
1095         sp_knot_update_ctrl(node->knot);
1096     } else if (node->type == Inkscape::NodePath::NODE_AUTO) {
1097         node->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1098         node->knot->setSize (node->selected? 11 : 9);
1099         sp_knot_update_ctrl(node->knot);
1100     } else {
1101         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1102         node->knot->setSize (node->selected? 9 : 7);
1103         sp_knot_update_ctrl(node->knot);
1104     }
1108 /**
1109  * Change node type, and its handles accordingly.
1110  */
1111 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1113     g_assert(node);
1114     g_assert(node->subpath);
1116     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1117         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1118             type =Inkscape::NodePath::NODE_CUSP;
1119         }
1120     }
1122     node->type = type;
1124     sp_nodepath_update_node_knot(node);
1126     // if one of handles is mouseovered, preserve its position
1127     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1128         sp_node_adjust_handle(node, 1);
1129     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1130         sp_node_adjust_handle(node, -1);
1131     } else {
1132         sp_node_adjust_handles(node);
1133     }
1135     sp_node_update_handles(node);
1137     sp_nodepath_update_statusbar(node->subpath->nodepath);
1139     return node;
1142 bool
1143 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1145 // TODO clean up multiple returns
1146         Inkscape::NodePath::Node *othernode = side->other;
1147         if (!othernode)
1148             return false;
1149         NRPathcode const code = sp_node_path_code_from_side(node, side);
1150         if (code == NR_LINETO)
1151             return true;
1152         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1153         if (&node->p == side) {
1154             other_to_me = &othernode->n;
1155         } else if (&node->n == side) {
1156             other_to_me = &othernode->p;
1157         }
1158         if (!other_to_me)
1159             return false;
1160         bool is_line =
1161              (Geom::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1162               Geom::L2(node->pos - side->pos) < 1e-6);
1163         return is_line;
1166 /**
1167  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1168  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1169  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too.
1170  * If already cusp and set to cusp, retracts handles.
1171 */
1172 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1174     if (type == Inkscape::NodePath::NODE_AUTO) {
1175         if (node->p.other != NULL)
1176             node->code = NR_CURVETO;
1177         if (node->n.other != NULL)
1178             node->n.other->code = NR_CURVETO;
1179     }
1181     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1183 /*
1184   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1186         if (two_handles) {
1187             // do nothing, adjust_handles called via set_node_type will line them up
1188         } else if (one_handle) {
1189             if (opposite_to_handle_is_line) {
1190                 if (lined_up) {
1191                     // already half-smooth; pull opposite handle too making it fully smooth
1192                 } else {
1193                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1194                 }
1195             } else {
1196                 // pull opposite handle in line with the existing one
1197             }
1198         } else if (no_handles) {
1199             if (both_segments_are_lines OR both_segments_are_curves) {
1200                 //pull both handles
1201             } else {
1202                 // pull the handle opposite to line segment, making node half-smooth
1203             }
1204         }
1205 */
1206         bool p_has_handle = (Geom::L2(node->pos  - node->p.pos) > 1e-6);
1207         bool n_has_handle = (Geom::L2(node->pos  - node->n.pos) > 1e-6);
1208         bool p_is_line = sp_node_side_is_line(node, &node->p);
1209         bool n_is_line = sp_node_side_is_line(node, &node->n);
1211         if (p_has_handle && n_has_handle) {
1212             // do nothing, adjust_handles will line them up
1213         } else if (p_has_handle || n_has_handle) {
1214             if (p_has_handle && n_is_line) {
1215                 Radial line (node->n.other->pos - node->pos);
1216                 Radial handle (node->pos - node->p.pos);
1217                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1218                     // already half-smooth; pull opposite handle too making it fully smooth
1219                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1220                 } else {
1221                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1222                 }
1223             } else if (n_has_handle && p_is_line) {
1224                 Radial line (node->p.other->pos - node->pos);
1225                 Radial handle (node->pos - node->n.pos);
1226                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1227                     // already half-smooth; pull opposite handle too making it fully smooth
1228                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1229                 } else {
1230                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1231                 }
1232             } else if (p_has_handle && node->n.other) {
1233                 // pull n handle
1234                 node->n.other->code = NR_CURVETO;
1235                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1236                     Geom::L2(node->p.pos - node->pos) :
1237                     Geom::L2(node->n.other->pos - node->pos) / 3;
1238                 node->n.pos = node->pos - (len / Geom::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1239             } else if (n_has_handle && node->p.other) {
1240                 // pull p handle
1241                 node->code = NR_CURVETO;
1242                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1243                     Geom::L2(node->n.pos - node->pos) :
1244                     Geom::L2(node->p.other->pos - node->pos) / 3;
1245                 node->p.pos = node->pos - (len / Geom::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1246             }
1247         } else if (!p_has_handle && !n_has_handle) {
1248             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1249                 // no handles, but both segments are either lnes or curves:
1250                 //pull both handles
1252                 // convert both to curves:
1253                 node->code = NR_CURVETO;
1254                 node->n.other->code = NR_CURVETO;
1256                 sp_node_adjust_handles_auto(node);
1257             } else {
1258                 // pull the handle opposite to line segment, making it half-smooth
1259                 if (p_is_line && node->n.other) {
1260                     if (type != Inkscape::NodePath::NODE_SYMM) {
1261                         // pull n handle
1262                         node->n.other->code = NR_CURVETO;
1263                         double len =  Geom::L2(node->n.other->pos - node->pos) / 3;
1264                         node->n.pos = node->pos + (len / Geom::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1265                     }
1266                 } else if (n_is_line && node->p.other) {
1267                     if (type != Inkscape::NodePath::NODE_SYMM) {
1268                         // pull p handle
1269                         node->code = NR_CURVETO;
1270                         double len =  Geom::L2(node->p.other->pos - node->pos) / 3;
1271                         node->p.pos = node->pos + (len / Geom::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1272                     }
1273                 }
1274             }
1275         }
1276     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1277         // cusping a cusp: retract nodes
1278         node->p.pos = node->pos;
1279         node->n.pos = node->pos;
1280     }
1282     sp_nodepath_set_node_type (node, type);
1285 /**
1286  * Move node to point, and adjust its and neighbouring handles.
1287  */
1288 void sp_node_moveto(Inkscape::NodePath::Node *node, Geom::Point p)
1290     if (node->type == Inkscape::NodePath::NODE_AUTO) {
1291         node->pos = p;
1292         sp_node_adjust_handles_auto(node);
1293     } else {
1294         Geom::Point delta = p - node->pos;
1295         node->pos = p;
1297         node->p.pos += delta;
1298         node->n.pos += delta;
1299     }
1301     Inkscape::NodePath::Node *node_p = NULL;
1302     Inkscape::NodePath::Node *node_n = NULL;
1304     if (node->p.other) {
1305         if (node->code == NR_LINETO) {
1306             sp_node_adjust_handle(node, 1);
1307             sp_node_adjust_handle(node->p.other, -1);
1308             node_p = node->p.other;
1309         }
1310         if (!node->p.other->selected && node->p.other->type == Inkscape::NodePath::NODE_AUTO) {
1311             sp_node_adjust_handles_auto(node->p.other);
1312             node_p = node->p.other;
1313         }
1314     }
1315     if (node->n.other) {
1316         if (node->n.other->code == NR_LINETO) {
1317             sp_node_adjust_handle(node, -1);
1318             sp_node_adjust_handle(node->n.other, 1);
1319             node_n = node->n.other;
1320         }
1321         if (!node->n.other->selected && node->n.other->type == Inkscape::NodePath::NODE_AUTO) {
1322             sp_node_adjust_handles_auto(node->n.other);
1323             node_n = node->n.other;
1324         }
1325     }
1327     // this function is only called from batch movers that will update display at the end
1328     // themselves, so here we just move all the knots without emitting move signals, for speed
1329     sp_node_update_handles(node, false);
1330     if (node_n) {
1331         sp_node_update_handles(node_n, false);
1332     }
1333     if (node_p) {
1334         sp_node_update_handles(node_p, false);
1335     }
1338 /**
1339  * Call sp_node_moveto() for node selection and handle possible snapping.
1340  */
1341 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, Geom::Coord dx, Geom::Coord dy,
1342                                             bool const snap, bool constrained = false,
1343                                             Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point())
1345     Geom::Point delta(dx, dy);
1346     Geom::Point best_pt = delta;
1347     Inkscape::SnappedPoint best;
1349     if (snap) {
1350         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1351          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we
1352          * must provide that information. */
1354         // Build a list of the unselected nodes to which the snapper should snap
1355         std::vector<std::pair<Geom::Point, int> > unselected_nodes;
1356         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1357             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1358             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1359                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1360                 if (!node->selected) {
1361                     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));
1362                 }
1363             }
1364         }
1366         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1368         // When only the node closest to the mouse pointer is to be snapped
1369         // then we will not even try to snap to other points and discard those immediately
1370         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1371         bool closest_only = prefs->getBool("/options/snapclosestonly/value", false);
1373         Inkscape::NodePath::Node *closest_node = NULL;
1374         Geom::Coord closest_dist = NR_HUGE;
1376         if (closest_only) {
1377                 for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1378                         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1379                         Geom::Coord dist = Geom::L2(nodepath->drag_origin_mouse - n->origin);
1380                         if (dist < closest_dist) {
1381                                 closest_node = n;
1382                                 closest_dist = dist;
1383                         }
1384                 }
1385         }
1387         // Iterate through all selected nodes
1388         m.setup(nodepath->desktop, false, SP_PATH(nodepath->item), &unselected_nodes);
1389         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1390             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1391             if (!closest_only || n == closest_node) { //try to snap either all selected nodes or only the closest one
1392                     Inkscape::SnappedPoint s;
1393                     Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP);
1394                     if (constrained) {
1395                         Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1396                         dedicated_constraint.setPoint(n->pos);
1397                         s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type, dedicated_constraint);
1398                     } else {
1399                         s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type);
1400                     }
1402                     if (s.getSnapped()) {
1403                         s.setPointerDistance(Geom::L2(nodepath->drag_origin_mouse - n->origin));
1404                         if (!s.isOtherSnapBetter(best, true)) {
1405                                 best = s;
1406                                 best_pt = from_2geom(s.getPoint()) - n->pos;
1407                         }
1408                     }
1409             }
1410         }
1412         if (best.getSnapped()) {
1413             nodepath->desktop->snapindicator->set_new_snaptarget(best);
1414         } else {
1415             nodepath->desktop->snapindicator->remove_snaptarget();
1416         }
1417     }
1419     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1420         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1421         sp_node_moveto(n, n->pos + best_pt);
1422     }
1424     // do not update repr here so that node dragging is acceptably fast
1425     update_object(nodepath);
1428 /**
1429 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1430 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1431 near x = 0.
1432  */
1433 double
1434 sculpt_profile (double x, double alpha, guint profile)
1436     double result = 1;
1438     if (x >= 1) {
1439         result = 0;
1440     } else if (x <= 0) {
1441         result = 1;
1442     } else {
1443         switch (profile) {
1444             case SCULPT_PROFILE_LINEAR:
1445                 result = 1 - x;
1446                 break;
1447             case SCULPT_PROFILE_BELL:
1448                 result = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1449                 break;
1450             case SCULPT_PROFILE_ELLIPTIC:
1451                 result = sqrt(1 - x*x);
1452                 break;
1453             default:
1454                 g_assert_not_reached();
1455         }
1456     }
1458     return result;
1461 double
1462 bezier_length (Geom::Point a, Geom::Point ah, Geom::Point bh, Geom::Point b)
1464     // extremely primitive for now, don't have time to look for the real one
1465     double lower = Geom::L2(b - a);
1466     double upper = Geom::L2(ah - a) + Geom::L2(bh - ah) + Geom::L2(bh - b);
1467     return (lower + upper)/2;
1470 void
1471 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, Geom::Point delta, Geom::Point delta_n, Geom::Point delta_p)
1473     n->pos = n->origin + delta;
1474     n->n.pos = n->n.origin + delta_n;
1475     n->p.pos = n->p.origin + delta_p;
1476     sp_node_adjust_handles(n);
1477     sp_node_update_handles(n, false);
1480 /**
1481  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1482  * on how far they are from the dragged node n.
1483  */
1484 static void
1485 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, Geom::Point delta)
1487     g_assert (n);
1488     g_assert (nodepath);
1489     g_assert (n->subpath->nodepath == nodepath);
1491     double pressure = n->knot->pressure;
1492     if (pressure == 0)
1493         pressure = 0.5; // default
1494     pressure = CLAMP (pressure, 0.2, 0.8);
1496     // map pressure to alpha = 1/5 ... 5
1497     double alpha = 1 - 2 * fabs(pressure - 0.5);
1498     if (pressure > 0.5)
1499         alpha = 1/alpha;
1501     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1502     guint profile = prefs->getInt("/tools/nodes/sculpting_profile", SCULPT_PROFILE_BELL);
1504     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1505         // Only one subpath has selected nodes:
1506         // use linear mode, where the distance from n to node being dragged is calculated along the path
1508         double n_sel_range = 0, p_sel_range = 0;
1509         guint n_nodes = 0, p_nodes = 0;
1510         guint n_sel_nodes = 0, p_sel_nodes = 0;
1512         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1513         {
1514             double n_range = 0, p_range = 0;
1515             bool n_going = true, p_going = true;
1516             Inkscape::NodePath::Node *n_node = n;
1517             Inkscape::NodePath::Node *p_node = n;
1518             do {
1519                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1520                 if (n_node && n_going)
1521                     n_node = n_node->n.other;
1522                 if (n_node == NULL) {
1523                     n_going = false;
1524                 } else {
1525                     n_nodes ++;
1526                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1527                     if (n_node->selected) {
1528                         n_sel_nodes ++;
1529                         n_sel_range = n_range;
1530                     }
1531                     if (n_node == p_node) {
1532                         n_going = false;
1533                         p_going = false;
1534                     }
1535                 }
1536                 if (p_node && p_going)
1537                     p_node = p_node->p.other;
1538                 if (p_node == NULL) {
1539                     p_going = false;
1540                 } else {
1541                     p_nodes ++;
1542                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1543                     if (p_node->selected) {
1544                         p_sel_nodes ++;
1545                         p_sel_range = p_range;
1546                     }
1547                     if (p_node == n_node) {
1548                         n_going = false;
1549                         p_going = false;
1550                     }
1551                 }
1552             } while (n_going || p_going);
1553         }
1555         // Second pass: actually move nodes in this subpath
1556         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1557         {
1558             double n_range = 0, p_range = 0;
1559             bool n_going = true, p_going = true;
1560             Inkscape::NodePath::Node *n_node = n;
1561             Inkscape::NodePath::Node *p_node = n;
1562             do {
1563                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1564                 if (n_node && n_going)
1565                     n_node = n_node->n.other;
1566                 if (n_node == NULL) {
1567                     n_going = false;
1568                 } else {
1569                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1570                     if (n_node->selected) {
1571                         sp_nodepath_move_node_and_handles (n_node,
1572                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1573                                                            sculpt_profile ((n_range + Geom::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1574                                                            sculpt_profile ((n_range - Geom::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1575                     }
1576                     if (n_node == p_node) {
1577                         n_going = false;
1578                         p_going = false;
1579                     }
1580                 }
1581                 if (p_node && p_going)
1582                     p_node = p_node->p.other;
1583                 if (p_node == NULL) {
1584                     p_going = false;
1585                 } else {
1586                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1587                     if (p_node->selected) {
1588                         sp_nodepath_move_node_and_handles (p_node,
1589                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1590                                                            sculpt_profile ((p_range - Geom::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1591                                                            sculpt_profile ((p_range + Geom::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1592                     }
1593                     if (p_node == n_node) {
1594                         n_going = false;
1595                         p_going = false;
1596                     }
1597                 }
1598             } while (n_going || p_going);
1599         }
1601     } else {
1602         // Multiple subpaths have selected nodes:
1603         // use spatial mode, where the distance from n to node being dragged is measured directly as Geom::L2.
1604         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1605         // fix the pear-like shape when sculpting e.g. a ring
1607         // First pass: calculate range
1608         gdouble direct_range = 0;
1609         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1610             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1611             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1612                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1613                 if (node->selected) {
1614                     direct_range = MAX(direct_range, Geom::L2(node->origin - n->origin));
1615                 }
1616             }
1617         }
1619         // Second pass: actually move nodes
1620         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1621             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1622             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1623                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1624                 if (node->selected) {
1625                     if (direct_range > 1e-6) {
1626                         sp_nodepath_move_node_and_handles (node,
1627                                                        sculpt_profile (Geom::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1628                                                        sculpt_profile (Geom::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1629                                                        sculpt_profile (Geom::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1630                     } else {
1631                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1632                     }
1634                 }
1635             }
1636         }
1637     }
1639     // do not update repr here so that node dragging is acceptably fast
1640     update_object(nodepath);
1644 /**
1645  * Move node selection to point, adjust its and neighbouring handles,
1646  * handle possible snapping, and commit the change with possible undo.
1647  */
1648 void
1649 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1651     if (!nodepath) return;
1653     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1655     if (dx == 0) {
1656         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1657     } else if (dy == 0) {
1658         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1659     } else {
1660         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1661     }
1664 /**
1665  * Move node selection off screen and commit the change.
1666  */
1667 void
1668 sp_node_selected_move_screen(SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1670     // borrowed from sp_selection_move_screen in selection-chemistry.c
1671     // we find out the current zoom factor and divide deltas by it
1673     gdouble zoom = desktop->current_zoom();
1674     gdouble zdx = dx / zoom;
1675     gdouble zdy = dy / zoom;
1677     if (!nodepath) return;
1679     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1681     if (dx == 0) {
1682         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1683     } else if (dy == 0) {
1684         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1685     } else {
1686         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1687     }
1690 /**
1691  * Move selected nodes to the absolute position given
1692  */
1693 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis)
1695     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1696         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1697         Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]);
1698         sp_node_moveto(n, npos);
1699     }
1701     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1704 /**
1705  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return Geom::Nothing
1706  */
1707 boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1709     boost::optional<Geom::Coord> no_coord;
1710     g_return_val_if_fail(nodepath->selected, no_coord);
1712     // determine coordinate of first selected node
1713     GList *nsel = nodepath->selected;
1714     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1715     Geom::Coord coord = n->pos[axis];
1716     bool coincide = true;
1718     // compare it to the coordinates of all the other selected nodes
1719     for (GList *l = nsel->next; l != NULL; l = l->next) {
1720         n = (Inkscape::NodePath::Node *) l->data;
1721         if (n->pos[axis] != coord) {
1722             coincide = false;
1723         }
1724     }
1725     if (coincide) {
1726         return coord;
1727     } else {
1728         Geom::Rect bbox = sp_node_selected_bbox(nodepath);
1729         // currently we return the coordinate of the bounding box midpoint because I don't know how
1730         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1731         return bbox.midpoint()[axis];
1732     }
1735 /** If they don't yet exist, creates knot and line for the given side of the node */
1736 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1738     if (!side->knot) {
1739         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"));
1741         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1742         side->knot->setSize (7);
1743         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1744         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1745         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1746         sp_knot_update_ctrl(side->knot);
1748         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1749         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1750         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1751         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1752         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1753         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1754     }
1756     if (!side->line) {
1757         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1758                                         SP_TYPE_CTRLLINE, NULL);
1759     }
1762 /**
1763  * Ensure the given handle of the node is visible/invisible, update its screen position
1764  */
1765 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1767     g_assert(node != NULL);
1769    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1770     NRPathcode code = sp_node_path_code_from_side(node, side);
1772     show_handle = show_handle && (code == NR_CURVETO) && (Geom::L2(side->pos - node->pos) > 1e-6);
1774     if (show_handle) {
1775         if (!side->knot) { // No handle knot at all
1776             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1777             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1778             side->knot->pos = side->pos;
1779             if (side->knot->item)
1780                 SP_CTRL(side->knot->item)->moveto(side->pos);
1781             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1782             sp_knot_show(side->knot);
1783         } else {
1784             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1785                 if (fire_move_signals) {
1786                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1787                 } else {
1788                     sp_knot_moveto(side->knot, side->pos);
1789                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1790                 }
1791             }
1792             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1793                 sp_knot_show(side->knot);
1794             }
1795         }
1796         sp_canvas_item_show(side->line);
1797     } else {
1798         if (side->knot) {
1799             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1800                 sp_knot_hide(side->knot);
1801             }
1802         }
1803         if (side->line) {
1804             sp_canvas_item_hide(side->line);
1805         }
1806     }
1809 /**
1810  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1811  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1812  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1813  * updated; otherwise, just move the knots silently (used in batch moves).
1814  */
1815 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1817     g_assert(node != NULL);
1819     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1820         sp_knot_show(node->knot);
1821     }
1823     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1824         if (fire_move_signals)
1825             sp_knot_set_position(node->knot, node->pos, 0);
1826         else
1827             sp_knot_moveto(node->knot, node->pos);
1828     }
1830     gboolean show_handles = node->selected;
1831     if (node->p.other != NULL) {
1832         if (node->p.other->selected) show_handles = TRUE;
1833     }
1834     if (node->n.other != NULL) {
1835         if (node->n.other->selected) show_handles = TRUE;
1836     }
1838     if (node->subpath->nodepath->show_handles == false)
1839         show_handles = FALSE;
1841     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1842     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1845 /**
1846  * Call sp_node_update_handles() for all nodes on subpath.
1847  */
1848 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1850     g_assert(subpath != NULL);
1852     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1853         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1854     }
1857 /**
1858  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1859  */
1860 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1862     g_assert(nodepath != NULL);
1864     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1865         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1866     }
1869 void
1870 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1872     if (nodepath) {
1873         nodepath->show_handles = show;
1874         sp_nodepath_update_handles(nodepath);
1875     }
1878 /**
1879  * Adds all selected nodes in nodepath to list.
1880  */
1881 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1883     StlConv<Node *>::list(l, selected);
1884 /// \todo this adds a copying, rework when the selection becomes a stl list
1887 /**
1888  * Align selected nodes on the specified axis.
1889  */
1890 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1892     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1893         return;
1894     }
1896     if ( !nodepath->selected->next ) { // only one node selected
1897         return;
1898     }
1899    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1900     Geom::Point dest(pNode->pos);
1901     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1902         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1903         if (pNode) {
1904             dest[axis] = pNode->pos[axis];
1905             sp_node_moveto(pNode, dest);
1906         }
1907     }
1909     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1912 /// Helper struct.
1913 struct NodeSort
1915    Inkscape::NodePath::Node *_node;
1916     Geom::Coord _coord;
1917     /// \todo use vectorof pointers instead of calling copy ctor
1918     NodeSort(Inkscape::NodePath::Node *node, Geom::Dim2 axis) :
1919         _node(node), _coord(node->pos[axis])
1920     {}
1922 };
1924 static bool operator<(NodeSort const &a, NodeSort const &b)
1926     return (a._coord < b._coord);
1929 /**
1930  * Distribute selected nodes on the specified axis.
1931  */
1932 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1934     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1935         return;
1936     }
1938     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1939         return;
1940     }
1942    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1943     std::vector<NodeSort> sorted;
1944     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1945         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1946         if (pNode) {
1947             NodeSort n(pNode, axis);
1948             sorted.push_back(n);
1949             //dest[axis] = pNode->pos[axis];
1950             //sp_node_moveto(pNode, dest);
1951         }
1952     }
1953     std::sort(sorted.begin(), sorted.end());
1954     unsigned int len = sorted.size();
1955     //overall bboxes span
1956     float dist = (sorted.back()._coord -
1957                   sorted.front()._coord);
1958     //new distance between each bbox
1959     float step = (dist) / (len - 1);
1960     float pos = sorted.front()._coord;
1961     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1962           it < sorted.end();
1963           it ++ )
1964     {
1965         Geom::Point dest((*it)._node->pos);
1966         dest[axis] = pos;
1967         sp_node_moveto((*it)._node, dest);
1968         pos += step;
1969     }
1971     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1975 /**
1976  * Call sp_nodepath_line_add_node() for all selected segments.
1977  */
1978 void
1979 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1981     if (!nodepath) {
1982         return;
1983     }
1985     GList *nl = NULL;
1987     int n_added = 0;
1989     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1990        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1991         g_assert(t->selected);
1992         if (t->p.other && t->p.other->selected) {
1993             nl = g_list_prepend(nl, t);
1994         }
1995     }
1997     while (nl) {
1998        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1999        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
2000        sp_nodepath_node_select(n, TRUE, FALSE);
2001        n_added ++;
2002        nl = g_list_remove(nl, t);
2003     }
2005     /** \todo fixme: adjust ? */
2006     sp_nodepath_update_handles(nodepath);
2008     if (n_added > 1) {
2009         sp_nodepath_update_repr(nodepath, _("Add nodes"));
2010     } else if (n_added > 0) {
2011         sp_nodepath_update_repr(nodepath, _("Add node"));
2012     }
2014     sp_nodepath_update_statusbar(nodepath);
2017 /**
2018  * Select segment nearest to point
2019  */
2020 void
2021 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p, bool toggle)
2023     if (!nodepath) {
2024         return;
2025     }
2027     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2028     Geom::PathVector const &pathv = curve->get_pathvector();
2029     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2030     if (!pvpos) {
2031         g_print ("Possible error?\n");
2032         return;
2033     }
2035     // calculate index for nodepath's representation.
2036     unsigned int segment_index = floor(pvpos->t) + 1;
2037     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2038         segment_index += pathv[i].size() + 1;
2039         if (pathv[i].closed()) {
2040             segment_index += 1;
2041         }
2042     }
2044     curve->unref();
2046     //find segment to segment
2047     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2049     //fixme: this can return NULL, so check before proceeding.
2050     g_return_if_fail(e != NULL);
2052     gboolean force = FALSE;
2053     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
2054         force = TRUE;
2055     }
2056     sp_nodepath_node_select(e, (gboolean) toggle, force);
2057     if (e->p.other)
2058         sp_nodepath_node_select(e->p.other, TRUE, force);
2060     sp_nodepath_update_handles(nodepath);
2062     sp_nodepath_update_statusbar(nodepath);
2065 /**
2066  * Add a node nearest to point
2067  */
2068 void
2069 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p)
2071     if (!nodepath) {
2072         return;
2073     }
2075     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2076     Geom::PathVector const &pathv = curve->get_pathvector();
2077     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2078     if (!pvpos) {
2079         g_print ("Possible error?\n");
2080         return;
2081     }
2083     // calculate index for nodepath's representation.
2084     double int_part;
2085     double t = std::modf(pvpos->t, &int_part);
2086     unsigned int segment_index = (unsigned int)int_part + 1;
2087     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2088         segment_index += pathv[i].size() + 1;
2089         if (pathv[i].closed()) {
2090             segment_index += 1;
2091         }
2092     }
2094     curve->unref();
2096     //find segment to split
2097     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2098     if (!e) {
2099         return;
2100     }
2102     //don't know why but t seems to flip for lines
2103     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2104         t = 1.0 - t;
2105     }
2107     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2108     sp_nodepath_node_select(n, FALSE, TRUE);
2110     /* fixme: adjust ? */
2111     sp_nodepath_update_handles(nodepath);
2113     sp_nodepath_update_repr(nodepath, _("Add node"));
2115     sp_nodepath_update_statusbar(nodepath);
2118 /*
2119  * Adjusts a segment so that t moves by a certain delta for dragging
2120  * converts lines to curves
2121  *
2122  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2123  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2124  */
2125 void
2126 sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, Geom::Point delta)
2128     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node);
2130     //fixme: e and e->p can be NULL, so check for those before proceeding
2131     g_return_if_fail(e != NULL);
2132     g_return_if_fail(&e->p != NULL);
2134     if (e->type == Inkscape::NodePath::NODE_AUTO) {
2135         e->type = Inkscape::NodePath::NODE_SMOOTH;
2136         sp_nodepath_update_node_knot (e);
2137     }
2138     if (e->p.other->type == Inkscape::NodePath::NODE_AUTO) {
2139         e->p.other->type = Inkscape::NodePath::NODE_SMOOTH;
2140         sp_nodepath_update_node_knot (e->p.other);
2141     }
2143     /* feel good is an arbitrary parameter that distributes the delta between handles
2144      * if t of the drag point is less than 1/6 distance form the endpoint only
2145      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2146      */
2147     double feel_good;
2148     if (t <= 1.0 / 6.0)
2149         feel_good = 0;
2150     else if (t <= 0.5)
2151         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2152     else if (t <= 5.0 / 6.0)
2153         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2154     else
2155         feel_good = 1;
2157     //if we're dragging a line convert it to a curve
2158     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2159         sp_nodepath_set_line_type(e, NR_CURVETO);
2160     }
2162     Geom::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2163     Geom::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2164     e->p.other->n.pos += offsetcoord0;
2165     e->p.pos += offsetcoord1;
2167     // adjust handles of adjacent nodes where necessary
2168     sp_node_adjust_handle(e,1);
2169     sp_node_adjust_handle(e->p.other,-1);
2171     sp_nodepath_update_handles(e->subpath->nodepath);
2173     update_object(e->subpath->nodepath);
2175     sp_nodepath_update_statusbar(e->subpath->nodepath);
2179 /**
2180  * Call sp_nodepath_break() for all selected segments.
2181  */
2182 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2184     if (!nodepath) return;
2186     GList *tempin = g_list_copy(nodepath->selected);
2187     GList *temp = NULL;
2188     for (GList *l = tempin; l != NULL; l = l->next) {
2189        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2190        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2191         if (nn == NULL) continue; // no break, no new node
2192         temp = g_list_prepend(temp, nn);
2193     }
2194     g_list_free(tempin);
2196     if (temp) {
2197         sp_nodepath_deselect(nodepath);
2198     }
2199     for (GList *l = temp; l != NULL; l = l->next) {
2200         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2201     }
2203     sp_nodepath_update_handles(nodepath);
2205     sp_nodepath_update_repr(nodepath, _("Break path"));
2208 /**
2209  * Duplicate the selected node(s).
2210  */
2211 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2213     if (!nodepath) {
2214         return;
2215     }
2217     GList *temp = NULL;
2218     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2219        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2220        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2221         if (nn == NULL) continue; // could not duplicate
2222         temp = g_list_prepend(temp, nn);
2223     }
2225     if (temp) {
2226         sp_nodepath_deselect(nodepath);
2227     }
2228     for (GList *l = temp; l != NULL; l = l->next) {
2229         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2230     }
2232     sp_nodepath_update_handles(nodepath);
2234     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2237 /**
2238  *  Internal function to join two nodes by merging them into one.
2239  */
2240 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2242     /* a and b are endpoints */
2244     // if one of the two nodes is mouseovered, fix its position
2245     Geom::Point c;
2246     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2247         c = a->pos;
2248     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2249         c = b->pos;
2250     } else {
2251         // otherwise, move joined node to the midpoint
2252         c = (a->pos + b->pos) / 2;
2253     }
2255     if (a->subpath == b->subpath) {
2256        Inkscape::NodePath::SubPath *sp = a->subpath;
2257         sp_nodepath_subpath_close(sp);
2258         sp_node_moveto (sp->first, c);
2260         sp_nodepath_update_handles(sp->nodepath);
2261         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2262         return;
2263     }
2265     /* a and b are separate subpaths */
2266     Inkscape::NodePath::SubPath *sa = a->subpath;
2267     Inkscape::NodePath::SubPath *sb = b->subpath;
2268     Geom::Point p;
2269     Inkscape::NodePath::Node *n;
2270     NRPathcode code;
2271     if (a == sa->first) {
2272         // we will now reverse sa, so that a is its last node, not first, and drop that node
2273         p = sa->first->n.pos;
2274         code = (NRPathcode)sa->first->n.other->code;
2275         // create new subpath
2276        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2277        // create a first moveto node on it
2278         n = sa->last;
2279         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2280         n = n->p.other;
2281         if (n == sa->first) n = NULL;
2282         while (n) {
2283             // copy the rest of the nodes from sa to t, going backwards
2284             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2285             n = n->p.other;
2286             if (n == sa->first) n = NULL;
2287         }
2288         // replace sa with t
2289         sp_nodepath_subpath_destroy(sa);
2290         sa = t;
2291     } else if (a == sa->last) {
2292         // a is already last, just drop it
2293         p = sa->last->p.pos;
2294         code = (NRPathcode)sa->last->code;
2295         sp_nodepath_node_destroy(sa->last);
2296     } else {
2297         code = NR_END;
2298         g_assert_not_reached();
2299     }
2301     if (b == sb->first) {
2302         // copy all nodes from b to a, forward
2303         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2304         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2305             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2306         }
2307     } else if (b == sb->last) {
2308         // copy all nodes from b to a, backward
2309         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2310         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2311             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2312         }
2313     } else {
2314         g_assert_not_reached();
2315     }
2316     /* and now destroy sb */
2318     sp_nodepath_subpath_destroy(sb);
2320     sp_nodepath_update_handles(sa->nodepath);
2322     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2324     sp_nodepath_update_statusbar(nodepath);
2327 /**
2328  *  Internal function to join two nodes by adding a segment between them.
2329  */
2330 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2332     if (a->subpath == b->subpath) {
2333        Inkscape::NodePath::SubPath *sp = a->subpath;
2335         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2336         sp->closed = TRUE;
2338         sp->first->p.other = sp->last;
2339         sp->last->n.other  = sp->first;
2341         sp_node_handle_mirror_p_to_n(sp->last);
2342         sp_node_handle_mirror_n_to_p(sp->first);
2344         sp->first->code = sp->last->code;
2345         sp->first       = sp->last;
2347         sp_nodepath_update_handles(sp->nodepath);
2349         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2351         return;
2352     }
2354     /* a and b are separate subpaths */
2355     Inkscape::NodePath::SubPath *sa = a->subpath;
2356     Inkscape::NodePath::SubPath *sb = b->subpath;
2358     Inkscape::NodePath::Node *n;
2359     Geom::Point p;
2360     NRPathcode code;
2361     if (a == sa->first) {
2362         code = (NRPathcode) sa->first->n.other->code;
2363        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2364         n = sa->last;
2365         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2366         for (n = n->p.other; n != NULL; n = n->p.other) {
2367             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2368         }
2369         sp_nodepath_subpath_destroy(sa);
2370         sa = t;
2371     } else if (a == sa->last) {
2372         code = (NRPathcode)sa->last->code;
2373     } else {
2374         code = NR_END;
2375         g_assert_not_reached();
2376     }
2378     if (b == sb->first) {
2379         n = sb->first;
2380         sp_node_handle_mirror_p_to_n(sa->last);
2381         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2382         sp_node_handle_mirror_n_to_p(sa->last);
2383         for (n = n->n.other; n != NULL; n = n->n.other) {
2384             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2385         }
2386     } else if (b == sb->last) {
2387         n = sb->last;
2388         sp_node_handle_mirror_p_to_n(sa->last);
2389         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2390         sp_node_handle_mirror_n_to_p(sa->last);
2391         for (n = n->p.other; n != NULL; n = n->p.other) {
2392             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2393         }
2394     } else {
2395         g_assert_not_reached();
2396     }
2397     /* and now destroy sb */
2399     sp_nodepath_subpath_destroy(sb);
2401     sp_nodepath_update_handles(sa->nodepath);
2403     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2406 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2408 /**
2409  * Internal function to handle joining two nodes.
2410  */
2411 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2413     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2415     if (g_list_length(nodepath->selected) != 2) {
2416         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2417         return;
2418     }
2420     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2421     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2423     g_assert(a != b);
2424     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2425         // someone tried to join an orphan node (i.e. a single-node subpath).
2426         // this is not worth an error message, just fail silently.
2427         return;
2428     }
2430     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2431         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2432         return;
2433     }
2435     switch(mode) {
2436         case NODE_JOIN_ENDPOINTS:
2437             do_node_selected_join(nodepath, a, b);
2438             break;
2439         case NODE_JOIN_SEGMENT:
2440             do_node_selected_join_segment(nodepath, a, b);
2441             break;
2442     }
2445 /**
2446  *  Join two nodes by merging them into one.
2447  */
2448 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2450     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2453 /**
2454  *  Join two nodes by adding a segment between them.
2455  */
2456 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2458     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2461 /**
2462  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2463  */
2464 void sp_node_delete_preserve(GList *nodes_to_delete)
2466     GSList *nodepaths = NULL;
2468     while (nodes_to_delete) {
2469         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2470         Inkscape::NodePath::SubPath *sp = node->subpath;
2471         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2472         Inkscape::NodePath::Node *sample_cursor = NULL;
2473         Inkscape::NodePath::Node *sample_end = NULL;
2474         Inkscape::NodePath::Node *delete_cursor = node;
2475         bool just_delete = false;
2477         //find the start of this contiguous selection
2478         //move left to the first node that is not selected
2479         //or the start of the non-closed path
2480         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2481             delete_cursor = curr;
2482         }
2484         //just delete at the beginning of an open path
2485         if (!delete_cursor->p.other) {
2486             sample_cursor = delete_cursor;
2487             just_delete = true;
2488         } else {
2489             sample_cursor = delete_cursor->p.other;
2490         }
2492         //calculate points for each segment
2493         int rate = 5;
2494         float period = 1.0 / rate;
2495         std::vector<Geom::Point> data;
2496         if (!just_delete) {
2497             data.push_back(sample_cursor->pos);
2498             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2499                 //just delete at the end of an open path
2500                 if (!sp->closed && curr == sp->last) {
2501                     just_delete = true;
2502                     break;
2503                 }
2505                 //sample points on the contiguous selected segment
2506                 Geom::Point *bez;
2507                 bez = new Geom::Point [4];
2508                 bez[0] = curr->pos;
2509                 bez[1] = curr->n.pos;
2510                 bez[2] = curr->n.other->p.pos;
2511                 bez[3] = curr->n.other->pos;
2512                 for (int i=1; i<rate; i++) {
2513                     gdouble t = i * period;
2514                     Geom::Point p = bezier_pt(3, bez, t);
2515                     data.push_back(p);
2516                 }
2517                 data.push_back(curr->n.other->pos);
2519                 sample_end = curr->n.other;
2520                 //break if we've come full circle or hit the end of the selection
2521                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2522                     break;
2523                 }
2524             }
2525         }
2527         if (!just_delete) {
2528             //calculate the best fitting single segment and adjust the endpoints
2529             Geom::Point *adata;
2530             adata = new Geom::Point [data.size()];
2531             copy(data.begin(), data.end(), adata);
2533             Geom::Point *bez;
2534             bez = new Geom::Point [4];
2535             //would decreasing error create a better fitting approximation?
2536             gdouble error = 1.0;
2537             gint ret;
2538             ret = Geom::bezier_fit_cubic (bez, adata, data.size(), error);
2540             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2541             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2542             //the resulting nodes behave as expected.
2543             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2544                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2545             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2546                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2548             //adjust endpoints
2549             sample_cursor->n.pos = bez[1];
2550             sample_end->p.pos = bez[2];
2551         }
2553         //destroy this contiguous selection
2554         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2555             Inkscape::NodePath::Node *temp = delete_cursor;
2556             if (delete_cursor->n.other == delete_cursor) {
2557                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2558                 delete_cursor = NULL;
2559             } else {
2560                 delete_cursor = delete_cursor->n.other;
2561             }
2562             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2563             sp_nodepath_node_destroy(temp);
2564         }
2566         sp_nodepath_update_handles(nodepath);
2568         if (!g_slist_find(nodepaths, nodepath))
2569             nodepaths = g_slist_prepend (nodepaths, nodepath);
2570     }
2572     for (GSList *i = nodepaths; i; i = i->next) {
2573         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2574         // different nodepaths will give us one undo event per nodepath
2575         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2577         // if the entire nodepath is removed, delete the selected object.
2578         if (nodepath->subpaths == NULL ||
2579             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2580             //at least 2
2581             sp_nodepath_get_node_count(nodepath) < 2) {
2582             SPDocument *document = sp_desktop_document (nodepath->desktop);
2583             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2584             //delete this nodepath's object, not the entire selection! (though at this time, this
2585             //does not matter)
2586             sp_selection_delete(nodepath->desktop);
2587             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2588                               _("Delete nodes"));
2589         } else {
2590             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2591             sp_nodepath_update_statusbar(nodepath);
2592         }
2593     }
2595     g_slist_free (nodepaths);
2598 /**
2599  * Delete one or more selected nodes.
2600  */
2601 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2603     if (!nodepath) return;
2604     if (!nodepath->selected) return;
2606     /** \todo fixme: do it the right way */
2607     while (nodepath->selected) {
2608        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2609         sp_nodepath_node_destroy(node);
2610     }
2613     //clean up the nodepath (such as for trivial subpaths)
2614     sp_nodepath_cleanup(nodepath);
2616     sp_nodepath_update_handles(nodepath);
2618     // if the entire nodepath is removed, delete the selected object.
2619     if (nodepath->subpaths == NULL ||
2620         sp_nodepath_get_node_count(nodepath) < 2) {
2621         SPDocument *document = sp_desktop_document (nodepath->desktop);
2622         sp_selection_delete(nodepath->desktop);
2623         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2624                           _("Delete nodes"));
2625         return;
2626     }
2628     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2630     sp_nodepath_update_statusbar(nodepath);
2633 /**
2634  * Delete one or more segments between two selected nodes.
2635  * This is the code for 'split'.
2636  */
2637 void
2638 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2640    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2641    Inkscape::NodePath::Node *curr, *next;     //Iterators
2643     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2645     if (g_list_length(nodepath->selected) != 2) {
2646         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2647                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2648         return;
2649     }
2651     //Selected nodes, not inclusive
2652    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2653    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2655     if ( ( a==b)                       ||  //same node
2656          (a->subpath  != b->subpath )  ||  //not the same path
2657          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2658          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2659     {
2660         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2661                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2662         return;
2663     }
2665     //###########################################
2666     //# BEGIN EDITS
2667     //###########################################
2668     //##################################
2669     //# CLOSED PATH
2670     //##################################
2671     if (a->subpath->closed) {
2674         gboolean reversed = FALSE;
2676         //Since we can go in a circle, we need to find the shorter distance.
2677         //  a->b or b->a
2678         start = end = NULL;
2679         int distance    = 0;
2680         int minDistance = 0;
2681         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2682             if (curr==b) {
2683                 //printf("a to b:%d\n", distance);
2684                 start = a;//go from a to b
2685                 end   = b;
2686                 minDistance = distance;
2687                 //printf("A to B :\n");
2688                 break;
2689             }
2690             distance++;
2691         }
2693         //try again, the other direction
2694         distance = 0;
2695         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2696             if (curr==a) {
2697                 //printf("b to a:%d\n", distance);
2698                 if (distance < minDistance) {
2699                     start    = b;  //we go from b to a
2700                     end      = a;
2701                     reversed = TRUE;
2702                     //printf("B to A\n");
2703                 }
2704                 break;
2705             }
2706             distance++;
2707         }
2710         //Copy everything from 'end' to 'start' to a new subpath
2711        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2712         for (curr=end ; curr ; curr=curr->n.other) {
2713             NRPathcode code = (NRPathcode) curr->code;
2714             if (curr == end)
2715                 code = NR_MOVETO;
2716             sp_nodepath_node_new(t, NULL,
2717                                  (Inkscape::NodePath::NodeType)curr->type, code,
2718                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2719             if (curr == start)
2720                 break;
2721         }
2722         sp_nodepath_subpath_destroy(a->subpath);
2725     }
2729     //##################################
2730     //# OPEN PATH
2731     //##################################
2732     else {
2734         //We need to get the direction of the list between A and B
2735         //Can we walk from a to b?
2736         start = end = NULL;
2737         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2738             if (curr==b) {
2739                 start = a;  //did it!  we go from a to b
2740                 end   = b;
2741                 //printf("A to B\n");
2742                 break;
2743             }
2744         }
2745         if (!start) {//didn't work?  let's try the other direction
2746             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2747                 if (curr==a) {
2748                     start = b;  //did it!  we go from b to a
2749                     end   = a;
2750                     //printf("B to A\n");
2751                     break;
2752                 }
2753             }
2754         }
2755         if (!start) {
2756             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2757                                                      _("Cannot find path between nodes."));
2758             return;
2759         }
2763         //Copy everything after 'end' to a new subpath
2764        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2765         for (curr=end ; curr ; curr=curr->n.other) {
2766             NRPathcode code = (NRPathcode) curr->code;
2767             if (curr == end)
2768                 code = NR_MOVETO;
2769             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2770                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2771         }
2773         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2774         for (curr = start->n.other ; curr  ; curr=next) {
2775             next = curr->n.other;
2776             sp_nodepath_node_destroy(curr);
2777         }
2779     }
2780     //###########################################
2781     //# END EDITS
2782     //###########################################
2784     //clean up the nodepath (such as for trivial subpaths)
2785     sp_nodepath_cleanup(nodepath);
2787     sp_nodepath_update_handles(nodepath);
2789     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2791     sp_nodepath_update_statusbar(nodepath);
2794 /**
2795  * Call sp_nodepath_set_line() for all selected segments.
2796  */
2797 void
2798 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2800     if (nodepath == NULL) return;
2802     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2803        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2804         g_assert(n->selected);
2805         if (n->p.other && n->p.other->selected) {
2806             sp_nodepath_set_line_type(n, code);
2807         }
2808     }
2810     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2813 /**
2814  * Call sp_nodepath_convert_node_type() for all selected nodes.
2815  */
2816 void
2817 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2819     if (nodepath == NULL) return;
2821     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2823     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2824         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2825     }
2827     sp_nodepath_update_repr(nodepath, _("Change node type"));
2830 /**
2831  * Change select status of node, update its own and neighbour handles.
2832  */
2833 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2835     node->selected = selected;
2837     if (selected) {
2838         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 11 : 9);
2839         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2840         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2841         sp_knot_update_ctrl(node->knot);
2842     } else {
2843         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 9 : 7);
2844         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2845         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2846         sp_knot_update_ctrl(node->knot);
2847     }
2849     sp_node_update_handles(node);
2850     if (node->n.other) sp_node_update_handles(node->n.other);
2851     if (node->p.other) sp_node_update_handles(node->p.other);
2854 /**
2855 \brief Select a node
2856 \param node     The node to select
2857 \param incremental   If true, add to selection, otherwise deselect others
2858 \param override   If true, always select this node, otherwise toggle selected status
2859 */
2860 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2862     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2864     if (incremental) {
2865         if (override) {
2866             if (!g_list_find(nodepath->selected, node)) {
2867                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2868             }
2869             sp_node_set_selected(node, TRUE);
2870         } else { // toggle
2871             if (node->selected) {
2872                 g_assert(g_list_find(nodepath->selected, node));
2873                 nodepath->selected = g_list_remove(nodepath->selected, node);
2874             } else {
2875                 g_assert(!g_list_find(nodepath->selected, node));
2876                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2877             }
2878             sp_node_set_selected(node, !node->selected);
2879         }
2880     } else {
2881         sp_nodepath_deselect(nodepath);
2882         nodepath->selected = g_list_prepend(nodepath->selected, node);
2883         sp_node_set_selected(node, TRUE);
2884     }
2886     sp_nodepath_update_statusbar(nodepath);
2890 /**
2891 \brief Deselect all nodes in the nodepath
2892 */
2893 void
2894 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2896     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2898     while (nodepath->selected) {
2899         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2900         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2901     }
2902     sp_nodepath_update_statusbar(nodepath);
2905 /**
2906 \brief Select or invert selection of all nodes in the nodepath
2907 */
2908 void
2909 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2911     if (!nodepath) return;
2913     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2914        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2915         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2916            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2917            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2918         }
2919     }
2922 /**
2923  * If nothing selected, does the same as sp_nodepath_select_all();
2924  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2925  * (i.e., similar to "select all in layer", with the "selected" subpaths
2926  * being treated as "layers" in the path).
2927  */
2928 void
2929 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2931     if (!nodepath) return;
2933     if (g_list_length (nodepath->selected) == 0) {
2934         sp_nodepath_select_all (nodepath, invert);
2935         return;
2936     }
2938     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2939     GSList *subpaths = NULL;
2941     for (GList *l = copy; l != NULL; l = l->next) {
2942         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2943         Inkscape::NodePath::SubPath *subpath = n->subpath;
2944         if (!g_slist_find (subpaths, subpath))
2945             subpaths = g_slist_prepend (subpaths, subpath);
2946     }
2948     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2949         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2950         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2951             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2952             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2953         }
2954     }
2956     g_slist_free (subpaths);
2957     g_list_free (copy);
2960 /**
2961  * \brief Select the node after the last selected; if none is selected,
2962  * select the first within path.
2963  */
2964 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2966     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2968    Inkscape::NodePath::Node *last = NULL;
2969     if (nodepath->selected) {
2970         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2971            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2972             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2973             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2974                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2975                 if (node->selected) {
2976                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2977                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2978                             if (spl->next) { // there's a next subpath
2979                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2980                                 last = subpath_next->first;
2981                             } else if (spl->prev) { // there's a previous subpath
2982                                 last = NULL; // to be set later to the first node of first subpath
2983                             } else {
2984                                 last = node->n.other;
2985                             }
2986                         } else {
2987                             last = node->n.other;
2988                         }
2989                     } else {
2990                         if (node->n.other) {
2991                             last = node->n.other;
2992                         } else {
2993                             if (spl->next) { // there's a next subpath
2994                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2995                                 last = subpath_next->first;
2996                             } else if (spl->prev) { // there's a previous subpath
2997                                 last = NULL; // to be set later to the first node of first subpath
2998                             } else {
2999                                 last = (Inkscape::NodePath::Node *) subpath->first;
3000                             }
3001                         }
3002                     }
3003                 }
3004             }
3005         }
3006         sp_nodepath_deselect(nodepath);
3007     }
3009     if (last) { // there's at least one more node after selected
3010         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3011     } else { // no more nodes, select the first one in first subpath
3012        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
3013         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
3014     }
3017 /**
3018  * \brief Select the node before the first selected; if none is selected,
3019  * select the last within path
3020  */
3021 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
3023     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
3025    Inkscape::NodePath::Node *last = NULL;
3026     if (nodepath->selected) {
3027         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
3028            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3029             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
3030                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3031                 if (node->selected) {
3032                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
3033                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
3034                             if (spl->prev) { // there's a prev subpath
3035                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3036                                 last = subpath_prev->last;
3037                             } else if (spl->next) { // there's a next subpath
3038                                 last = NULL; // to be set later to the last node of last subpath
3039                             } else {
3040                                 last = node->p.other;
3041                             }
3042                         } else {
3043                             last = node->p.other;
3044                         }
3045                     } else {
3046                         if (node->p.other) {
3047                             last = node->p.other;
3048                         } else {
3049                             if (spl->prev) { // there's a prev subpath
3050                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3051                                 last = subpath_prev->last;
3052                             } else if (spl->next) { // there's a next subpath
3053                                 last = NULL; // to be set later to the last node of last subpath
3054                             } else {
3055                                 last = (Inkscape::NodePath::Node *) subpath->last;
3056                             }
3057                         }
3058                     }
3059                 }
3060             }
3061         }
3062         sp_nodepath_deselect(nodepath);
3063     }
3065     if (last) { // there's at least one more node before selected
3066         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3067     } else { // no more nodes, select the last one in last subpath
3068         GList *spl = g_list_last(nodepath->subpaths);
3069        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3070         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
3071     }
3074 /**
3075  * \brief Select all nodes that are within the rectangle.
3076  */
3077 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, Geom::Rect const &b, gboolean incremental)
3079     if (!incremental) {
3080         sp_nodepath_deselect(nodepath);
3081     }
3083     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3084        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3085         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3086            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3088             if (b.contains(node->pos)) {
3089                 sp_nodepath_node_select(node, TRUE, TRUE);
3090             }
3091         }
3092     }
3096 void
3097 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3099     g_assert (n);
3100     g_assert (nodepath);
3101     g_assert (n->subpath->nodepath == nodepath);
3103     if (g_list_length (nodepath->selected) == 0) {
3104         if (grow > 0) {
3105             sp_nodepath_node_select(n, TRUE, TRUE);
3106         }
3107         return;
3108     }
3110     if (g_list_length (nodepath->selected) == 1) {
3111         if (grow < 0) {
3112             sp_nodepath_deselect (nodepath);
3113             return;
3114         }
3115     }
3117         double n_sel_range = 0, p_sel_range = 0;
3118             Inkscape::NodePath::Node *farthest_n_node = n;
3119             Inkscape::NodePath::Node *farthest_p_node = n;
3121         // Calculate ranges
3122         {
3123             double n_range = 0, p_range = 0;
3124             bool n_going = true, p_going = true;
3125             Inkscape::NodePath::Node *n_node = n;
3126             Inkscape::NodePath::Node *p_node = n;
3127             do {
3128                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3129                 if (n_node && n_going)
3130                     n_node = n_node->n.other;
3131                 if (n_node == NULL) {
3132                     n_going = false;
3133                 } else {
3134                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3135                     if (n_node->selected) {
3136                         n_sel_range = n_range;
3137                         farthest_n_node = n_node;
3138                     }
3139                     if (n_node == p_node) {
3140                         n_going = false;
3141                         p_going = false;
3142                     }
3143                 }
3144                 if (p_node && p_going)
3145                     p_node = p_node->p.other;
3146                 if (p_node == NULL) {
3147                     p_going = false;
3148                 } else {
3149                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3150                     if (p_node->selected) {
3151                         p_sel_range = p_range;
3152                         farthest_p_node = p_node;
3153                     }
3154                     if (p_node == n_node) {
3155                         n_going = false;
3156                         p_going = false;
3157                     }
3158                 }
3159             } while (n_going || p_going);
3160         }
3162     if (grow > 0) {
3163         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3164                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3165         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3166                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3167         }
3168     } else {
3169         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3170                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3171         } else if (farthest_p_node && farthest_p_node->selected) {
3172                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3173         }
3174     }
3177 void
3178 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3180     g_assert (n);
3181     g_assert (nodepath);
3182     g_assert (n->subpath->nodepath == nodepath);
3184     if (g_list_length (nodepath->selected) == 0) {
3185         if (grow > 0) {
3186             sp_nodepath_node_select(n, TRUE, TRUE);
3187         }
3188         return;
3189     }
3191     if (g_list_length (nodepath->selected) == 1) {
3192         if (grow < 0) {
3193             sp_nodepath_deselect (nodepath);
3194             return;
3195         }
3196     }
3198     Inkscape::NodePath::Node *farthest_selected = NULL;
3199     double farthest_dist = 0;
3201     Inkscape::NodePath::Node *closest_unselected = NULL;
3202     double closest_dist = NR_HUGE;
3204     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3205        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3206         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3207            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3208            if (node == n)
3209                continue;
3210            if (node->selected) {
3211                if (Geom::L2(node->pos - n->pos) > farthest_dist) {
3212                    farthest_dist = Geom::L2(node->pos - n->pos);
3213                    farthest_selected = node;
3214                }
3215            } else {
3216                if (Geom::L2(node->pos - n->pos) < closest_dist) {
3217                    closest_dist = Geom::L2(node->pos - n->pos);
3218                    closest_unselected = node;
3219                }
3220            }
3221         }
3222     }
3224     if (grow > 0) {
3225         if (closest_unselected) {
3226             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3227         }
3228     } else {
3229         if (farthest_selected) {
3230             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3231         }
3232     }
3236 /**
3237 \brief  Saves all nodes' and handles' current positions in their origin members
3238 */
3239 void
3240 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3242     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3243        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3244         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3245            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3246            n->origin = n->pos;
3247            n->p.origin = n->p.pos;
3248            n->n.origin = n->n.pos;
3249         }
3250     }
3253 /**
3254 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3255 */
3256 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3258     GList *r = NULL;
3259     if (nodepath->selected) {
3260         guint i = 0;
3261         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3262             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3263             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3264                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3265                 i++;
3266                 if (node->selected) {
3267                     r = g_list_append(r, GINT_TO_POINTER(i));
3268                 }
3269             }
3270         }
3271     }
3272     return r;
3275 /**
3276 \brief  Restores selection by selecting nodes whose positions are in the list
3277 */
3278 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3280     sp_nodepath_deselect(nodepath);
3282     guint i = 0;
3283     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3284        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3285         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3286            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3287             i++;
3288             if (g_list_find(r, GINT_TO_POINTER(i))) {
3289                 sp_nodepath_node_select(node, TRUE, TRUE);
3290             }
3291         }
3292     }
3296 /**
3297 \brief Adjusts handle according to node type and line code.
3298 */
3299 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3301     g_assert(node);
3303     // nothing to do for auto nodes (sp_node_adjust_handles() does the job)
3304     if (node->type == Inkscape::NodePath::NODE_AUTO)
3305         return;
3307    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3308    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3310    // nothing to do if we are an end node
3311     if (me->other == NULL) return;
3312     if (other->other == NULL) return;
3314     // nothing to do if we are a cusp node
3315     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3317     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3318     NRPathcode mecode;
3319     if (which_adjust == 1) {
3320         mecode = (NRPathcode)me->other->code;
3321     } else {
3322         mecode = (NRPathcode)node->code;
3323     }
3324     if (mecode == NR_LINETO) return;
3326     if (sp_node_side_is_line(node, other)) {
3327         // other is a line, and we are either smooth or symm
3328        Inkscape::NodePath::Node *othernode = other->other;
3329         double len = Geom::L2(me->pos - node->pos);
3330         Geom::Point delta = node->pos - othernode->pos;
3331         double linelen = Geom::L2(delta);
3332         if (linelen < 1e-18)
3333             return;
3334         me->pos = node->pos + (len / linelen)*delta;
3335         return;
3336     }
3338     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3339         // symmetrize
3340         me->pos = 2 * node->pos - other->pos;
3341         return;
3342     } else {
3343         // smoothify
3344         double len = Geom::L2(me->pos - node->pos);
3345         Geom::Point delta = other->pos - node->pos;
3346         double otherlen = Geom::L2(delta);
3347         if (otherlen < 1e-18) return;
3348         me->pos = node->pos - (len / otherlen) * delta;
3349     }
3352 /**
3353  \brief Adjusts both handles according to node type and line code
3354  */
3355 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3357     g_assert(node);
3359     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3361     /* we are either smooth or symm */
3363     if (node->p.other == NULL) return;
3364     if (node->n.other == NULL) return;
3366     if (node->type == Inkscape::NodePath::NODE_AUTO) {
3367         sp_node_adjust_handles_auto(node);
3368         return;
3369     }
3371     if (sp_node_side_is_line(node, &node->p)) {
3372         sp_node_adjust_handle(node, 1);
3373         return;
3374     }
3376     if (sp_node_side_is_line(node, &node->n)) {
3377         sp_node_adjust_handle(node, -1);
3378         return;
3379     }
3381     /* both are curves */
3382     Geom::Point const delta( node->n.pos - node->p.pos );
3384     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3385         node->p.pos = node->pos - delta / 2;
3386         node->n.pos = node->pos + delta / 2;
3387         return;
3388     }
3390     /* We are smooth */
3391     double plen = Geom::L2(node->p.pos - node->pos);
3392     if (plen < 1e-18) return;
3393     double nlen = Geom::L2(node->n.pos - node->pos);
3394     if (nlen < 1e-18) return;
3395     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3396     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3399 static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node)
3401     if (node->p.other == NULL || node->n.other == NULL) {
3402         node->p.pos = node->pos;
3403         node->n.pos = node->pos;
3404         return;
3405     }
3407     Geom::Point leg_prev = to_2geom(node->p.other->pos - node->pos);
3408     Geom::Point leg_next = to_2geom(node->n.other->pos - node->pos);
3410     double norm_leg_prev = Geom::L2(leg_prev);
3411     double norm_leg_next = Geom::L2(leg_next);
3413     Geom::Point delta;
3414     if (norm_leg_next > 0.0) {
3415         delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
3416         delta.normalize();
3417     }
3419     node->p.pos = node->pos - norm_leg_prev / 3 * delta;
3420     node->n.pos = node->pos + norm_leg_next / 3 * delta;
3423 /**
3424  * Node event callback.
3425  */
3426 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3428     gboolean ret = FALSE;
3429     switch (event->type) {
3430         case GDK_ENTER_NOTIFY:
3431             Inkscape::NodePath::Path::active_node = n;
3432             break;
3433         case GDK_LEAVE_NOTIFY:
3434             Inkscape::NodePath::Path::active_node = NULL;
3435             break;
3436         case GDK_SCROLL:
3437             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3438                 switch (event->scroll.direction) {
3439                     case GDK_SCROLL_UP:
3440                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3441                         break;
3442                     case GDK_SCROLL_DOWN:
3443                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3444                         break;
3445                     default:
3446                         break;
3447                 }
3448                 ret = TRUE;
3449             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3450                 switch (event->scroll.direction) {
3451                     case GDK_SCROLL_UP:
3452                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3453                         break;
3454                     case GDK_SCROLL_DOWN:
3455                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3456                         break;
3457                     default:
3458                         break;
3459                 }
3460                 ret = TRUE;
3461             }
3462             break;
3463         case GDK_KEY_PRESS:
3464             switch (get_group0_keyval (&event->key)) {
3465                 case GDK_space:
3466                     if (event->key.state & GDK_BUTTON1_MASK) {
3467                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3468                         stamp_repr(nodepath);
3469                         ret = TRUE;
3470                     }
3471                     break;
3472                 case GDK_Page_Up:
3473                     if (event->key.state & GDK_CONTROL_MASK) {
3474                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3475                     } else {
3476                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3477                     }
3478                     break;
3479                 case GDK_Page_Down:
3480                     if (event->key.state & GDK_CONTROL_MASK) {
3481                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3482                     } else {
3483                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3484                     }
3485                     break;
3486                 default:
3487                     break;
3488             }
3489             break;
3490         default:
3491             break;
3492     }
3494     return ret;
3497 /**
3498  * Handle keypress on node; directly called.
3499  */
3500 gboolean node_key(GdkEvent *event)
3502     Inkscape::NodePath::Path *np;
3504     // there is no way to verify nodes so set active_node to nil when deleting!!
3505     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3507     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3508         gint ret = FALSE;
3509         switch (get_group0_keyval (&event->key)) {
3510             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3511             case GDK_BackSpace:
3512                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3513                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3514                 sp_nodepath_update_repr(np, _("Delete node"));
3515                 Inkscape::NodePath::Path::active_node = NULL;
3516                 ret = TRUE;
3517                 break;
3518             case GDK_c:
3519                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3520                 ret = TRUE;
3521                 break;
3522             case GDK_s:
3523                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3524                 ret = TRUE;
3525                 break;
3526             case GDK_a:
3527                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_AUTO);
3528                 ret = TRUE;
3529                 break;
3530             case GDK_y:
3531                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3532                 ret = TRUE;
3533                 break;
3534             case GDK_b:
3535                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3536                 ret = TRUE;
3537                 break;
3538         }
3539         return ret;
3540     }
3541     return FALSE;
3544 /**
3545  * Mouseclick on node callback.
3546  */
3547 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3549    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3551     if (state & GDK_CONTROL_MASK) {
3552         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3554         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3555             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3556                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3557             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3558                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3559             } else if (n->type == Inkscape::NodePath::NODE_SYMM) {
3560                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_AUTO);
3561             } else {
3562                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3563             }
3564             sp_nodepath_update_repr(nodepath, _("Change node type"));
3565             sp_nodepath_update_statusbar(nodepath);
3567         } else { //ctrl+alt+click: delete node
3568             GList *node_to_delete = NULL;
3569             node_to_delete = g_list_append(node_to_delete, n);
3570             sp_node_delete_preserve(node_to_delete);
3571         }
3573     } else {
3574         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3575     }
3578 /**
3579  * Mouse grabbed node callback.
3580  */
3581 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3583    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3585     if (!n->selected) {
3586         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3587     }
3589     n->is_dragging = true;
3590     //sp_canvas_set_snap_delay_active(n->subpath->nodepath->desktop->canvas, true);
3591     // Reconstruct and store the location of the mouse pointer at the time when we started dragging (needed for snapping)
3592     n->subpath->nodepath->drag_origin_mouse = knot->grabbed_rel_pos + knot->drag_origin;
3594     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3596     sp_nodepath_remember_origins (n->subpath->nodepath);
3599 /**
3600  * Mouse ungrabbed node callback.
3601  */
3602 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3604    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3606    n->dragging_out = NULL;
3607    n->is_dragging = false;
3608    //sp_canvas_set_snap_delay_active(n->subpath->nodepath->desktop->canvas, false);
3609    n->subpath->nodepath->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
3610    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3612    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3615 /**
3616  * The point on a line, given by its angle, closest to the given point.
3617  * \param p  A point.
3618  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3619  * \param closest  Pointer to the point struct where the result is stored.
3620  * \todo FIXME: use dot product perhaps?
3621  */
3622 static void point_line_closest(Geom::Point *p, double a, Geom::Point *closest)
3624     if (a == HUGE_VAL) { // vertical
3625         *closest = Geom::Point(0, (*p)[Geom::Y]);
3626     } else {
3627         (*closest)[Geom::X] = ( a * (*p)[Geom::Y] + (*p)[Geom::X]) / (a*a + 1);
3628         (*closest)[Geom::Y] = a * (*closest)[Geom::X];
3629     }
3632 /**
3633  * Distance from the point to a line given by its angle.
3634  * \param p  A point.
3635  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3636  */
3637 static double point_line_distance(Geom::Point *p, double a)
3639     Geom::Point c;
3640     point_line_closest(p, a, &c);
3641     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]));
3644 /**
3645  * Callback for node "request" signal.
3646  * \todo fixme: This goes to "moved" event? (lauris)
3647  */
3648 static gboolean
3649 node_request(SPKnot */*knot*/, Geom::Point const &p, guint state, gpointer data)
3651     double yn, xn, yp, xp;
3652     double an, ap, na, pa;
3653     double d_an, d_ap, d_na, d_pa;
3654     gboolean collinear = FALSE;
3655     Geom::Point c;
3656     Geom::Point pr;
3658     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3660     n->subpath->nodepath->desktop->snapindicator->remove_snaptarget();
3662     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3663     if ( (!n->subpath->nodepath->straight_path) &&
3664          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3665            || n->dragging_out ) )
3666     {
3667        Geom::Point mouse = p;
3669        if (!n->dragging_out) {
3670            // This is the first drag-out event; find out which handle to drag out
3671            double appr_n = (n->n.other ? Geom::L2(n->n.other->pos - n->pos) - Geom::L2(n->n.other->pos - p) : -HUGE_VAL);
3672            double appr_p = (n->p.other ? Geom::L2(n->p.other->pos - n->pos) - Geom::L2(n->p.other->pos - p) : -HUGE_VAL);
3674            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3675                return FALSE;
3677            Inkscape::NodePath::NodeSide *opposite;
3678            if (appr_p > appr_n) { // closer to p
3679                n->dragging_out = &n->p;
3680                opposite = &n->n;
3681                n->code = NR_CURVETO;
3682            } else if (appr_p < appr_n) { // closer to n
3683                n->dragging_out = &n->n;
3684                opposite = &n->p;
3685                n->n.other->code = NR_CURVETO;
3686            } else { // p and n nodes are the same
3687                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3688                    n->dragging_out = &n->p;
3689                    opposite = &n->n;
3690                    n->code = NR_CURVETO;
3691                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3692                    n->dragging_out = &n->n;
3693                    opposite = &n->p;
3694                    n->n.other->code = NR_CURVETO;
3695                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3696                    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);
3697                    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);
3698                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3699                        n->dragging_out = &n->n;
3700                        opposite = &n->p;
3701                        n->n.other->code = NR_CURVETO;
3702                    } else { // closer to other's n handle
3703                        n->dragging_out = &n->p;
3704                        opposite = &n->n;
3705                        n->code = NR_CURVETO;
3706                    }
3707                }
3708            }
3710            // if there's another handle, make sure the one we drag out starts parallel to it
3711            if (opposite->pos != n->pos) {
3712                mouse = n->pos - Geom::L2(mouse - n->pos) * Geom::unit_vector(opposite->pos - n->pos);
3713            }
3715            // knots might not be created yet!
3716            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3717            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3718        }
3720        // pass this on to the handle-moved callback
3721        node_handle_moved(n->dragging_out->knot, mouse, state, (gpointer) n);
3722        sp_node_update_handles(n);
3723        return TRUE;
3724    }
3726     if (state & GDK_CONTROL_MASK) { // constrained motion
3728         // calculate relative distances of handles
3729         // n handle:
3730         yn = n->n.pos[Geom::Y] - n->pos[Geom::Y];
3731         xn = n->n.pos[Geom::X] - n->pos[Geom::X];
3732         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3733         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3734             if (n->n.other) { // if there is the next point
3735                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3736                     yn = n->n.other->origin[Geom::Y] - n->origin[Geom::Y]; // use origin because otherwise the direction will change as you drag
3737                     xn = n->n.other->origin[Geom::X] - n->origin[Geom::X];
3738             }
3739         }
3740         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3741         if (yn < 0) { xn = -xn; yn = -yn; }
3743         // p handle:
3744         yp = n->p.pos[Geom::Y] - n->pos[Geom::Y];
3745         xp = n->p.pos[Geom::X] - n->pos[Geom::X];
3746         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3747         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3748             if (n->p.other) {
3749                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3750                     yp = n->p.other->origin[Geom::Y] - n->origin[Geom::Y];
3751                     xp = n->p.other->origin[Geom::X] - n->origin[Geom::X];
3752             }
3753         }
3754         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3755         if (yp < 0) { xp = -xp; yp = -yp; }
3757         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3758             // sliding on handles, only if at least one of the handles is non-vertical
3759             // (otherwise it's the same as ctrl+drag anyway)
3761             // calculate angles of the handles
3762             if (xn == 0) {
3763                 if (yn == 0) { // no handle, consider it the continuation of the other one
3764                     an = 0;
3765                     collinear = TRUE;
3766                 }
3767                 else an = 0; // vertical; set the angle to horizontal
3768             } else an = yn/xn;
3770             if (xp == 0) {
3771                 if (yp == 0) { // no handle, consider it the continuation of the other one
3772                     ap = an;
3773                 }
3774                 else ap = 0; // vertical; set the angle to horizontal
3775             } else  ap = yp/xp;
3777             if (collinear) an = ap;
3779             // angles of the perpendiculars; HUGE_VAL means vertical
3780             if (an == 0) na = HUGE_VAL; else na = -1/an;
3781             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3783             // mouse point relative to the node's original pos
3784             pr = p - n->origin;
3786             // distances to the four lines (two handles and two perpendiculars)
3787             d_an = point_line_distance(&pr, an);
3788             d_na = point_line_distance(&pr, na);
3789             d_ap = point_line_distance(&pr, ap);
3790             d_pa = point_line_distance(&pr, pa);
3792             // find out which line is the closest, save its closest point in c
3793             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3794                 point_line_closest(&pr, an, &c);
3795             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3796                 point_line_closest(&pr, ap, &c);
3797             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3798                 point_line_closest(&pr, na, &c);
3799             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3800                 point_line_closest(&pr, pa, &c);
3801             }
3803             // move the node to the closest point
3804             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3805                                             n->origin[Geom::X] + c[Geom::X] - n->pos[Geom::X],
3806                                             n->origin[Geom::Y] + c[Geom::Y] - n->pos[Geom::Y],
3807                                             true);
3809         } else {  // constraining to hor/vert
3811             if (fabs(p[Geom::X] - n->origin[Geom::X]) > fabs(p[Geom::Y] - n->origin[Geom::Y])) { // snap to hor
3812                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3813                                                 p[Geom::X] - n->pos[Geom::X],
3814                                                 n->origin[Geom::Y] - n->pos[Geom::Y],
3815                                                 true,
3816                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::X]));
3817             } else { // snap to vert
3818                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3819                                                 n->origin[Geom::X] - n->pos[Geom::X],
3820                                                 p[Geom::Y] - n->pos[Geom::Y],
3821                                                 true,
3822                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::Y]));
3823             }
3824         }
3825     } else { // move freely
3826         if (n->is_dragging) {
3827             if (state & GDK_MOD1_MASK) { // sculpt
3828                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, p - n->origin);
3829             } else {
3830                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3831                                             p[Geom::X] - n->pos[Geom::X],
3832                                             p[Geom::Y] - n->pos[Geom::Y],
3833                                             (state & GDK_SHIFT_MASK) == 0);
3834             }
3835         }
3836     }
3838     n->subpath->nodepath->desktop->scroll_to_point(p);
3840     return TRUE;
3843 /**
3844  * Node handle clicked callback.
3845  */
3846 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3848    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3850     if (state & GDK_CONTROL_MASK) { // "delete" handle
3851         if (n->p.knot == knot) {
3852             n->p.pos = n->pos;
3853         } else if (n->n.knot == knot) {
3854             n->n.pos = n->pos;
3855         }
3856         sp_node_update_handles(n);
3857         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3858         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3859         sp_nodepath_update_statusbar(nodepath);
3861     } else { // just select or add to selection, depending in Shift
3862         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3863     }
3866 /**
3867  * Node handle grabbed callback.
3868  */
3869 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3871    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3873     // convert auto -> smooth when dragging handle
3874    if (n->type == Inkscape::NodePath::NODE_AUTO) {
3875         n->type = Inkscape::NodePath::NODE_SMOOTH;
3876         sp_nodepath_update_node_knot (n);
3877    }
3879     if (!n->selected) {
3880         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3881     }
3883     // remember the origin point of the handle
3884     if (n->p.knot == knot) {
3885         n->p.origin_radial = n->p.pos - n->pos;
3886     } else if (n->n.knot == knot) {
3887         n->n.origin_radial = n->n.pos - n->pos;
3888     } else {
3889         g_assert_not_reached();
3890     }
3892     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3895 /**
3896  * Node handle ungrabbed callback.
3897  */
3898 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3900    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3902     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3903     if (n->p.knot == knot) {
3904         n->p.origin_radial.a = 0;
3905         sp_knot_set_position(knot, n->p.pos, state);
3906     } else if (n->n.knot == knot) {
3907         n->n.origin_radial.a = 0;
3908         sp_knot_set_position(knot, n->n.pos, state);
3909     } else {
3910         g_assert_not_reached();
3911     }
3913     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3916 /**
3917  * Node handle "request" signal callback.
3918  */
3919 static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, gpointer data)
3921     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3923     Inkscape::NodePath::NodeSide *me, *opposite;
3924     gint which;
3925     if (n->p.knot == knot) {
3926         me = &n->p;
3927         opposite = &n->n;
3928         which = -1;
3929     } else if (n->n.knot == knot) {
3930         me = &n->n;
3931         opposite = &n->p;
3932         which = 1;
3933     } else {
3934         me = opposite = NULL;
3935         which = 0;
3936         g_assert_not_reached();
3937     }
3939     SPDesktop *desktop = n->subpath->nodepath->desktop;
3940     SnapManager &m = desktop->namedview->snap_manager;
3941     m.setup(desktop, true, n->subpath->nodepath->item);
3942     Inkscape::SnappedPoint s;
3944     if ((state & GDK_SHIFT_MASK) != 0) {
3945         // We will not try to snap when the shift-key is pressed
3946         // so remove the old snap indicator and don't wait for it to time-out
3947         desktop->snapindicator->remove_snaptarget();
3948     }
3950     Inkscape::NodePath::Node *othernode = opposite->other;
3951     Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP);
3952     if (othernode) {
3953         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3954             /* We are smooth node adjacent with line */
3955             Geom::Point const delta = p - n->pos;
3956             Geom::Coord const len = Geom::L2(delta);
3957             Inkscape::NodePath::Node *othernode = opposite->other;
3958             Geom::Point const ndelta = n->pos - othernode->pos;
3959             Geom::Coord const linelen = Geom::L2(ndelta);
3960             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3961                 Geom::Coord const scal = dot(delta, ndelta) / linelen;
3962                 p = n->pos + (scal / linelen) * ndelta;
3963             }
3964             if ((state & GDK_SHIFT_MASK) == 0) {
3965                 s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type, Inkscape::Snapper::ConstraintLine(p, ndelta));
3966             }
3967         } else {
3968             if ((state & GDK_SHIFT_MASK) == 0) {
3969                 s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type);
3970             }
3971         }
3972     } else {
3973         if ((state & GDK_SHIFT_MASK) == 0) {
3974             s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type);
3975         }
3976     }
3978     s.getPoint(p);
3980     sp_node_adjust_handle(n, -which);
3982     return FALSE;
3985 /**
3986  * Node handle moved callback.
3987  */
3988 static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data)
3990    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3991    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3993    Inkscape::NodePath::NodeSide *me;
3994    Inkscape::NodePath::NodeSide *other;
3995     if (n->p.knot == knot) {
3996         me = &n->p;
3997         other = &n->n;
3998     } else if (n->n.knot == knot) {
3999         me = &n->n;
4000         other = &n->p;
4001     } else {
4002         me = NULL;
4003         other = NULL;
4004         g_assert_not_reached();
4005     }
4007     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
4008     Radial rme(me->pos - n->pos);
4009     Radial rother(other->pos - n->pos);
4010     Radial rnew(p - n->pos);
4012     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
4013         int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
4014         /* 0 interpreted as "no snapping". */
4016         // 1. Snap to the closest PI/snaps angle, starting from zero.
4017         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
4019         // 2. Snap to the original angle, its opposite and perpendiculars
4020         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
4021             /* The closest PI/2 angle, starting from original angle */
4022             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
4024             // Snap to the closest.
4025             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
4026                        ? a_snapped
4027                        : a_ortho );
4028         }
4030         // 3. Snap to the angle of the opposite line, if any
4031         Inkscape::NodePath::Node *othernode = other->other;
4032         if (othernode) {
4033             Geom::Point other_to_snap(0,0);
4034             if (sp_node_side_is_line(n, other)) {
4035                 other_to_snap = othernode->pos - n->pos;
4036             } else {
4037                 other_to_snap = other->pos - n->pos;
4038             }
4039             if (Geom::L2(other_to_snap) > 1e-3) {
4040                 Radial rother_to_snap(other_to_snap);
4041                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
4042                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
4044                 // Snap to the closest.
4045                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
4046                        ? a_snapped
4047                        : a_oppo );
4048             }
4049         }
4051         rnew.a = a_snapped;
4052     }
4054     if (state & GDK_MOD1_MASK) {
4055         // lock handle length
4056         rnew.r = me->origin_radial.r;
4057     }
4059     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (!n->dragging_out && (state & GDK_SHIFT_MASK)))
4060         && (rme.a != HUGE_VAL) && (rnew.a != HUGE_VAL) && ((fabs(rme.a - rnew.a) > 0.001) || (n->type ==Inkscape::NodePath::NODE_SYMM))) {
4061         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
4062         rother.a += rnew.a - rme.a;
4063         other->pos = Geom::Point(rother) + n->pos;
4064         if (other->knot) {
4065             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
4066             sp_knot_moveto(other->knot, other->pos);
4067         }
4068     }
4070     me->pos = Geom::Point(rnew) + n->pos;
4071     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
4073     // move knot, but without emitting the signal:
4074     // we cannot emit a "moved" signal because we're now processing it
4075     sp_knot_moveto(me->knot, me->pos);
4077     update_object(n->subpath->nodepath);
4079     /* status text */
4080     SPDesktop *desktop = n->subpath->nodepath->desktop;
4081     if (!desktop) return;
4082     SPEventContext *ec = desktop->event_context;
4083     if (!ec) return;
4085     Inkscape::MessageContext *mc = get_message_context(ec);
4087     if (!mc) return;
4089     double degrees = 180 / M_PI * rnew.a;
4090     if (degrees > 180) degrees -= 360;
4091     if (degrees < -180) degrees += 360;
4092     if (prefs->getBool("/options/compassangledisplay/value"))
4093         degrees = angle_to_compass (degrees);
4095     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
4097     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
4098          _("<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);
4100     g_string_free(length, TRUE);
4103 /**
4104  * Node handle event callback.
4105  */
4106 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
4108     gboolean ret = FALSE;
4109     switch (event->type) {
4110         case GDK_KEY_PRESS:
4111             switch (get_group0_keyval (&event->key)) {
4112                 case GDK_space:
4113                     if (event->key.state & GDK_BUTTON1_MASK) {
4114                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
4115                         stamp_repr(nodepath);
4116                         ret = TRUE;
4117                     }
4118                     break;
4119                 default:
4120                     break;
4121             }
4122             break;
4123         case GDK_ENTER_NOTIFY:
4124             // we use an experimentally determined threshold that seems to work fine
4125             if (Geom::L2(n->pos - knot->pos) < 0.75)
4126                 Inkscape::NodePath::Path::active_node = n;
4127             break;
4128         case GDK_LEAVE_NOTIFY:
4129             // we use an experimentally determined threshold that seems to work fine
4130             if (Geom::L2(n->pos - knot->pos) < 0.75)
4131                 Inkscape::NodePath::Path::active_node = NULL;
4132             break;
4133         default:
4134             break;
4135     }
4137     return ret;
4140 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
4141                                  Radial &rme, Radial &rother, gboolean const both)
4143     rme.a += angle;
4144     if ( both
4145          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4146          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4147     {
4148         rother.a += angle;
4149     }
4152 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
4153                                         Radial &rme, Radial &rother, gboolean const both)
4155     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
4157     gdouble r;
4158     if ( both
4159          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4160          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4161     {
4162         r = MAX(rme.r, rother.r);
4163     } else {
4164         r = rme.r;
4165     }
4167     gdouble const weird_angle = atan2(norm_angle, r);
4168 /* Bulia says norm_angle is just the visible distance that the
4169  * object's end must travel on the screen.  Left as 'angle' for want of
4170  * a better name.*/
4172     rme.a += weird_angle;
4173     if ( both
4174          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4175          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4176     {
4177         rother.a += weird_angle;
4178     }
4181 /**
4182  * Rotate one node.
4183  */
4184 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4186     Inkscape::NodePath::NodeSide *me, *other;
4187     bool both = false;
4189     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4190     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4192     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4193         me = &(n->p);
4194         other = &(n->n);
4195     } else if (!n->p.other) {
4196         me = &(n->n);
4197         other = &(n->p);
4198     } else {
4199         if (which > 0) { // right handle
4200             if (xn > xp) {
4201                 me = &(n->n);
4202                 other = &(n->p);
4203             } else {
4204                 me = &(n->p);
4205                 other = &(n->n);
4206             }
4207         } else if (which < 0){ // left handle
4208             if (xn <= xp) {
4209                 me = &(n->n);
4210                 other = &(n->p);
4211             } else {
4212                 me = &(n->p);
4213                 other = &(n->n);
4214             }
4215         } else { // both handles
4216             me = &(n->n);
4217             other = &(n->p);
4218             both = true;
4219         }
4220     }
4222     Radial rme(me->pos - n->pos);
4223     Radial rother(other->pos - n->pos);
4225     if (screen) {
4226         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4227     } else {
4228         node_rotate_one_internal (*n, angle, rme, rother, both);
4229     }
4231     me->pos = n->pos + Geom::Point(rme);
4233     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4234         other->pos =  n->pos + Geom::Point(rother);
4235     }
4237     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4238     // so here we just move all the knots without emitting move signals, for speed
4239     sp_node_update_handles(n, false);
4242 /**
4243  * Rotate selected nodes.
4244  */
4245 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4247     if (!nodepath || !nodepath->selected) return;
4249     if (g_list_length(nodepath->selected) == 1) {
4250        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4251         node_rotate_one (n, angle, which, screen);
4252     } else {
4253        // rotate as an object:
4255         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4256         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4257         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4258             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4259             box.expandTo (n->pos); // contain all selected nodes
4260         }
4262         gdouble rot;
4263         if (screen) {
4264             gdouble const zoom = nodepath->desktop->current_zoom();
4265             gdouble const zmove = angle / zoom;
4266             gdouble const r = Geom::L2(box.max() - box.midpoint());
4267             rot = atan2(zmove, r);
4268         } else {
4269             rot = angle;
4270         }
4272         Geom::Point rot_center;
4273         if (Inkscape::NodePath::Path::active_node == NULL)
4274             rot_center = box.midpoint();
4275         else
4276             rot_center = Inkscape::NodePath::Path::active_node->pos;
4278         Geom::Matrix t =
4279             Geom::Matrix (Geom::Translate(-rot_center)) *
4280             Geom::Matrix (Geom::Rotate(rot)) *
4281             Geom::Matrix (Geom::Translate(rot_center));
4283         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4284             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4285             n->pos *= t;
4286             n->n.pos *= t;
4287             n->p.pos *= t;
4288             sp_node_update_handles(n, false);
4289         }
4290     }
4292     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4295 /**
4296  * Scale one node.
4297  */
4298 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4300     bool both = false;
4301     Inkscape::NodePath::NodeSide *me, *other;
4303     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4304     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4306     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4307         me = &(n->p);
4308         other = &(n->n);
4309         n->code = NR_CURVETO;
4310     } else if (!n->p.other) {
4311         me = &(n->n);
4312         other = &(n->p);
4313         if (n->n.other)
4314             n->n.other->code = NR_CURVETO;
4315     } else {
4316         if (which > 0) { // right handle
4317             if (xn > xp) {
4318                 me = &(n->n);
4319                 other = &(n->p);
4320                 if (n->n.other)
4321                     n->n.other->code = NR_CURVETO;
4322             } else {
4323                 me = &(n->p);
4324                 other = &(n->n);
4325                 n->code = NR_CURVETO;
4326             }
4327         } else if (which < 0){ // left handle
4328             if (xn <= xp) {
4329                 me = &(n->n);
4330                 other = &(n->p);
4331                 if (n->n.other)
4332                     n->n.other->code = NR_CURVETO;
4333             } else {
4334                 me = &(n->p);
4335                 other = &(n->n);
4336                 n->code = NR_CURVETO;
4337             }
4338         } else { // both handles
4339             me = &(n->n);
4340             other = &(n->p);
4341             both = true;
4342             n->code = NR_CURVETO;
4343             if (n->n.other)
4344                 n->n.other->code = NR_CURVETO;
4345         }
4346     }
4348     Radial rme(me->pos - n->pos);
4349     Radial rother(other->pos - n->pos);
4351     rme.r += grow;
4352     if (rme.r < 0) rme.r = 0;
4353     if (rme.a == HUGE_VAL) {
4354         if (me->other) { // if direction is unknown, initialize it towards the next node
4355             Radial rme_next(me->other->pos - n->pos);
4356             rme.a = rme_next.a;
4357         } else { // if there's no next, initialize to 0
4358             rme.a = 0;
4359         }
4360     }
4361     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4362         rother.r += grow;
4363         if (rother.r < 0) rother.r = 0;
4364         if (rother.a == HUGE_VAL) {
4365             rother.a = rme.a + M_PI;
4366         }
4367     }
4369     me->pos = n->pos + Geom::Point(rme);
4371     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4372         other->pos = n->pos + Geom::Point(rother);
4373     }
4375     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4376     // so here we just move all the knots without emitting move signals, for speed
4377     sp_node_update_handles(n, false);
4380 /**
4381  * Scale selected nodes.
4382  */
4383 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4385     if (!nodepath || !nodepath->selected) return;
4387     if (g_list_length(nodepath->selected) == 1) {
4388         // scale handles of the single selected node
4389         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4390         node_scale_one (n, grow, which);
4391     } else {
4392         // scale nodes as an "object":
4394         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4395         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4396         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4397             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4398             box.expandTo (n->pos); // contain all selected nodes
4399         }
4401         if ( Geom::are_near(box.maxExtent(), 0) ) {
4402             SPEventContext *ec = nodepath->desktop->event_context;
4403             if (!ec) return;
4404             Inkscape::MessageContext *mc = get_message_context(ec);
4405             if (!mc) return;
4406             mc->setF(Inkscape::WARNING_MESSAGE,
4407                              _("Cannot scale nodes when all are at the same location."));
4408             return;
4409         }
4410         double scale = (box.maxExtent() + grow)/box.maxExtent();
4413         Geom::Point scale_center;
4414         if (Inkscape::NodePath::Path::active_node == NULL)
4415             scale_center = box.midpoint();
4416         else
4417             scale_center = Inkscape::NodePath::Path::active_node->pos;
4419         Geom::Matrix t =
4420             Geom::Translate(-scale_center) *
4421             Geom::Scale(scale, scale) *
4422             Geom::Translate(scale_center);
4424         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4425             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4426             n->pos *= t;
4427             n->n.pos *= t;
4428             n->p.pos *= t;
4429             sp_node_update_handles(n, false);
4430         }
4431     }
4433     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4436 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4438     if (!nodepath) return;
4439     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4442 /**
4443  * Flip selected nodes horizontally/vertically.
4444  */
4445 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis, boost::optional<Geom::Point> center)
4447     if (!nodepath || !nodepath->selected) return;
4449     if (g_list_length(nodepath->selected) == 1 && !center) {
4450         // flip handles of the single selected node
4451         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4452         double temp = n->p.pos[axis];
4453         n->p.pos[axis] = n->n.pos[axis];
4454         n->n.pos[axis] = temp;
4455         sp_node_update_handles(n, false);
4456     } else {
4457         // scale nodes as an "object":
4459         Geom::Rect box = sp_node_selected_bbox (nodepath);
4460         if (!center) {
4461             center = box.midpoint();
4462         }
4463         Geom::Matrix t =
4464             Geom::Matrix (Geom::Translate(- *center)) *
4465             Geom::Matrix ((axis == Geom::X)? Geom::Scale(-1, 1) : Geom::Scale(1, -1)) *
4466             Geom::Matrix (Geom::Translate(*center));
4468         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4469             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4470             n->pos *= t;
4471             n->n.pos *= t;
4472             n->p.pos *= t;
4473             sp_node_update_handles(n, false);
4474         }
4475     }
4477     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4480 Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4482     g_assert (nodepath->selected);
4484     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4485     Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4486     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4487         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4488         box.expandTo (n->pos); // contain all selected nodes
4489     }
4490     return box;
4493 //-----------------------------------------------
4494 /**
4495  * Return new subpath under given nodepath.
4496  */
4497 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4499     g_assert(nodepath);
4500     g_assert(nodepath->desktop);
4502    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4504     s->nodepath = nodepath;
4505     s->closed = FALSE;
4506     s->nodes = NULL;
4507     s->first = NULL;
4508     s->last = NULL;
4510     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4511     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4512     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4514     return s;
4517 /**
4518  * Destroy nodes in subpath, then subpath itself.
4519  */
4520 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4522     g_assert(subpath);
4523     g_assert(subpath->nodepath);
4524     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4526     while (subpath->nodes) {
4527         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4528     }
4530     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4532     g_free(subpath);
4535 /**
4536  * Link head to tail in subpath.
4537  */
4538 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4540     g_assert(!sp->closed);
4541     g_assert(sp->last != sp->first);
4542     g_assert(sp->first->code == NR_MOVETO);
4544     sp->closed = TRUE;
4546     //Link the head to the tail
4547     sp->first->p.other = sp->last;
4548     sp->last->n.other  = sp->first;
4549     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4550     sp->first          = sp->last;
4552     //Remove the extra end node
4553     sp_nodepath_node_destroy(sp->last->n.other);
4556 /**
4557  * Open closed (loopy) subpath at node.
4558  */
4559 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4561     g_assert(sp->closed);
4562     g_assert(n->subpath == sp);
4563     g_assert(sp->first == sp->last);
4565     /* We create new startpoint, current node will become last one */
4567    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4568                                                 &n->pos, &n->pos, &n->n.pos);
4571     sp->closed        = FALSE;
4573     //Unlink to make a head and tail
4574     sp->first         = new_path;
4575     sp->last          = n;
4576     n->n.other        = NULL;
4577     new_path->p.other = NULL;
4580 /**
4581  * Return new node in subpath with given properties.
4582  * \param pos Position of node.
4583  * \param ppos Handle position in previous direction
4584  * \param npos Handle position in previous direction
4585  */
4586 Inkscape::NodePath::Node *
4587 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)
4589     g_assert(sp);
4590     g_assert(sp->nodepath);
4591     g_assert(sp->nodepath->desktop);
4593     if (nodechunk == NULL)
4594         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4596     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4598     n->subpath  = sp;
4600     if (type != Inkscape::NodePath::NODE_NONE) {
4601         // use the type from sodipodi:nodetypes
4602         n->type = type;
4603     } else {
4604         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4605             // points are (almost) collinear
4606             if (Geom::L2(*pos - *ppos) < 1e-6 || Geom::L2(*pos - *npos) < 1e-6) {
4607                 // endnode, or a node with a retracted handle
4608                 n->type = Inkscape::NodePath::NODE_CUSP;
4609             } else {
4610                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4611             }
4612         } else {
4613             n->type = Inkscape::NodePath::NODE_CUSP;
4614         }
4615     }
4617     n->code     = code;
4618     n->selected = FALSE;
4619     n->pos      = *pos;
4620     n->p.pos    = *ppos;
4621     n->n.pos    = *npos;
4623     n->dragging_out = NULL;
4625     Inkscape::NodePath::Node *prev;
4626     if (next) {
4627         //g_assert(g_list_find(sp->nodes, next));
4628         prev = next->p.other;
4629     } else {
4630         prev = sp->last;
4631     }
4633     if (prev)
4634         prev->n.other = n;
4635     else
4636         sp->first = n;
4638     if (next)
4639         next->p.other = n;
4640     else
4641         sp->last = n;
4643     n->p.other = prev;
4644     n->n.other = next;
4646     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"));
4647     sp_knot_set_position(n->knot, *pos, 0);
4649     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4650     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4651     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4653     sp_nodepath_update_node_knot(n);
4655     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4656     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4657     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4658     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4659     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4660     sp_knot_show(n->knot);
4662     // We only create handle knots and lines on demand
4663     n->p.knot = NULL;
4664     n->p.line = NULL;
4665     n->n.knot = NULL;
4666     n->n.line = NULL;
4668     sp->nodes = g_list_prepend(sp->nodes, n);
4670     return n;
4673 /**
4674  * Destroy node and its knots, link neighbors in subpath.
4675  */
4676 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4678     g_assert(node);
4679     g_assert(node->subpath);
4680     g_assert(SP_IS_KNOT(node->knot));
4682    Inkscape::NodePath::SubPath *sp = node->subpath;
4684     if (node->selected) { // first, deselect
4685         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4686         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4687     }
4689     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4691     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4692     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4693     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4694     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4695     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4696     g_object_unref(G_OBJECT(node->knot));
4698     if (node->p.knot) {
4699         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4700         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4701         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4702         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4703         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4704         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4705         g_object_unref(G_OBJECT(node->p.knot));
4706         node->p.knot = NULL;
4707     }
4709     if (node->n.knot) {
4710         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4711         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4712         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4713         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4714         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4715         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4716         g_object_unref(G_OBJECT(node->n.knot));
4717         node->n.knot = NULL;
4718     }
4720     if (node->p.line)
4721         gtk_object_destroy(GTK_OBJECT(node->p.line));
4722     if (node->n.line)
4723         gtk_object_destroy(GTK_OBJECT(node->n.line));
4725     if (sp->nodes) { // there are others nodes on the subpath
4726         if (sp->closed) {
4727             if (sp->first == node) {
4728                 g_assert(sp->last == node);
4729                 sp->first = node->n.other;
4730                 sp->last = sp->first;
4731             }
4732             node->p.other->n.other = node->n.other;
4733             node->n.other->p.other = node->p.other;
4734         } else {
4735             if (sp->first == node) {
4736                 sp->first = node->n.other;
4737                 sp->first->code = NR_MOVETO;
4738             }
4739             if (sp->last == node) sp->last = node->p.other;
4740             if (node->p.other) node->p.other->n.other = node->n.other;
4741             if (node->n.other) node->n.other->p.other = node->p.other;
4742         }
4743     } else { // this was the last node on subpath
4744         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4745     }
4747     g_mem_chunk_free(nodechunk, node);
4750 /**
4751  * Returns one of the node's two sides.
4752  * \param which Indicates which side.
4753  * \return Pointer to previous node side if which==-1, next if which==1.
4754  */
4755 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4757     g_assert(node);
4758     Inkscape::NodePath::NodeSide * result = 0;
4759     switch (which) {
4760         case -1:
4761             result = &node->p;
4762             break;
4763         case 1:
4764             result = &node->n;
4765             break;
4766         default:
4767             g_assert_not_reached();
4768     }
4770     return result;
4773 /**
4774  * Return the other side of the node, given one of its sides.
4775  */
4776 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4778     g_assert(node);
4779     Inkscape::NodePath::NodeSide *result = 0;
4781     if (me == &node->p) {
4782         result = &node->n;
4783     } else if (me == &node->n) {
4784         result = &node->p;
4785     } else {
4786         g_assert_not_reached();
4787     }
4789     return result;
4792 /**
4793  * Return NRPathcode on the given side of the node.
4794  */
4795 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4797     g_assert(node);
4799     NRPathcode result = NR_END;
4800     if (me == &node->p) {
4801         if (node->p.other) {
4802             result = (NRPathcode)node->code;
4803         } else {
4804             result = NR_MOVETO;
4805         }
4806     } else if (me == &node->n) {
4807         if (node->n.other) {
4808             result = (NRPathcode)node->n.other->code;
4809         } else {
4810             result = NR_MOVETO;
4811         }
4812     } else {
4813         g_assert_not_reached();
4814     }
4816     return result;
4819 /**
4820  * Return node with the given index
4821  */
4822 Inkscape::NodePath::Node *
4823 sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index)
4825     Inkscape::NodePath::Node *e = NULL;
4827     if (!nodepath) {
4828         return e;
4829     }
4831     //find segment
4832     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4834         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4835         int n = g_list_length(sp->nodes);
4836         if (sp->closed) {
4837             n++;
4838         }
4840         //if the piece belongs to this subpath grab it
4841         //otherwise move onto the next subpath
4842         if (index < n) {
4843             e = sp->first;
4844             for (int i = 0; i < index; ++i) {
4845                 e = e->n.other;
4846             }
4847             break;
4848         } else {
4849             if (sp->closed) {
4850                 index -= (n+1);
4851             } else {
4852                 index -= n;
4853             }
4854         }
4855     }
4857     return e;
4860 /**
4861  * Returns plain text meaning of node type.
4862  */
4863 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4865     unsigned retracted = 0;
4866     bool endnode = false;
4868     for (int which = -1; which <= 1; which += 2) {
4869         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4870         if (side->other && Geom::L2(side->pos - node->pos) < 1e-6)
4871             retracted ++;
4872         if (!side->other)
4873             endnode = true;
4874     }
4876     if (retracted == 0) {
4877         if (endnode) {
4878                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4879                 return _("end node");
4880         } else {
4881             switch (node->type) {
4882                 case Inkscape::NodePath::NODE_CUSP:
4883                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4884                     return _("cusp");
4885                 case Inkscape::NodePath::NODE_SMOOTH:
4886                     // TRANSLATORS: "smooth" is an adjective here
4887                     return _("smooth");
4888                 case Inkscape::NodePath::NODE_AUTO:
4889                     return _("auto");
4890                 case Inkscape::NodePath::NODE_SYMM:
4891                     return _("symmetric");
4892             }
4893         }
4894     } else if (retracted == 1) {
4895         if (endnode) {
4896             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4897             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4898         } else {
4899             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4900         }
4901     } else {
4902         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4903     }
4905     return NULL;
4908 /**
4909  * Handles content of statusbar as long as node tool is active.
4910  */
4911 void
4912 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4914     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");
4915     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4917     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4918     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4919     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4920     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4922     SPDesktop *desktop = NULL;
4923     if (nodepath) {
4924         desktop = nodepath->desktop;
4925     } else {
4926         desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above
4927     }
4929     SPEventContext *ec = desktop->event_context;
4930     if (!ec) return;
4932     Inkscape::MessageContext *mc = get_message_context(ec);
4933     if (!mc) return;
4935     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4937     if (selected_nodes == 0) {
4938         Inkscape::Selection *sel = desktop->selection;
4939         if (!sel || sel->isEmpty()) {
4940             mc->setF(Inkscape::NORMAL_MESSAGE,
4941                      _("Select a single object to edit its nodes or handles."));
4942         } else {
4943             if (nodepath) {
4944             mc->setF(Inkscape::NORMAL_MESSAGE,
4945                      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.",
4946                               "<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.",
4947                               total_nodes),
4948                      total_nodes);
4949             } else {
4950                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4951                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4952                 } else {
4953                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4954                 }
4955             }
4956         }
4957     } else if (nodepath && selected_nodes == 1) {
4958         mc->setF(Inkscape::NORMAL_MESSAGE,
4959                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4960                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4961                           total_nodes),
4962                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4963     } else {
4964         if (selected_subpaths > 1) {
4965             mc->setF(Inkscape::NORMAL_MESSAGE,
4966                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4967                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4968                               total_nodes),
4969                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4970         } else {
4971             mc->setF(Inkscape::NORMAL_MESSAGE,
4972                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4973                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4974                               total_nodes),
4975                      selected_nodes, total_nodes, when_selected);
4976         }
4977     }
4980 /*
4981  * returns a *copy* of the curve of that object.
4982  */
4983 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4984     if (!object)
4985         return NULL;
4987     SPCurve *curve = NULL;
4988     if (SP_IS_PATH(object)) {
4989         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4990         curve = curve_new->copy();
4991     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4992         const gchar *svgd = object->repr->attribute(key);
4993         if (svgd) {
4994             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4995             SPCurve *curve_new = new SPCurve(pv);
4996             if (curve_new) {
4997                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4998             }
4999         }
5000     }
5002     return curve;
5005 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
5006     if (!np || !np->object || !curve)
5007         return;
5009     if (SP_IS_PATH(np->object)) {
5010         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
5011             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
5012         } else {
5013             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
5014         }
5015     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
5016         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(np->object)->get_lpe();
5017         if (lpe) {
5018             Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( lpe->getParameter(np->repr_key) );
5019             if (pathparam) {
5020                 pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
5021                 np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
5022             }
5023         }
5024     }
5027 /*
5028 SPCanvasItem *
5029 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
5030     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
5032 */
5035 /// \todo this code to generate a helper canvasitem from an spcurve should be moved to different file
5036 SPCanvasItem *
5037 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const Geom::Matrix & i2d, guint32 color = 0xff0000ff) {
5038     SPCurve *flash_curve = curve->copy();
5039     flash_curve->transform(i2d);
5040     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
5041     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
5042     // unless we also flash the nodes...
5043     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
5044     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
5045     sp_canvas_item_show(canvasitem);
5046     flash_curve->unref();
5047     return canvasitem;
5050 SPCanvasItem *
5051 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item) {
5052     if (!item || !desktop) {
5053         return NULL;
5054     }
5056     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5057     guint32 color = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
5059     Geom::Matrix i2d = sp_item_i2d_affine(item);
5061     SPCurve *curve = NULL;
5062     if (SP_IS_PATH(item)) {
5063         curve = sp_path_get_curve_for_edit(SP_PATH(item));
5064     } else if ( SP_IS_SHAPE(item) && SP_SHAPE(item)->curve ) {
5065         curve = sp_shape_get_curve (SP_SHAPE(item));
5066     } else if ( SP_IS_TEXT(item) ) {
5067         curve = SP_TEXT(item)->getNormalizedBpath();
5068     } else {
5069         g_warning ("-----> sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item): TODO: generate the helper path for this item type!\n");
5070         return NULL;
5071     }
5073     SPCanvasItem * helperpath = sp_nodepath_generate_helperpath(desktop, curve, i2d, color);
5075     curve->unref();
5077     return helperpath;
5081 // TODO: Merge this with sp_nodepath_make_helper_item()!
5082 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
5083     np->show_helperpath = show;
5085     if (show) {
5086         SPCurve *helper_curve = np->curve->copy();
5087         helper_curve->transform(np->i2d);
5088         if (!np->helper_path) {
5089             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
5091             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
5092             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);
5093             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
5094             sp_canvas_item_move_to_z(np->helper_path, 0);
5095             sp_canvas_item_show(np->helper_path);
5096         } else {
5097             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
5098         }
5099         helper_curve->unref();
5100     } else {
5101         if (np->helper_path) {
5102             GtkObject *temp = np->helper_path;
5103             np->helper_path = NULL;
5104             gtk_object_destroy(temp);
5105         }
5106     }
5109 /* sp_nodepath_make_straight_path:
5110  *   Prevents user from curving the path by dragging a segment or activating handles etc.
5111  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
5112  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
5113  */
5114 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
5115     np->straight_path = true;
5116     np->show_handles = false;
5117     g_message("add code to make the path straight.");
5118     // do sp_nodepath_convert_node_type on all nodes?
5119     // coding tip: search for this text : "Make selected segments lines"
5122 /*
5123   Local Variables:
5124   mode:c++
5125   c-file-style:"stroustrup"
5126   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5127   indent-tabs-mode:nil
5128   fill-column:99
5129   End:
5130 */
5131 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :