Code

tidy up SPObjects that must always be LivePathEffectObjects
[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  * If repr_key_in is not NULL, object *has* to be a LivePathEffectObject !
252  *
253  * \todo create proper constructor for nodepath::path, this method returns null a constructor cannot so this cannot be simply converted to constructor.
254  */
255 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
257     if (repr_key_in) {
258         g_assert(IS_LIVEPATHEFFECT(object));
259     }
261     Inkscape::XML::Node *repr = object->repr;
263     /** \todo
264      * FIXME: remove this. We don't want to edit paths inside flowtext.
265      * Instead we will build our flowtext with cloned paths, so that the
266      * real paths are outside the flowtext and thus editable as usual.
267      */
268     if (SP_IS_FLOWTEXT(object)) {
269         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
270             if SP_IS_FLOWREGION(child) {
271                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
272                 if (grandchild && SP_IS_PATH(grandchild)) {
273                     object = SP_ITEM(grandchild);
274                     break;
275                 }
276             }
277         }
278     }
280     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
282     if (curve == NULL) {
283         return NULL;
284     }
286     if (curve->get_segment_count() < 1) {
287         curve->unref();
288         return NULL; // prevent crash for one-node paths
289     }
291     //Create new nodepath
292     Inkscape::NodePath::Path *np = new Inkscape::NodePath::Path();
293     if (!np) {
294         curve->unref();
295         return NULL;
296     }
298     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
300     // Set defaults
301     np->desktop     = desktop;
302     np->object      = object;
303     np->subpaths    = NULL;
304     np->selected    = NULL;
305     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
306     np->local_change = 0;
307     np->show_handles = show_handles;
308     np->helper_path = NULL;
309     np->helperpath_rgba = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
310     np->helperpath_width = 1.0;
311     np->curve = curve->copy();
312     np->show_helperpath = prefs->getBool("/tools/nodes/show_helperpath");
313     if (SP_IS_LPE_ITEM(object)) {
314         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
315         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
316             np->show_helperpath = true;
317         }
318     }
319     np->straight_path = false;
320     if (IS_LIVEPATHEFFECT(object) && item) {
321         np->item = item;
322     } else {
323         np->item = SP_ITEM(object);
324     }
326     np->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
328     // we need to update item's transform from the repr here,
329     // because they may be out of sync when we respond
330     // to a change in repr by regenerating nodepath     --bb
331     sp_object_read_attr(SP_OBJECT(np->item), "transform");
333     np->i2d  = sp_item_i2d_affine(np->item);
334     np->d2i  = np->i2d.inverse();
336     np->repr = repr;
337     if (repr_key_in) { // apparently the object is an LPEObject
338         np->repr_key = g_strdup(repr_key_in);
339         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
340         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(object)->get_lpe();
341         if (!lpe) {
342             g_error("sp_nodepath_new: lpeobject without real lpe passed as argument!");
343             delete np;
344         }
345         Inkscape::LivePathEffect::Parameter *lpeparam = lpe->getParameter(repr_key_in);
346         if (lpeparam) {
347             lpeparam->param_setup_nodepath(np);
348         }
349     } else {
350         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
351         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
352             np->repr_key = g_strdup("inkscape:original-d");
354             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
355             if (lpe) {
356                 lpe->setup_nodepath(np);
357             }
358         } else {
359             np->repr_key = g_strdup("d");
360         }
361     }
363     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
364      * So for example a closed rectangle has a nodetypestring of length 5.
365      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
366     Geom::PathVector pathv_sanitized = pathv_to_linear_and_cubic_beziers(np->curve->get_pathvector());
367     np->curve->set_pathvector(pathv_sanitized);
368     guint length = np->curve->get_segment_count();
369     for (Geom::PathVector::const_iterator pit = pathv_sanitized.begin(); pit != pathv_sanitized.end(); ++pit) {
370         length += pit->empty() ? 0 : 1;
371     }
373     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
374     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
376     // create the subpath(s) from the bpath
377     subpaths_from_pathvector(np, pathv_sanitized, typestr);
379     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
380     np->subpaths = g_list_reverse(np->subpaths);
382     delete[] typestr;
383     curve->unref();
385     // Draw helper curve
386     if (np->show_helperpath) {
387         np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true);
388     }
390     sp_nodepath_create_helperpaths(np);
392     return np;
395 /**
396  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
397  */
398 Inkscape::NodePath::Path::~Path() {
399     while (this->subpaths) {
400         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) this->subpaths->data);
401     }
403     //Inform the ShapeEditor that made me, if any, that I am gone.
404     if (this->shape_editor)
405         this->shape_editor->nodepath_destroyed();
407     g_assert(!this->selected);
409     if (this->helper_path) {
410         GtkObject *temp = this->helper_path;
411         this->helper_path = NULL;
412         gtk_object_destroy(temp);
413     }
414     if (this->curve) {
415         this->curve->unref();
416         this->curve = NULL;
417     }
419     if (this->repr_key) {
420         g_free(this->repr_key);
421         this->repr_key = NULL;
422     }
423     if (this->repr_nodetypes_key) {
424         g_free(this->repr_nodetypes_key);
425         this->repr_nodetypes_key = NULL;
426     }
428     sp_nodepath_destroy_helperpaths(this);
430     this->desktop = NULL;
433 /**
434  *  Return the node count of a given NodeSubPath.
435  */
436 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
438     int nodeCount = 0;
440     if (subpath) {
441         nodeCount = g_list_length(subpath->nodes);
442     }
444     return nodeCount;
447 /**
448  *  Return the node count of a given NodePath.
449  */
450 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
452     gint nodeCount = 0;
453     if (np) {
454         for (GList *item = np->subpaths ; item ; item=item->next) {
455             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
456             nodeCount += g_list_length(subpath->nodes);
457         }
458     }
459     return nodeCount;
462 /**
463  *  Return the subpath count of a given NodePath.
464  */
465 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
467     gint nodeCount = 0;
468     if (np) {
469         nodeCount = g_list_length(np->subpaths);
470     }
471     return nodeCount;
474 /**
475  *  Return the selected node count of a given NodePath.
476  */
477 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
479     gint nodeCount = 0;
480     if (np) {
481         nodeCount = g_list_length(np->selected);
482     }
483     return nodeCount;
486 /**
487  *  Return the number of subpaths where nodes are selected in a given NodePath.
488  */
489 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
491     gint nodeCount = 0;
492     if (np && np->selected) {
493         if (!np->selected->next) {
494             nodeCount = 1;
495         } else {
496             for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
497                 Inkscape::NodePath::SubPath *subpath = static_cast<Inkscape::NodePath::SubPath *>(spl->data);
498                 for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
499                     Inkscape::NodePath::Node *node = static_cast<Inkscape::NodePath::Node *>(nl->data);
500                     if (node->selected) {
501                         nodeCount++;
502                         break;
503                     }
504                 }
505             }
506         }
507     }
508     return nodeCount;
511 /**
512  * Clean up a nodepath after editing.
513  *
514  * Currently we are deleting trivial subpaths.
515  */
516 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
518     GList *badSubPaths = NULL;
520     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
521     for (GList *l = nodepath->subpaths; l ; l=l->next) {
522        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
523        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
524             badSubPaths = g_list_append(badSubPaths, sp);
525     }
527     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
528     //also removes the subpath from nodepath->subpaths
529     for (GList *l = badSubPaths; l ; l=l->next) {
530        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
531         sp_nodepath_subpath_destroy(sp);
532     }
534     g_list_free(badSubPaths);
537 /**
538  * Create new nodepaths from pathvector, make it subpaths of np.
539  * \param t The node type array.
540  */
541 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
543     guint i = 0;  // index into node type array
544     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
545         if (pit->empty())
546             continue;  // don't add single knot paths
548         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
550         Geom::Point ppos = pit->initialPoint() * np->i2d;
551         NRPathcode pcode = NR_MOVETO;
553         /* 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)*/
554         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
555             if( dynamic_cast<Geom::LineSegment const*>(&*cit) ||
556                 dynamic_cast<Geom::HLineSegment const*>(&*cit) ||
557                 dynamic_cast<Geom::VLineSegment const*>(&*cit) )
558             {
559                 Geom::Point pos = cit->initialPoint() * (Geom::Matrix)np->i2d;
560                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
562                 ppos = cit->finalPoint() * (Geom::Matrix)np->i2d;
563                 pcode = NR_LINETO;
564             }
565             else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
566                 std::vector<Geom::Point> points = cubic_bezier->points();
567                 Geom::Point pos = points[0] * (Geom::Matrix)np->i2d;
568                 Geom::Point npos = points[1] * (Geom::Matrix)np->i2d;
569                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
571                 ppos = points[2] * (Geom::Matrix)np->i2d;
572                 pcode = NR_CURVETO;
573             }
574         }
576         if (pit->closed()) {
577             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
578             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
579              * If the length is zero, don't add it to the nodepath. */
580             Geom::Curve const &closing_seg = pit->back_closed();
581             // Don't use !closing_seg.isDegenerate() as it is too precise, and does not account for floating point rounding probs (LP bug #257289)
582             if ( ! are_near(closing_seg.initialPoint(), closing_seg.finalPoint()) ) {
583                 Geom::Point pos = closing_seg.finalPoint() * (Geom::Matrix)np->i2d;
584                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
585             }
587             sp_nodepath_subpath_close(sp);
588         }
589     }
592 /**
593  * Convert from sodipodi:nodetypes to new style type array.
594  */
595 static
596 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
598     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
600     guint pos = 0;
602     if (types) {
603         for (guint i = 0; types[i] && ( i < length ); i++) {
604             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
605             if (types[i] != '\0') {
606                 switch (types[i]) {
607                     case 's':
608                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
609                         break;
610                     case 'a':
611                         typestr[pos++] =Inkscape::NodePath::NODE_AUTO;
612                         break;
613                     case 'z':
614                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
615                         break;
616                     case 'c':
617                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
618                         break;
619                     default:
620                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
621                         break;
622                 }
623             }
624         }
625     }
627     while (pos < length) {
628         typestr[pos++] = Inkscape::NodePath::NODE_NONE;
629     }
631     return typestr;
634 /**
635  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
636  * updated but repr is not (for speed). Used during curve and node drag.
637  */
638 static void update_object(Inkscape::NodePath::Path *np)
640     g_assert(np);
642     np->curve->unref();
643     np->curve = create_curve(np);
645     sp_nodepath_set_curve(np, np->curve);
647     if (np->show_helperpath) {
648         SPCurve * helper_curve = np->curve->copy();
649         helper_curve->transform(np->i2d);
650         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
651         helper_curve->unref();
652     }
654     // updating helperpaths of LPEItems is now done in sp_lpe_item_update();
655     //sp_nodepath_update_helperpaths(np);
657     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
658     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
659     np->shape_editor->update_knotholder();
662 /**
663  * Update XML path node with data from path object.
664  */
665 static void update_repr_internal(Inkscape::NodePath::Path *np)
667     g_assert(np);
669     Inkscape::XML::Node *repr = np->object->repr;
671     np->curve->unref();
672     np->curve = create_curve(np);
674     gchar *typestr = create_typestr(np);
675     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
677     // determine if path has an effect applied and write to correct "d" attribute.
678     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
679         np->local_change++;
680         repr->setAttribute(np->repr_key, svgpath);
681     }
683     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
684         np->local_change++;
685         repr->setAttribute(np->repr_nodetypes_key, typestr);
686     }
688     g_free(svgpath);
689     g_free(typestr);
691     if (np->show_helperpath) {
692         SPCurve * helper_curve = np->curve->copy();
693         helper_curve->transform(np->i2d);
694         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
695         helper_curve->unref();
696     }
698     // TODO: do we need this call here? after all, update_object() should have been called just before
699     //sp_nodepath_update_helperpaths(np);
702 /**
703  * Update XML path node with data from path object, commit changes forever.
704  */
705 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
707     //fixme: np can be NULL, so check before proceeding
708     g_return_if_fail(np != NULL);
710     update_repr_internal(np);
711     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
713     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
714                      annotation);
717 /**
718  * Update XML path node with data from path object, commit changes with undo.
719  */
720 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
722     update_repr_internal(np);
723     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
724                            annotation);
727 /**
728  * Make duplicate of path, replace corresponding XML node in tree, commit.
729  */
730 static void stamp_repr(Inkscape::NodePath::Path *np)
732     g_assert(np);
734     Inkscape::XML::Node *old_repr = np->object->repr;
735     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
737     // remember the position of the item
738     gint pos = old_repr->position();
739     // remember parent
740     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
742     SPCurve *curve = create_curve(np);
743     gchar *typestr = create_typestr(np);
745     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
747     new_repr->setAttribute(np->repr_key, svgpath);
748     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
750     // add the new repr to the parent
751     parent->appendChild(new_repr);
752     // move to the saved position
753     new_repr->setPosition(pos > 0 ? pos : 0);
755     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
756                      _("Stamp"));
758     Inkscape::GC::release(new_repr);
759     g_free(svgpath);
760     g_free(typestr);
761     curve->unref();
764 /**
765  * Create curve from path.
766  */
767 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
769     SPCurve *curve = new SPCurve();
771     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
772        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
773        curve->moveto(sp->first->pos * np->d2i);
774        Inkscape::NodePath::Node *n = sp->first->n.other;
775         while (n) {
776             Geom::Point const end_pt = n->pos * np->d2i;
777             if (!IS_FINITE(n->pos[0]) || !IS_FINITE(n->pos[1])){
778                 g_message("niet finite");
779             }
780             switch (n->code) {
781                 case NR_LINETO:
782                     curve->lineto(end_pt);
783                     break;
784                 case NR_CURVETO:
785                     curve->curveto(n->p.other->n.pos * np->d2i,
786                                      n->p.pos * np->d2i,
787                                      end_pt);
788                     break;
789                 default:
790                     g_assert_not_reached();
791                     break;
792             }
793             if (n != sp->last) {
794                 n = n->n.other;
795             } else {
796                 n = NULL;
797             }
798         }
799         if (sp->closed) {
800             curve->closepath();
801         }
802     }
804     return curve;
807 /**
808  * Convert path type string to sodipodi:nodetypes style.
809  */
810 static gchar *create_typestr(Inkscape::NodePath::Path *np)
812     gchar *typestr = g_new(gchar, 32);
813     gint len = 32;
814     gint pos = 0;
816     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
817        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
819         if (pos >= len) {
820             typestr = g_renew(gchar, typestr, len + 32);
821             len += 32;
822         }
824         typestr[pos++] = 'c';
826        Inkscape::NodePath::Node *n;
827         n = sp->first->n.other;
828         while (n) {
829             gchar code;
831             switch (n->type) {
832                 case Inkscape::NodePath::NODE_CUSP:
833                     code = 'c';
834                     break;
835                 case Inkscape::NodePath::NODE_SMOOTH:
836                     code = 's';
837                     break;
838                 case Inkscape::NodePath::NODE_AUTO:
839                     code = 'a';
840                     break;
841                 case Inkscape::NodePath::NODE_SYMM:
842                     code = 'z';
843                     break;
844                 default:
845                     g_assert_not_reached();
846                     code = '\0';
847                     break;
848             }
850             if (pos >= len) {
851                 typestr = g_renew(gchar, typestr, len + 32);
852                 len += 32;
853             }
855             typestr[pos++] = code;
857             if (n != sp->last) {
858                 n = n->n.other;
859             } else {
860                 n = NULL;
861             }
862         }
863     }
865     if (pos >= len) {
866         typestr = g_renew(gchar, typestr, len + 1);
867         len += 1;
868     }
870     typestr[pos++] = '\0';
872     return typestr;
875 // Returns different message contexts depending on the current context. This function should only
876 // be called when ec is either a SPNodeContext or SPLPEToolContext, thus we return NULL in all
877 // other cases.
878 static Inkscape::MessageContext *
879 get_message_context(SPEventContext *ec)
881     Inkscape::MessageContext *mc = 0;
883     if (SP_IS_NODE_CONTEXT(ec)) {
884         mc = SP_NODE_CONTEXT(ec)->_node_message_context;
885     } else if (SP_IS_LPETOOL_CONTEXT(ec)) {
886         mc = SP_LPETOOL_CONTEXT(ec)->_lpetool_message_context;
887     } else {
888         g_warning ("Nodepath should only be present in Node tool or Geometric tool.");
889     }
891     return mc;
894 /**
895  \brief Fills node and handle positions for three nodes, splitting line
896   marked by end at distance t.
897  */
898 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
900     g_assert(new_path != NULL);
901     g_assert(end      != NULL);
903     g_assert(end->p.other == new_path);
904    Inkscape::NodePath::Node *start = new_path->p.other;
905     g_assert(start);
907     if (end->code == NR_LINETO) {
908         new_path->type =Inkscape::NodePath::NODE_CUSP;
909         new_path->code = NR_LINETO;
910         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
911     } else {
912         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
913         new_path->code = NR_CURVETO;
914         gdouble s      = 1 - t;
915         for (int dim = 0; dim < 2; dim++) {
916             Geom::Coord const f000 = start->pos[dim];
917             Geom::Coord const f001 = start->n.pos[dim];
918             Geom::Coord const f011 = end->p.pos[dim];
919             Geom::Coord const f111 = end->pos[dim];
920             Geom::Coord const f00t = s * f000 + t * f001;
921             Geom::Coord const f01t = s * f001 + t * f011;
922             Geom::Coord const f11t = s * f011 + t * f111;
923             Geom::Coord const f0tt = s * f00t + t * f01t;
924             Geom::Coord const f1tt = s * f01t + t * f11t;
925             Geom::Coord const fttt = s * f0tt + t * f1tt;
926             start->n.pos[dim]    = f00t;
927             new_path->p.pos[dim] = f0tt;
928             new_path->pos[dim]   = fttt;
929             new_path->n.pos[dim] = f1tt;
930             end->p.pos[dim]      = f11t;
931         }
932     }
935 /**
936  * Adds new node on direct line between two nodes, activates handles of all
937  * three nodes.
938  */
939 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
941     g_assert(end);
942     g_assert(end->subpath);
943     g_assert(g_list_find(end->subpath->nodes, end));
945    Inkscape::NodePath::Node *start = end->p.other;
946     g_assert( start->n.other == end );
947    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
948                                                end,
949                                                (NRPathcode)end->code == NR_LINETO?
950                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
951                                                (NRPathcode)end->code,
952                                                &start->pos, &start->pos, &start->n.pos);
953     sp_nodepath_line_midpoint(newnode, end, t);
955     sp_node_adjust_handles(start);
956     sp_node_update_handles(start);
957     sp_node_update_handles(newnode);
958     sp_node_adjust_handles(end);
959     sp_node_update_handles(end);
961     return newnode;
964 /**
965 \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
966 */
967 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
969     g_assert(node);
970     g_assert(node->subpath);
971     g_assert(g_list_find(node->subpath->nodes, node));
973     Inkscape::NodePath::Node* result = 0;
974     Inkscape::NodePath::SubPath *sp = node->subpath;
975     Inkscape::NodePath::Path *np    = sp->nodepath;
977     if (sp->closed) {
978         sp_nodepath_subpath_open(sp, node);
979         result = sp->first;
980     } else if ( (node == sp->first) || (node == sp->last ) ){
981         // no break for end nodes
982         result = 0;
983     } else {
984         // create a new subpath
985         Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
987         // duplicate the break node as start of the new subpath
988         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL,
989                                                                  static_cast<Inkscape::NodePath::NodeType>(node->type),
990                                                                  NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
992         // attach rest of curve to new node
993         g_assert(node->n.other);
994         newnode->n.other = node->n.other; node->n.other = NULL;
995         newnode->n.other->p.other = newnode;
996         newsubpath->last = sp->last;
997         sp->last = node;
998         node = newnode;
999         while (node->n.other) {
1000             node = node->n.other;
1001             node->subpath = newsubpath;
1002             sp->nodes = g_list_remove(sp->nodes, node);
1003             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
1004         }
1007         result = newnode;
1008     }
1009     return result;
1012 /**
1013  * Duplicate node and connect to neighbours.
1014  */
1015 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
1017     g_assert(node);
1018     g_assert(node->subpath);
1019     g_assert(g_list_find(node->subpath->nodes, node));
1021    Inkscape::NodePath::SubPath *sp = node->subpath;
1023     NRPathcode code = (NRPathcode) node->code;
1024     if (code == NR_MOVETO) { // if node is the endnode,
1025         node->code = NR_LINETO; // new one is inserted before it, so change that to line
1026     }
1028     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
1030     if (!node->n.other || !node->p.other) { // if node is an endnode, select it
1031         return node;
1032     } else {
1033         return newnode; // otherwise select the newly created node
1034     }
1037 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
1039     node->p.pos = (node->pos + (node->pos - node->n.pos));
1042 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
1044     node->n.pos = (node->pos + (node->pos - node->p.pos));
1047 /**
1048  * Change line type at node, with side effects on neighbours.
1049  */
1050 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
1052     g_assert(end);
1053     g_assert(end->subpath);
1054     g_assert(end->p.other);
1056     if (end->code != static_cast<guint>(code) ) {
1057         Inkscape::NodePath::Node *start = end->p.other;
1059         end->code = code;
1061         if (code == NR_LINETO) {
1062             if (start->code == NR_LINETO) {
1063                 sp_nodepath_set_node_type(start, Inkscape::NodePath::NODE_CUSP);
1064             }
1065             if (end->n.other) {
1066                 if (end->n.other->code == NR_LINETO) {
1067                     sp_nodepath_set_node_type(end, Inkscape::NodePath::NODE_CUSP);
1068                 }
1069             }
1071             if (start->type == Inkscape::NodePath::NODE_AUTO)
1072                 start->type = Inkscape::NodePath::NODE_SMOOTH;
1073             if (end->type == Inkscape::NodePath::NODE_AUTO)
1074                 end->type = Inkscape::NodePath::NODE_SMOOTH;
1076             start->n.pos = start->pos;
1077             end->p.pos = end->pos;
1079             sp_node_adjust_handle(start, -1);
1080             sp_node_adjust_handle(end, 1);
1082         } else {
1083             Geom::Point delta = end->pos - start->pos;
1084             start->n.pos = start->pos + delta / 3;
1085             end->p.pos = end->pos - delta / 3;
1086             sp_node_adjust_handle(start, 1);
1087             sp_node_adjust_handle(end, -1);
1088         }
1090         sp_node_update_handles(start);
1091         sp_node_update_handles(end);
1092     }
1095 static void
1096 sp_nodepath_update_node_knot(Inkscape::NodePath::Node *node)
1098     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1099         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1100         node->knot->setSize (node->selected? 11 : 9);
1101         sp_knot_update_ctrl(node->knot);
1102     } else if (node->type == Inkscape::NodePath::NODE_AUTO) {
1103         node->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1104         node->knot->setSize (node->selected? 11 : 9);
1105         sp_knot_update_ctrl(node->knot);
1106     } else {
1107         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1108         node->knot->setSize (node->selected? 9 : 7);
1109         sp_knot_update_ctrl(node->knot);
1110     }
1114 /**
1115  * Change node type, and its handles accordingly.
1116  */
1117 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1119     g_assert(node);
1120     g_assert(node->subpath);
1122     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1123         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1124             type =Inkscape::NodePath::NODE_CUSP;
1125         }
1126     }
1128     node->type = type;
1130     sp_nodepath_update_node_knot(node);
1132     // if one of handles is mouseovered, preserve its position
1133     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1134         sp_node_adjust_handle(node, 1);
1135     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1136         sp_node_adjust_handle(node, -1);
1137     } else {
1138         sp_node_adjust_handles(node);
1139     }
1141     sp_node_update_handles(node);
1143     sp_nodepath_update_statusbar(node->subpath->nodepath);
1145     return node;
1148 bool
1149 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1151 // TODO clean up multiple returns
1152         Inkscape::NodePath::Node *othernode = side->other;
1153         if (!othernode)
1154             return false;
1155         NRPathcode const code = sp_node_path_code_from_side(node, side);
1156         if (code == NR_LINETO)
1157             return true;
1158         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1159         if (&node->p == side) {
1160             other_to_me = &othernode->n;
1161         } else if (&node->n == side) {
1162             other_to_me = &othernode->p;
1163         }
1164         if (!other_to_me)
1165             return false;
1166         bool is_line =
1167              (Geom::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1168               Geom::L2(node->pos - side->pos) < 1e-6);
1169         return is_line;
1172 /**
1173  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1174  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1175  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too.
1176  * If already cusp and set to cusp, retracts handles.
1177 */
1178 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1180     if (type == Inkscape::NodePath::NODE_AUTO) {
1181         if (node->p.other != NULL)
1182             node->code = NR_CURVETO;
1183         if (node->n.other != NULL)
1184             node->n.other->code = NR_CURVETO;
1185     }
1187     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1189 /*
1190   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1192         if (two_handles) {
1193             // do nothing, adjust_handles called via set_node_type will line them up
1194         } else if (one_handle) {
1195             if (opposite_to_handle_is_line) {
1196                 if (lined_up) {
1197                     // already half-smooth; pull opposite handle too making it fully smooth
1198                 } else {
1199                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1200                 }
1201             } else {
1202                 // pull opposite handle in line with the existing one
1203             }
1204         } else if (no_handles) {
1205             if (both_segments_are_lines OR both_segments_are_curves) {
1206                 //pull both handles
1207             } else {
1208                 // pull the handle opposite to line segment, making node half-smooth
1209             }
1210         }
1211 */
1212         bool p_has_handle = (Geom::L2(node->pos  - node->p.pos) > 1e-6);
1213         bool n_has_handle = (Geom::L2(node->pos  - node->n.pos) > 1e-6);
1214         bool p_is_line = sp_node_side_is_line(node, &node->p);
1215         bool n_is_line = sp_node_side_is_line(node, &node->n);
1217         if (p_has_handle && n_has_handle) {
1218             // do nothing, adjust_handles will line them up
1219         } else if (p_has_handle || n_has_handle) {
1220             if (p_has_handle && n_is_line) {
1221                 Radial line (node->n.other->pos - node->pos);
1222                 Radial handle (node->pos - node->p.pos);
1223                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1224                     // already half-smooth; pull opposite handle too making it fully smooth
1225                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1226                 } else {
1227                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1228                 }
1229             } else if (n_has_handle && p_is_line) {
1230                 Radial line (node->p.other->pos - node->pos);
1231                 Radial handle (node->pos - node->n.pos);
1232                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1233                     // already half-smooth; pull opposite handle too making it fully smooth
1234                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1235                 } else {
1236                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1237                 }
1238             } else if (p_has_handle && node->n.other) {
1239                 // pull n handle
1240                 node->n.other->code = NR_CURVETO;
1241                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1242                     Geom::L2(node->p.pos - node->pos) :
1243                     Geom::L2(node->n.other->pos - node->pos) / 3;
1244                 node->n.pos = node->pos - (len / Geom::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1245             } else if (n_has_handle && node->p.other) {
1246                 // pull p handle
1247                 node->code = NR_CURVETO;
1248                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1249                     Geom::L2(node->n.pos - node->pos) :
1250                     Geom::L2(node->p.other->pos - node->pos) / 3;
1251                 node->p.pos = node->pos - (len / Geom::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1252             }
1253         } else if (!p_has_handle && !n_has_handle) {
1254             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1255                 // no handles, but both segments are either lnes or curves:
1256                 //pull both handles
1258                 // convert both to curves:
1259                 node->code = NR_CURVETO;
1260                 node->n.other->code = NR_CURVETO;
1262                 sp_node_adjust_handles_auto(node);
1263             } else {
1264                 // pull the handle opposite to line segment, making it half-smooth
1265                 if (p_is_line && node->n.other) {
1266                     if (type != Inkscape::NodePath::NODE_SYMM) {
1267                         // pull n handle
1268                         node->n.other->code = NR_CURVETO;
1269                         double len =  Geom::L2(node->n.other->pos - node->pos) / 3;
1270                         node->n.pos = node->pos + (len / Geom::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1271                     }
1272                 } else if (n_is_line && node->p.other) {
1273                     if (type != Inkscape::NodePath::NODE_SYMM) {
1274                         // pull p handle
1275                         node->code = NR_CURVETO;
1276                         double len =  Geom::L2(node->p.other->pos - node->pos) / 3;
1277                         node->p.pos = node->pos + (len / Geom::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1278                     }
1279                 }
1280             }
1281         }
1282     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1283         // cusping a cusp: retract nodes
1284         node->p.pos = node->pos;
1285         node->n.pos = node->pos;
1286     }
1288     sp_nodepath_set_node_type (node, type);
1291 /**
1292  * Move node to point, and adjust its and neighbouring handles.
1293  */
1294 void sp_node_moveto(Inkscape::NodePath::Node *node, Geom::Point p)
1296     if (node->type == Inkscape::NodePath::NODE_AUTO) {
1297         node->pos = p;
1298         sp_node_adjust_handles_auto(node);
1299     } else {
1300         Geom::Point delta = p - node->pos;
1301         node->pos = p;
1303         node->p.pos += delta;
1304         node->n.pos += delta;
1305     }
1307     Inkscape::NodePath::Node *node_p = NULL;
1308     Inkscape::NodePath::Node *node_n = NULL;
1310     if (node->p.other) {
1311         if (node->code == NR_LINETO) {
1312             sp_node_adjust_handle(node, 1);
1313             sp_node_adjust_handle(node->p.other, -1);
1314             node_p = node->p.other;
1315         }
1316         if (!node->p.other->selected && node->p.other->type == Inkscape::NodePath::NODE_AUTO) {
1317             sp_node_adjust_handles_auto(node->p.other);
1318             node_p = node->p.other;
1319         }
1320     }
1321     if (node->n.other) {
1322         if (node->n.other->code == NR_LINETO) {
1323             sp_node_adjust_handle(node, -1);
1324             sp_node_adjust_handle(node->n.other, 1);
1325             node_n = node->n.other;
1326         }
1327         if (!node->n.other->selected && node->n.other->type == Inkscape::NodePath::NODE_AUTO) {
1328             sp_node_adjust_handles_auto(node->n.other);
1329             node_n = node->n.other;
1330         }
1331     }
1333     // this function is only called from batch movers that will update display at the end
1334     // themselves, so here we just move all the knots without emitting move signals, for speed
1335     sp_node_update_handles(node, false);
1336     if (node_n) {
1337         sp_node_update_handles(node_n, false);
1338     }
1339     if (node_p) {
1340         sp_node_update_handles(node_p, false);
1341     }
1344 /**
1345  * Call sp_node_moveto() for node selection and handle possible snapping.
1346  */
1347 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, Geom::Coord dx, Geom::Coord dy,
1348                                             bool const snap, bool constrained = false,
1349                                             Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point())
1351     Geom::Point delta(dx, dy);
1352     Geom::Point best_pt = delta;
1353     Inkscape::SnappedPoint best;
1355     if (snap) {
1356         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1357          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we
1358          * must provide that information. */
1360         // Build a list of the unselected nodes to which the snapper should snap
1361         std::vector<std::pair<Geom::Point, int> > unselected_nodes;
1362         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1363             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1364             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1365                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1366                 if (!node->selected) {
1367                     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));
1368                 }
1369             }
1370         }
1372         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1374         // When only the node closest to the mouse pointer is to be snapped
1375         // then we will not even try to snap to other points and discard those immediately
1376         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1377         bool closest_only = prefs->getBool("/options/snapclosestonly/value", false);
1379         Inkscape::NodePath::Node *closest_node = NULL;
1380         Geom::Coord closest_dist = NR_HUGE;
1382         if (closest_only) {
1383                 for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1384                         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1385                         Geom::Coord dist = Geom::L2(nodepath->drag_origin_mouse - n->origin);
1386                         if (dist < closest_dist) {
1387                                 closest_node = n;
1388                                 closest_dist = dist;
1389                         }
1390                 }
1391         }
1393         // Iterate through all selected nodes
1394         m.setup(nodepath->desktop, false, SP_PATH(nodepath->item), &unselected_nodes);
1395         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1396             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1397             if (!closest_only || n == closest_node) { //try to snap either all selected nodes or only the closest one
1398                     Inkscape::SnappedPoint s;
1399                     Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP);
1400                     if (constrained) {
1401                         Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1402                         dedicated_constraint.setPoint(n->pos);
1403                         s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type, dedicated_constraint, false);
1404                     } else {
1405                         s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type);
1406                     }
1408                     if (s.getSnapped()) {
1409                         s.setPointerDistance(Geom::L2(nodepath->drag_origin_mouse - n->origin));
1410                         if (!s.isOtherSnapBetter(best, true)) {
1411                                 best = s;
1412                                 best_pt = from_2geom(s.getPoint()) - n->pos;
1413                         }
1414                     }
1415             }
1416         }
1418         if (best.getSnapped()) {
1419             nodepath->desktop->snapindicator->set_new_snaptarget(best);
1420         } else {
1421             nodepath->desktop->snapindicator->remove_snaptarget();
1422         }
1423     }
1425     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1426         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1427         sp_node_moveto(n, n->pos + best_pt);
1428     }
1430     // do not update repr here so that node dragging is acceptably fast
1431     update_object(nodepath);
1434 /**
1435 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1436 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1437 near x = 0.
1438  */
1439 double
1440 sculpt_profile (double x, double alpha, guint profile)
1442     double result = 1;
1444     if (x >= 1) {
1445         result = 0;
1446     } else if (x <= 0) {
1447         result = 1;
1448     } else {
1449         switch (profile) {
1450             case SCULPT_PROFILE_LINEAR:
1451                 result = 1 - x;
1452                 break;
1453             case SCULPT_PROFILE_BELL:
1454                 result = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1455                 break;
1456             case SCULPT_PROFILE_ELLIPTIC:
1457                 result = sqrt(1 - x*x);
1458                 break;
1459             default:
1460                 g_assert_not_reached();
1461         }
1462     }
1464     return result;
1467 double
1468 bezier_length (Geom::Point a, Geom::Point ah, Geom::Point bh, Geom::Point b)
1470     // extremely primitive for now, don't have time to look for the real one
1471     double lower = Geom::L2(b - a);
1472     double upper = Geom::L2(ah - a) + Geom::L2(bh - ah) + Geom::L2(bh - b);
1473     return (lower + upper)/2;
1476 void
1477 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, Geom::Point delta, Geom::Point delta_n, Geom::Point delta_p)
1479     n->pos = n->origin + delta;
1480     n->n.pos = n->n.origin + delta_n;
1481     n->p.pos = n->p.origin + delta_p;
1482     sp_node_adjust_handles(n);
1483     sp_node_update_handles(n, false);
1486 /**
1487  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1488  * on how far they are from the dragged node n.
1489  */
1490 static void
1491 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, Geom::Point delta)
1493     g_assert (n);
1494     g_assert (nodepath);
1495     g_assert (n->subpath->nodepath == nodepath);
1497     double pressure = n->knot->pressure;
1498     if (pressure == 0)
1499         pressure = 0.5; // default
1500     pressure = CLAMP (pressure, 0.2, 0.8);
1502     // map pressure to alpha = 1/5 ... 5
1503     double alpha = 1 - 2 * fabs(pressure - 0.5);
1504     if (pressure > 0.5)
1505         alpha = 1/alpha;
1507     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1508     guint profile = prefs->getInt("/tools/nodes/sculpting_profile", SCULPT_PROFILE_BELL);
1510     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1511         // Only one subpath has selected nodes:
1512         // use linear mode, where the distance from n to node being dragged is calculated along the path
1514         double n_sel_range = 0, p_sel_range = 0;
1515         guint n_nodes = 0, p_nodes = 0;
1516         guint n_sel_nodes = 0, p_sel_nodes = 0;
1518         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1519         {
1520             double n_range = 0, p_range = 0;
1521             bool n_going = true, p_going = true;
1522             Inkscape::NodePath::Node *n_node = n;
1523             Inkscape::NodePath::Node *p_node = n;
1524             do {
1525                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1526                 if (n_node && n_going)
1527                     n_node = n_node->n.other;
1528                 if (n_node == NULL) {
1529                     n_going = false;
1530                 } else {
1531                     n_nodes ++;
1532                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1533                     if (n_node->selected) {
1534                         n_sel_nodes ++;
1535                         n_sel_range = n_range;
1536                     }
1537                     if (n_node == p_node) {
1538                         n_going = false;
1539                         p_going = false;
1540                     }
1541                 }
1542                 if (p_node && p_going)
1543                     p_node = p_node->p.other;
1544                 if (p_node == NULL) {
1545                     p_going = false;
1546                 } else {
1547                     p_nodes ++;
1548                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1549                     if (p_node->selected) {
1550                         p_sel_nodes ++;
1551                         p_sel_range = p_range;
1552                     }
1553                     if (p_node == n_node) {
1554                         n_going = false;
1555                         p_going = false;
1556                     }
1557                 }
1558             } while (n_going || p_going);
1559         }
1561         // Second pass: actually move nodes in this subpath
1562         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1563         {
1564             double n_range = 0, p_range = 0;
1565             bool n_going = true, p_going = true;
1566             Inkscape::NodePath::Node *n_node = n;
1567             Inkscape::NodePath::Node *p_node = n;
1568             do {
1569                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1570                 if (n_node && n_going)
1571                     n_node = n_node->n.other;
1572                 if (n_node == NULL) {
1573                     n_going = false;
1574                 } else {
1575                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1576                     if (n_node->selected) {
1577                         sp_nodepath_move_node_and_handles (n_node,
1578                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1579                                                            sculpt_profile ((n_range + Geom::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1580                                                            sculpt_profile ((n_range - Geom::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1581                     }
1582                     if (n_node == p_node) {
1583                         n_going = false;
1584                         p_going = false;
1585                     }
1586                 }
1587                 if (p_node && p_going)
1588                     p_node = p_node->p.other;
1589                 if (p_node == NULL) {
1590                     p_going = false;
1591                 } else {
1592                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1593                     if (p_node->selected) {
1594                         sp_nodepath_move_node_and_handles (p_node,
1595                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1596                                                            sculpt_profile ((p_range - Geom::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1597                                                            sculpt_profile ((p_range + Geom::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1598                     }
1599                     if (p_node == n_node) {
1600                         n_going = false;
1601                         p_going = false;
1602                     }
1603                 }
1604             } while (n_going || p_going);
1605         }
1607     } else {
1608         // Multiple subpaths have selected nodes:
1609         // use spatial mode, where the distance from n to node being dragged is measured directly as Geom::L2.
1610         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1611         // fix the pear-like shape when sculpting e.g. a ring
1613         // First pass: calculate range
1614         gdouble direct_range = 0;
1615         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1616             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1617             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1618                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1619                 if (node->selected) {
1620                     direct_range = MAX(direct_range, Geom::L2(node->origin - n->origin));
1621                 }
1622             }
1623         }
1625         // Second pass: actually move nodes
1626         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1627             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1628             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1629                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1630                 if (node->selected) {
1631                     if (direct_range > 1e-6) {
1632                         sp_nodepath_move_node_and_handles (node,
1633                                                        sculpt_profile (Geom::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1634                                                        sculpt_profile (Geom::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1635                                                        sculpt_profile (Geom::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1636                     } else {
1637                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1638                     }
1640                 }
1641             }
1642         }
1643     }
1645     // do not update repr here so that node dragging is acceptably fast
1646     update_object(nodepath);
1650 /**
1651  * Move node selection to point, adjust its and neighbouring handles,
1652  * handle possible snapping, and commit the change with possible undo.
1653  */
1654 void
1655 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1657     if (!nodepath) return;
1659     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1661     if (dx == 0) {
1662         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1663     } else if (dy == 0) {
1664         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1665     } else {
1666         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1667     }
1670 /**
1671  * Move node selection off screen and commit the change.
1672  */
1673 void
1674 sp_node_selected_move_screen(SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1676     // borrowed from sp_selection_move_screen in selection-chemistry.c
1677     // we find out the current zoom factor and divide deltas by it
1679     gdouble zoom = desktop->current_zoom();
1680     gdouble zdx = dx / zoom;
1681     gdouble zdy = dy / zoom;
1683     if (!nodepath) return;
1685     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1687     if (dx == 0) {
1688         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1689     } else if (dy == 0) {
1690         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1691     } else {
1692         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1693     }
1696 /**
1697  * Move selected nodes to the absolute position given
1698  */
1699 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis)
1701     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1702         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1703         Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]);
1704         sp_node_moveto(n, npos);
1705     }
1707     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1710 /**
1711  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return Geom::Nothing
1712  */
1713 boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1715     boost::optional<Geom::Coord> no_coord;
1716     g_return_val_if_fail(nodepath->selected, no_coord);
1718     // determine coordinate of first selected node
1719     GList *nsel = nodepath->selected;
1720     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1721     Geom::Coord coord = n->pos[axis];
1722     bool coincide = true;
1724     // compare it to the coordinates of all the other selected nodes
1725     for (GList *l = nsel->next; l != NULL; l = l->next) {
1726         n = (Inkscape::NodePath::Node *) l->data;
1727         if (n->pos[axis] != coord) {
1728             coincide = false;
1729         }
1730     }
1731     if (coincide) {
1732         return coord;
1733     } else {
1734         Geom::Rect bbox = sp_node_selected_bbox(nodepath);
1735         // currently we return the coordinate of the bounding box midpoint because I don't know how
1736         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1737         return bbox.midpoint()[axis];
1738     }
1741 /** If they don't yet exist, creates knot and line for the given side of the node */
1742 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1744     if (!side->knot) {
1745         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"));
1747         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1748         side->knot->setSize (7);
1749         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1750         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1751         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1752         sp_knot_update_ctrl(side->knot);
1754         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1755         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1756         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1757         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1758         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1759         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1760     }
1762     if (!side->line) {
1763         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1764                                         SP_TYPE_CTRLLINE, NULL);
1765     }
1768 /**
1769  * Ensure the given handle of the node is visible/invisible, update its screen position
1770  */
1771 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1773     g_assert(node != NULL);
1775    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1776     NRPathcode code = sp_node_path_code_from_side(node, side);
1778     show_handle = show_handle && (code == NR_CURVETO) && (Geom::L2(side->pos - node->pos) > 1e-6);
1780     if (show_handle) {
1781         if (!side->knot) { // No handle knot at all
1782             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1783             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1784             side->knot->pos = side->pos;
1785             if (side->knot->item)
1786                 SP_CTRL(side->knot->item)->moveto(side->pos);
1787             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1788             sp_knot_show(side->knot);
1789         } else {
1790             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1791                 if (fire_move_signals) {
1792                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1793                 } else {
1794                     sp_knot_moveto(side->knot, side->pos);
1795                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1796                 }
1797             }
1798             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1799                 sp_knot_show(side->knot);
1800             }
1801         }
1802         sp_canvas_item_show(side->line);
1803     } else {
1804         if (side->knot) {
1805             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1806                 sp_knot_hide(side->knot);
1807             }
1808         }
1809         if (side->line) {
1810             sp_canvas_item_hide(side->line);
1811         }
1812     }
1815 /**
1816  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1817  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1818  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1819  * updated; otherwise, just move the knots silently (used in batch moves).
1820  */
1821 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1823     g_assert(node != NULL);
1825     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1826         sp_knot_show(node->knot);
1827     }
1829     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1830         if (fire_move_signals)
1831             sp_knot_set_position(node->knot, node->pos, 0);
1832         else
1833             sp_knot_moveto(node->knot, node->pos);
1834     }
1836     gboolean show_handles = node->selected;
1837     if (node->p.other != NULL) {
1838         if (node->p.other->selected) show_handles = TRUE;
1839     }
1840     if (node->n.other != NULL) {
1841         if (node->n.other->selected) show_handles = TRUE;
1842     }
1844     if (node->subpath->nodepath->show_handles == false)
1845         show_handles = FALSE;
1847     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1848     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1851 /**
1852  * Call sp_node_update_handles() for all nodes on subpath.
1853  */
1854 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1856     g_assert(subpath != NULL);
1858     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1859         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1860     }
1863 /**
1864  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1865  */
1866 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1868     g_assert(nodepath != NULL);
1870     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1871         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1872     }
1875 void
1876 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1878     if (nodepath) {
1879         nodepath->show_handles = show;
1880         sp_nodepath_update_handles(nodepath);
1881     }
1884 /**
1885  * Adds all selected nodes in nodepath to list.
1886  */
1887 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1889     StlConv<Node *>::list(l, selected);
1890 /// \todo this adds a copying, rework when the selection becomes a stl list
1893 /**
1894  * Align selected nodes on the specified axis.
1895  */
1896 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1898     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1899         return;
1900     }
1902     if ( !nodepath->selected->next ) { // only one node selected
1903         return;
1904     }
1905    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1906     Geom::Point dest(pNode->pos);
1907     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1908         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1909         if (pNode) {
1910             dest[axis] = pNode->pos[axis];
1911             sp_node_moveto(pNode, dest);
1912         }
1913     }
1915     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1918 /// Helper struct.
1919 struct NodeSort
1921    Inkscape::NodePath::Node *_node;
1922     Geom::Coord _coord;
1923     /// \todo use vectorof pointers instead of calling copy ctor
1924     NodeSort(Inkscape::NodePath::Node *node, Geom::Dim2 axis) :
1925         _node(node), _coord(node->pos[axis])
1926     {}
1928 };
1930 static bool operator<(NodeSort const &a, NodeSort const &b)
1932     return (a._coord < b._coord);
1935 /**
1936  * Distribute selected nodes on the specified axis.
1937  */
1938 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1940     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1941         return;
1942     }
1944     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1945         return;
1946     }
1948    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1949     std::vector<NodeSort> sorted;
1950     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1951         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1952         if (pNode) {
1953             NodeSort n(pNode, axis);
1954             sorted.push_back(n);
1955             //dest[axis] = pNode->pos[axis];
1956             //sp_node_moveto(pNode, dest);
1957         }
1958     }
1959     std::sort(sorted.begin(), sorted.end());
1960     unsigned int len = sorted.size();
1961     //overall bboxes span
1962     float dist = (sorted.back()._coord -
1963                   sorted.front()._coord);
1964     //new distance between each bbox
1965     float step = (dist) / (len - 1);
1966     float pos = sorted.front()._coord;
1967     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1968           it < sorted.end();
1969           it ++ )
1970     {
1971         Geom::Point dest((*it)._node->pos);
1972         dest[axis] = pos;
1973         sp_node_moveto((*it)._node, dest);
1974         pos += step;
1975     }
1977     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1981 /**
1982  * Call sp_nodepath_line_add_node() for all selected segments.
1983  */
1984 void
1985 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1987     if (!nodepath) {
1988         return;
1989     }
1991     GList *nl = NULL;
1993     int n_added = 0;
1995     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1996        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1997         g_assert(t->selected);
1998         if (t->p.other && t->p.other->selected) {
1999             nl = g_list_prepend(nl, t);
2000         }
2001     }
2003     while (nl) {
2004        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
2005        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
2006        sp_nodepath_node_select(n, TRUE, FALSE);
2007        n_added ++;
2008        nl = g_list_remove(nl, t);
2009     }
2011     /** \todo fixme: adjust ? */
2012     sp_nodepath_update_handles(nodepath);
2014     if (n_added > 1) {
2015         sp_nodepath_update_repr(nodepath, _("Add nodes"));
2016     } else if (n_added > 0) {
2017         sp_nodepath_update_repr(nodepath, _("Add node"));
2018     }
2020     sp_nodepath_update_statusbar(nodepath);
2023 /**
2024  * Select segment nearest to point
2025  */
2026 void
2027 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p, bool toggle)
2029     if (!nodepath) {
2030         return;
2031     }
2033     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2034     Geom::PathVector const &pathv = curve->get_pathvector();
2035     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2036     if (!pvpos) {
2037         g_print ("Possible error?\n");
2038         return;
2039     }
2041     // calculate index for nodepath's representation.
2042     unsigned int segment_index = floor(pvpos->t) + 1;
2043     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2044         segment_index += pathv[i].size() + 1;
2045         if (pathv[i].closed()) {
2046             segment_index += 1;
2047         }
2048     }
2050     curve->unref();
2052     //find segment to segment
2053     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2055     //fixme: this can return NULL, so check before proceeding.
2056     g_return_if_fail(e != NULL);
2058     gboolean force = FALSE;
2059     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
2060         force = TRUE;
2061     }
2062     sp_nodepath_node_select(e, (gboolean) toggle, force);
2063     if (e->p.other)
2064         sp_nodepath_node_select(e->p.other, TRUE, force);
2066     sp_nodepath_update_handles(nodepath);
2068     sp_nodepath_update_statusbar(nodepath);
2071 /**
2072  * Add a node nearest to point
2073  */
2074 void
2075 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p)
2077     if (!nodepath) {
2078         return;
2079     }
2081     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2082     Geom::PathVector const &pathv = curve->get_pathvector();
2083     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2084     if (!pvpos) {
2085         g_print ("Possible error?\n");
2086         return;
2087     }
2089     // calculate index for nodepath's representation.
2090     double int_part;
2091     double t = std::modf(pvpos->t, &int_part);
2092     unsigned int segment_index = (unsigned int)int_part + 1;
2093     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2094         segment_index += pathv[i].size() + 1;
2095         if (pathv[i].closed()) {
2096             segment_index += 1;
2097         }
2098     }
2100     curve->unref();
2102     //find segment to split
2103     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2104     if (!e) {
2105         return;
2106     }
2108     //don't know why but t seems to flip for lines
2109     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2110         t = 1.0 - t;
2111     }
2113     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2114     sp_nodepath_node_select(n, FALSE, TRUE);
2116     /* fixme: adjust ? */
2117     sp_nodepath_update_handles(nodepath);
2119     sp_nodepath_update_repr(nodepath, _("Add node"));
2121     sp_nodepath_update_statusbar(nodepath);
2124 /*
2125  * Adjusts a segment so that t moves by a certain delta for dragging
2126  * converts lines to curves
2127  *
2128  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2129  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2130  */
2131 void
2132 sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, Geom::Point delta)
2134     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node);
2136     //fixme: e and e->p can be NULL, so check for those before proceeding
2137     g_return_if_fail(e != NULL);
2138     g_return_if_fail(&e->p != NULL);
2140     if (e->type == Inkscape::NodePath::NODE_AUTO) {
2141         e->type = Inkscape::NodePath::NODE_SMOOTH;
2142         sp_nodepath_update_node_knot (e);
2143     }
2144     if (e->p.other->type == Inkscape::NodePath::NODE_AUTO) {
2145         e->p.other->type = Inkscape::NodePath::NODE_SMOOTH;
2146         sp_nodepath_update_node_knot (e->p.other);
2147     }
2149     /* feel good is an arbitrary parameter that distributes the delta between handles
2150      * if t of the drag point is less than 1/6 distance form the endpoint only
2151      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2152      */
2153     double feel_good;
2154     if (t <= 1.0 / 6.0)
2155         feel_good = 0;
2156     else if (t <= 0.5)
2157         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2158     else if (t <= 5.0 / 6.0)
2159         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2160     else
2161         feel_good = 1;
2163     //if we're dragging a line convert it to a curve
2164     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2165         sp_nodepath_set_line_type(e, NR_CURVETO);
2166     }
2168     Geom::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2169     Geom::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2170     e->p.other->n.pos += offsetcoord0;
2171     e->p.pos += offsetcoord1;
2173     // adjust handles of adjacent nodes where necessary
2174     sp_node_adjust_handle(e,1);
2175     sp_node_adjust_handle(e->p.other,-1);
2177     sp_nodepath_update_handles(e->subpath->nodepath);
2179     update_object(e->subpath->nodepath);
2181     sp_nodepath_update_statusbar(e->subpath->nodepath);
2185 /**
2186  * Call sp_nodepath_break() for all selected segments.
2187  */
2188 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2190     if (!nodepath) return;
2192     GList *tempin = g_list_copy(nodepath->selected);
2193     GList *temp = NULL;
2194     for (GList *l = tempin; l != NULL; l = l->next) {
2195        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2196        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2197         if (nn == NULL) continue; // no break, no new node
2198         temp = g_list_prepend(temp, nn);
2199     }
2200     g_list_free(tempin);
2202     if (temp) {
2203         sp_nodepath_deselect(nodepath);
2204     }
2205     for (GList *l = temp; l != NULL; l = l->next) {
2206         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2207     }
2209     sp_nodepath_update_handles(nodepath);
2211     sp_nodepath_update_repr(nodepath, _("Break path"));
2214 /**
2215  * Duplicate the selected node(s).
2216  */
2217 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2219     if (!nodepath) {
2220         return;
2221     }
2223     GList *temp = NULL;
2224     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2225        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2226        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2227         if (nn == NULL) continue; // could not duplicate
2228         temp = g_list_prepend(temp, nn);
2229     }
2231     if (temp) {
2232         sp_nodepath_deselect(nodepath);
2233     }
2234     for (GList *l = temp; l != NULL; l = l->next) {
2235         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2236     }
2238     sp_nodepath_update_handles(nodepath);
2240     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2243 /**
2244  *  Internal function to join two nodes by merging them into one.
2245  */
2246 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2248     /* a and b are endpoints */
2250     // if one of the two nodes is mouseovered, fix its position
2251     Geom::Point c;
2252     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2253         c = a->pos;
2254     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2255         c = b->pos;
2256     } else {
2257         // otherwise, move joined node to the midpoint
2258         c = (a->pos + b->pos) / 2;
2259     }
2261     if (a->subpath == b->subpath) {
2262        Inkscape::NodePath::SubPath *sp = a->subpath;
2263         sp_nodepath_subpath_close(sp);
2264         sp_node_moveto (sp->first, c);
2266         sp_nodepath_update_handles(sp->nodepath);
2267         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2268         return;
2269     }
2271     /* a and b are separate subpaths */
2272     Inkscape::NodePath::SubPath *sa = a->subpath;
2273     Inkscape::NodePath::SubPath *sb = b->subpath;
2274     Geom::Point p;
2275     Inkscape::NodePath::Node *n;
2276     NRPathcode code;
2277     if (a == sa->first) {
2278         // we will now reverse sa, so that a is its last node, not first, and drop that node
2279         p = sa->first->n.pos;
2280         code = (NRPathcode)sa->first->n.other->code;
2281         // create new subpath
2282        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2283        // create a first moveto node on it
2284         n = sa->last;
2285         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2286         n = n->p.other;
2287         if (n == sa->first) n = NULL;
2288         while (n) {
2289             // copy the rest of the nodes from sa to t, going backwards
2290             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2291             n = n->p.other;
2292             if (n == sa->first) n = NULL;
2293         }
2294         // replace sa with t
2295         sp_nodepath_subpath_destroy(sa);
2296         sa = t;
2297     } else if (a == sa->last) {
2298         // a is already last, just drop it
2299         p = sa->last->p.pos;
2300         code = (NRPathcode)sa->last->code;
2301         sp_nodepath_node_destroy(sa->last);
2302     } else {
2303         code = NR_END;
2304         g_assert_not_reached();
2305     }
2307     if (b == sb->first) {
2308         // copy all nodes from b to a, forward
2309         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2310         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2311             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2312         }
2313     } else if (b == sb->last) {
2314         // copy all nodes from b to a, backward
2315         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2316         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2317             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2318         }
2319     } else {
2320         g_assert_not_reached();
2321     }
2322     /* and now destroy sb */
2324     sp_nodepath_subpath_destroy(sb);
2326     sp_nodepath_update_handles(sa->nodepath);
2328     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2330     sp_nodepath_update_statusbar(nodepath);
2333 /**
2334  *  Internal function to join two nodes by adding a segment between them.
2335  */
2336 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2338     if (a->subpath == b->subpath) {
2339        Inkscape::NodePath::SubPath *sp = a->subpath;
2341         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2342         sp->closed = TRUE;
2344         sp->first->p.other = sp->last;
2345         sp->last->n.other  = sp->first;
2347         sp_node_handle_mirror_p_to_n(sp->last);
2348         sp_node_handle_mirror_n_to_p(sp->first);
2350         sp->first->code = sp->last->code;
2351         sp->first       = sp->last;
2353         sp_nodepath_update_handles(sp->nodepath);
2355         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2357         return;
2358     }
2360     /* a and b are separate subpaths */
2361     Inkscape::NodePath::SubPath *sa = a->subpath;
2362     Inkscape::NodePath::SubPath *sb = b->subpath;
2364     Inkscape::NodePath::Node *n;
2365     Geom::Point p;
2366     NRPathcode code;
2367     if (a == sa->first) {
2368         code = (NRPathcode) sa->first->n.other->code;
2369        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2370         n = sa->last;
2371         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2372         for (n = n->p.other; n != NULL; n = n->p.other) {
2373             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2374         }
2375         sp_nodepath_subpath_destroy(sa);
2376         sa = t;
2377     } else if (a == sa->last) {
2378         code = (NRPathcode)sa->last->code;
2379     } else {
2380         code = NR_END;
2381         g_assert_not_reached();
2382     }
2384     if (b == sb->first) {
2385         n = sb->first;
2386         sp_node_handle_mirror_p_to_n(sa->last);
2387         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2388         sp_node_handle_mirror_n_to_p(sa->last);
2389         for (n = n->n.other; n != NULL; n = n->n.other) {
2390             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2391         }
2392     } else if (b == sb->last) {
2393         n = sb->last;
2394         sp_node_handle_mirror_p_to_n(sa->last);
2395         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2396         sp_node_handle_mirror_n_to_p(sa->last);
2397         for (n = n->p.other; n != NULL; n = n->p.other) {
2398             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2399         }
2400     } else {
2401         g_assert_not_reached();
2402     }
2403     /* and now destroy sb */
2405     sp_nodepath_subpath_destroy(sb);
2407     sp_nodepath_update_handles(sa->nodepath);
2409     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2412 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2414 /**
2415  * Internal function to handle joining two nodes.
2416  */
2417 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2419     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2421     if (g_list_length(nodepath->selected) != 2) {
2422         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2423         return;
2424     }
2426     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2427     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2429     g_assert(a != b);
2430     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2431         // someone tried to join an orphan node (i.e. a single-node subpath).
2432         // this is not worth an error message, just fail silently.
2433         return;
2434     }
2436     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2437         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2438         return;
2439     }
2441     switch(mode) {
2442         case NODE_JOIN_ENDPOINTS:
2443             do_node_selected_join(nodepath, a, b);
2444             break;
2445         case NODE_JOIN_SEGMENT:
2446             do_node_selected_join_segment(nodepath, a, b);
2447             break;
2448     }
2451 /**
2452  *  Join two nodes by merging them into one.
2453  */
2454 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2456     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2459 /**
2460  *  Join two nodes by adding a segment between them.
2461  */
2462 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2464     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2467 /**
2468  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2469  */
2470 void sp_node_delete_preserve(GList *nodes_to_delete)
2472     GSList *nodepaths = NULL;
2474     while (nodes_to_delete) {
2475         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2476         Inkscape::NodePath::SubPath *sp = node->subpath;
2477         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2478         Inkscape::NodePath::Node *sample_cursor = NULL;
2479         Inkscape::NodePath::Node *sample_end = NULL;
2480         Inkscape::NodePath::Node *delete_cursor = node;
2481         bool just_delete = false;
2483         //find the start of this contiguous selection
2484         //move left to the first node that is not selected
2485         //or the start of the non-closed path
2486         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2487             delete_cursor = curr;
2488         }
2490         //just delete at the beginning of an open path
2491         if (!delete_cursor->p.other) {
2492             sample_cursor = delete_cursor;
2493             just_delete = true;
2494         } else {
2495             sample_cursor = delete_cursor->p.other;
2496         }
2498         //calculate points for each segment
2499         int rate = 5;
2500         float period = 1.0 / rate;
2501         std::vector<Geom::Point> data;
2502         if (!just_delete) {
2503             data.push_back(sample_cursor->pos);
2504             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2505                 //just delete at the end of an open path
2506                 if (!sp->closed && curr == sp->last) {
2507                     just_delete = true;
2508                     break;
2509                 }
2511                 //sample points on the contiguous selected segment
2512                 Geom::Point *bez;
2513                 bez = new Geom::Point [4];
2514                 bez[0] = curr->pos;
2515                 bez[1] = curr->n.pos;
2516                 bez[2] = curr->n.other->p.pos;
2517                 bez[3] = curr->n.other->pos;
2518                 for (int i=1; i<rate; i++) {
2519                     gdouble t = i * period;
2520                     Geom::Point p = bezier_pt(3, bez, t);
2521                     data.push_back(p);
2522                 }
2523                 data.push_back(curr->n.other->pos);
2525                 sample_end = curr->n.other;
2526                 //break if we've come full circle or hit the end of the selection
2527                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2528                     break;
2529                 }
2530             }
2531         }
2533         if (!just_delete) {
2534             //calculate the best fitting single segment and adjust the endpoints
2535             Geom::Point *adata;
2536             adata = new Geom::Point [data.size()];
2537             copy(data.begin(), data.end(), adata);
2539             Geom::Point *bez;
2540             bez = new Geom::Point [4];
2541             //would decreasing error create a better fitting approximation?
2542             gdouble error = 1.0;
2543             gint ret;
2544             ret = Geom::bezier_fit_cubic (bez, adata, data.size(), error);
2546             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2547             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2548             //the resulting nodes behave as expected.
2549             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2550                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2551             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2552                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2554             //adjust endpoints
2555             sample_cursor->n.pos = bez[1];
2556             sample_end->p.pos = bez[2];
2557         }
2559         //destroy this contiguous selection
2560         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2561             Inkscape::NodePath::Node *temp = delete_cursor;
2562             if (delete_cursor->n.other == delete_cursor) {
2563                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2564                 delete_cursor = NULL;
2565             } else {
2566                 delete_cursor = delete_cursor->n.other;
2567             }
2568             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2569             sp_nodepath_node_destroy(temp);
2570         }
2572         sp_nodepath_update_handles(nodepath);
2574         if (!g_slist_find(nodepaths, nodepath))
2575             nodepaths = g_slist_prepend (nodepaths, nodepath);
2576     }
2578     for (GSList *i = nodepaths; i; i = i->next) {
2579         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2580         // different nodepaths will give us one undo event per nodepath
2581         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2583         // if the entire nodepath is removed, delete the selected object.
2584         if (nodepath->subpaths == NULL ||
2585             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2586             //at least 2
2587             sp_nodepath_get_node_count(nodepath) < 2) {
2588             SPDocument *document = sp_desktop_document (nodepath->desktop);
2589             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2590             //delete this nodepath's object, not the entire selection! (though at this time, this
2591             //does not matter)
2592             sp_selection_delete(nodepath->desktop);
2593             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2594                               _("Delete nodes"));
2595         } else {
2596             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2597             sp_nodepath_update_statusbar(nodepath);
2598         }
2599     }
2601     g_slist_free (nodepaths);
2604 /**
2605  * Delete one or more selected nodes.
2606  */
2607 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2609     if (!nodepath) return;
2610     if (!nodepath->selected) return;
2612     /** \todo fixme: do it the right way */
2613     while (nodepath->selected) {
2614        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2615         sp_nodepath_node_destroy(node);
2616     }
2619     //clean up the nodepath (such as for trivial subpaths)
2620     sp_nodepath_cleanup(nodepath);
2622     sp_nodepath_update_handles(nodepath);
2624     // if the entire nodepath is removed, delete the selected object.
2625     if (nodepath->subpaths == NULL ||
2626         sp_nodepath_get_node_count(nodepath) < 2) {
2627         SPDocument *document = sp_desktop_document (nodepath->desktop);
2628         sp_selection_delete(nodepath->desktop);
2629         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2630                           _("Delete nodes"));
2631         return;
2632     }
2634     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2636     sp_nodepath_update_statusbar(nodepath);
2639 /**
2640  * Delete one or more segments between two selected nodes.
2641  * This is the code for 'split'.
2642  */
2643 void
2644 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2646    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2647    Inkscape::NodePath::Node *curr, *next;     //Iterators
2649     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2651     if (g_list_length(nodepath->selected) != 2) {
2652         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2653                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2654         return;
2655     }
2657     //Selected nodes, not inclusive
2658    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2659    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2661     if ( ( a==b)                       ||  //same node
2662          (a->subpath  != b->subpath )  ||  //not the same path
2663          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2664          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2665     {
2666         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2667                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2668         return;
2669     }
2671     //###########################################
2672     //# BEGIN EDITS
2673     //###########################################
2674     //##################################
2675     //# CLOSED PATH
2676     //##################################
2677     if (a->subpath->closed) {
2680         gboolean reversed = FALSE;
2682         //Since we can go in a circle, we need to find the shorter distance.
2683         //  a->b or b->a
2684         start = end = NULL;
2685         int distance    = 0;
2686         int minDistance = 0;
2687         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2688             if (curr==b) {
2689                 //printf("a to b:%d\n", distance);
2690                 start = a;//go from a to b
2691                 end   = b;
2692                 minDistance = distance;
2693                 //printf("A to B :\n");
2694                 break;
2695             }
2696             distance++;
2697         }
2699         //try again, the other direction
2700         distance = 0;
2701         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2702             if (curr==a) {
2703                 //printf("b to a:%d\n", distance);
2704                 if (distance < minDistance) {
2705                     start    = b;  //we go from b to a
2706                     end      = a;
2707                     reversed = TRUE;
2708                     //printf("B to A\n");
2709                 }
2710                 break;
2711             }
2712             distance++;
2713         }
2716         //Copy everything from 'end' to 'start' to a new subpath
2717        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2718         for (curr=end ; curr ; curr=curr->n.other) {
2719             NRPathcode code = (NRPathcode) curr->code;
2720             if (curr == end)
2721                 code = NR_MOVETO;
2722             sp_nodepath_node_new(t, NULL,
2723                                  (Inkscape::NodePath::NodeType)curr->type, code,
2724                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2725             if (curr == start)
2726                 break;
2727         }
2728         sp_nodepath_subpath_destroy(a->subpath);
2731     }
2735     //##################################
2736     //# OPEN PATH
2737     //##################################
2738     else {
2740         //We need to get the direction of the list between A and B
2741         //Can we walk from a to b?
2742         start = end = NULL;
2743         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2744             if (curr==b) {
2745                 start = a;  //did it!  we go from a to b
2746                 end   = b;
2747                 //printf("A to B\n");
2748                 break;
2749             }
2750         }
2751         if (!start) {//didn't work?  let's try the other direction
2752             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2753                 if (curr==a) {
2754                     start = b;  //did it!  we go from b to a
2755                     end   = a;
2756                     //printf("B to A\n");
2757                     break;
2758                 }
2759             }
2760         }
2761         if (!start) {
2762             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2763                                                      _("Cannot find path between nodes."));
2764             return;
2765         }
2769         //Copy everything after 'end' to a new subpath
2770        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2771         for (curr=end ; curr ; curr=curr->n.other) {
2772             NRPathcode code = (NRPathcode) curr->code;
2773             if (curr == end)
2774                 code = NR_MOVETO;
2775             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2776                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2777         }
2779         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2780         for (curr = start->n.other ; curr  ; curr=next) {
2781             next = curr->n.other;
2782             sp_nodepath_node_destroy(curr);
2783         }
2785     }
2786     //###########################################
2787     //# END EDITS
2788     //###########################################
2790     //clean up the nodepath (such as for trivial subpaths)
2791     sp_nodepath_cleanup(nodepath);
2793     sp_nodepath_update_handles(nodepath);
2795     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2797     sp_nodepath_update_statusbar(nodepath);
2800 /**
2801  * Call sp_nodepath_set_line() for all selected segments.
2802  */
2803 void
2804 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2806     if (nodepath == NULL) return;
2808     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2809        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2810         g_assert(n->selected);
2811         if (n->p.other && n->p.other->selected) {
2812             sp_nodepath_set_line_type(n, code);
2813         }
2814     }
2816     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2819 /**
2820  * Call sp_nodepath_convert_node_type() for all selected nodes.
2821  */
2822 void
2823 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2825     if (nodepath == NULL) return;
2827     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2829     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2830         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2831     }
2833     sp_nodepath_update_repr(nodepath, _("Change node type"));
2836 /**
2837  * Change select status of node, update its own and neighbour handles.
2838  */
2839 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2841     node->selected = selected;
2843     if (selected) {
2844         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 11 : 9);
2845         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2846         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2847         sp_knot_update_ctrl(node->knot);
2848     } else {
2849         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 9 : 7);
2850         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2851         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2852         sp_knot_update_ctrl(node->knot);
2853     }
2855     sp_node_update_handles(node);
2856     if (node->n.other) sp_node_update_handles(node->n.other);
2857     if (node->p.other) sp_node_update_handles(node->p.other);
2860 /**
2861 \brief Select a node
2862 \param node     The node to select
2863 \param incremental   If true, add to selection, otherwise deselect others
2864 \param override   If true, always select this node, otherwise toggle selected status
2865 */
2866 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2868     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2870     if (incremental) {
2871         if (override) {
2872             if (!g_list_find(nodepath->selected, node)) {
2873                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2874             }
2875             sp_node_set_selected(node, TRUE);
2876         } else { // toggle
2877             if (node->selected) {
2878                 g_assert(g_list_find(nodepath->selected, node));
2879                 nodepath->selected = g_list_remove(nodepath->selected, node);
2880             } else {
2881                 g_assert(!g_list_find(nodepath->selected, node));
2882                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2883             }
2884             sp_node_set_selected(node, !node->selected);
2885         }
2886     } else {
2887         sp_nodepath_deselect(nodepath);
2888         nodepath->selected = g_list_prepend(nodepath->selected, node);
2889         sp_node_set_selected(node, TRUE);
2890     }
2892     sp_nodepath_update_statusbar(nodepath);
2896 /**
2897 \brief Deselect all nodes in the nodepath
2898 */
2899 void
2900 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2902     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2904     while (nodepath->selected) {
2905         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2906         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2907     }
2908     sp_nodepath_update_statusbar(nodepath);
2911 /**
2912 \brief Select or invert selection of all nodes in the nodepath
2913 */
2914 void
2915 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2917     if (!nodepath) return;
2919     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2920        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2921         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2922            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2923            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2924         }
2925     }
2928 /**
2929  * If nothing selected, does the same as sp_nodepath_select_all();
2930  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2931  * (i.e., similar to "select all in layer", with the "selected" subpaths
2932  * being treated as "layers" in the path).
2933  */
2934 void
2935 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2937     if (!nodepath) return;
2939     if (g_list_length (nodepath->selected) == 0) {
2940         sp_nodepath_select_all (nodepath, invert);
2941         return;
2942     }
2944     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2945     GSList *subpaths = NULL;
2947     for (GList *l = copy; l != NULL; l = l->next) {
2948         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2949         Inkscape::NodePath::SubPath *subpath = n->subpath;
2950         if (!g_slist_find (subpaths, subpath))
2951             subpaths = g_slist_prepend (subpaths, subpath);
2952     }
2954     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2955         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2956         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2957             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2958             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2959         }
2960     }
2962     g_slist_free (subpaths);
2963     g_list_free (copy);
2966 /**
2967  * \brief Select the node after the last selected; if none is selected,
2968  * select the first within path.
2969  */
2970 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2972     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2974    Inkscape::NodePath::Node *last = NULL;
2975     if (nodepath->selected) {
2976         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2977            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2978             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2979             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2980                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2981                 if (node->selected) {
2982                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2983                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2984                             if (spl->next) { // there's a next subpath
2985                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2986                                 last = subpath_next->first;
2987                             } else if (spl->prev) { // there's a previous subpath
2988                                 last = NULL; // to be set later to the first node of first subpath
2989                             } else {
2990                                 last = node->n.other;
2991                             }
2992                         } else {
2993                             last = node->n.other;
2994                         }
2995                     } else {
2996                         if (node->n.other) {
2997                             last = node->n.other;
2998                         } else {
2999                             if (spl->next) { // there's a next subpath
3000                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
3001                                 last = subpath_next->first;
3002                             } else if (spl->prev) { // there's a previous subpath
3003                                 last = NULL; // to be set later to the first node of first subpath
3004                             } else {
3005                                 last = (Inkscape::NodePath::Node *) subpath->first;
3006                             }
3007                         }
3008                     }
3009                 }
3010             }
3011         }
3012         sp_nodepath_deselect(nodepath);
3013     }
3015     if (last) { // there's at least one more node after selected
3016         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3017     } else { // no more nodes, select the first one in first subpath
3018        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
3019         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
3020     }
3023 /**
3024  * \brief Select the node before the first selected; if none is selected,
3025  * select the last within path
3026  */
3027 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
3029     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
3031    Inkscape::NodePath::Node *last = NULL;
3032     if (nodepath->selected) {
3033         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
3034            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3035             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
3036                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3037                 if (node->selected) {
3038                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
3039                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
3040                             if (spl->prev) { // there's a prev subpath
3041                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3042                                 last = subpath_prev->last;
3043                             } else if (spl->next) { // there's a next subpath
3044                                 last = NULL; // to be set later to the last node of last subpath
3045                             } else {
3046                                 last = node->p.other;
3047                             }
3048                         } else {
3049                             last = node->p.other;
3050                         }
3051                     } else {
3052                         if (node->p.other) {
3053                             last = node->p.other;
3054                         } else {
3055                             if (spl->prev) { // there's a prev subpath
3056                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3057                                 last = subpath_prev->last;
3058                             } else if (spl->next) { // there's a next subpath
3059                                 last = NULL; // to be set later to the last node of last subpath
3060                             } else {
3061                                 last = (Inkscape::NodePath::Node *) subpath->last;
3062                             }
3063                         }
3064                     }
3065                 }
3066             }
3067         }
3068         sp_nodepath_deselect(nodepath);
3069     }
3071     if (last) { // there's at least one more node before selected
3072         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3073     } else { // no more nodes, select the last one in last subpath
3074         GList *spl = g_list_last(nodepath->subpaths);
3075        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3076         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
3077     }
3080 /**
3081  * \brief Select all nodes that are within the rectangle.
3082  */
3083 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, Geom::Rect const &b, gboolean incremental)
3085     if (!incremental) {
3086         sp_nodepath_deselect(nodepath);
3087     }
3089     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3090        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3091         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3092            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3094             if (b.contains(node->pos)) {
3095                 sp_nodepath_node_select(node, TRUE, TRUE);
3096             }
3097         }
3098     }
3102 void
3103 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3105     g_assert (n);
3106     g_assert (nodepath);
3107     g_assert (n->subpath->nodepath == nodepath);
3109     if (g_list_length (nodepath->selected) == 0) {
3110         if (grow > 0) {
3111             sp_nodepath_node_select(n, TRUE, TRUE);
3112         }
3113         return;
3114     }
3116     if (g_list_length (nodepath->selected) == 1) {
3117         if (grow < 0) {
3118             sp_nodepath_deselect (nodepath);
3119             return;
3120         }
3121     }
3123         double n_sel_range = 0, p_sel_range = 0;
3124             Inkscape::NodePath::Node *farthest_n_node = n;
3125             Inkscape::NodePath::Node *farthest_p_node = n;
3127         // Calculate ranges
3128         {
3129             double n_range = 0, p_range = 0;
3130             bool n_going = true, p_going = true;
3131             Inkscape::NodePath::Node *n_node = n;
3132             Inkscape::NodePath::Node *p_node = n;
3133             do {
3134                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3135                 if (n_node && n_going)
3136                     n_node = n_node->n.other;
3137                 if (n_node == NULL) {
3138                     n_going = false;
3139                 } else {
3140                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3141                     if (n_node->selected) {
3142                         n_sel_range = n_range;
3143                         farthest_n_node = n_node;
3144                     }
3145                     if (n_node == p_node) {
3146                         n_going = false;
3147                         p_going = false;
3148                     }
3149                 }
3150                 if (p_node && p_going)
3151                     p_node = p_node->p.other;
3152                 if (p_node == NULL) {
3153                     p_going = false;
3154                 } else {
3155                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3156                     if (p_node->selected) {
3157                         p_sel_range = p_range;
3158                         farthest_p_node = p_node;
3159                     }
3160                     if (p_node == n_node) {
3161                         n_going = false;
3162                         p_going = false;
3163                     }
3164                 }
3165             } while (n_going || p_going);
3166         }
3168     if (grow > 0) {
3169         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3170                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3171         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3172                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3173         }
3174     } else {
3175         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3176                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3177         } else if (farthest_p_node && farthest_p_node->selected) {
3178                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3179         }
3180     }
3183 void
3184 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3186     g_assert (n);
3187     g_assert (nodepath);
3188     g_assert (n->subpath->nodepath == nodepath);
3190     if (g_list_length (nodepath->selected) == 0) {
3191         if (grow > 0) {
3192             sp_nodepath_node_select(n, TRUE, TRUE);
3193         }
3194         return;
3195     }
3197     if (g_list_length (nodepath->selected) == 1) {
3198         if (grow < 0) {
3199             sp_nodepath_deselect (nodepath);
3200             return;
3201         }
3202     }
3204     Inkscape::NodePath::Node *farthest_selected = NULL;
3205     double farthest_dist = 0;
3207     Inkscape::NodePath::Node *closest_unselected = NULL;
3208     double closest_dist = NR_HUGE;
3210     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3211        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3212         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3213            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3214            if (node == n)
3215                continue;
3216            if (node->selected) {
3217                if (Geom::L2(node->pos - n->pos) > farthest_dist) {
3218                    farthest_dist = Geom::L2(node->pos - n->pos);
3219                    farthest_selected = node;
3220                }
3221            } else {
3222                if (Geom::L2(node->pos - n->pos) < closest_dist) {
3223                    closest_dist = Geom::L2(node->pos - n->pos);
3224                    closest_unselected = node;
3225                }
3226            }
3227         }
3228     }
3230     if (grow > 0) {
3231         if (closest_unselected) {
3232             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3233         }
3234     } else {
3235         if (farthest_selected) {
3236             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3237         }
3238     }
3242 /**
3243 \brief  Saves all nodes' and handles' current positions in their origin members
3244 */
3245 void
3246 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3248     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3249        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3250         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3251            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3252            n->origin = n->pos;
3253            n->p.origin = n->p.pos;
3254            n->n.origin = n->n.pos;
3255         }
3256     }
3259 /**
3260 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3261 */
3262 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3264     GList *r = NULL;
3265     if (nodepath->selected) {
3266         guint i = 0;
3267         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3268             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3269             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3270                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3271                 i++;
3272                 if (node->selected) {
3273                     r = g_list_append(r, GINT_TO_POINTER(i));
3274                 }
3275             }
3276         }
3277     }
3278     return r;
3281 /**
3282 \brief  Restores selection by selecting nodes whose positions are in the list
3283 */
3284 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3286     sp_nodepath_deselect(nodepath);
3288     guint i = 0;
3289     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3290        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3291         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3292            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3293             i++;
3294             if (g_list_find(r, GINT_TO_POINTER(i))) {
3295                 sp_nodepath_node_select(node, TRUE, TRUE);
3296             }
3297         }
3298     }
3302 /**
3303 \brief Adjusts handle according to node type and line code.
3304 */
3305 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3307     g_assert(node);
3309     // nothing to do for auto nodes (sp_node_adjust_handles() does the job)
3310     if (node->type == Inkscape::NodePath::NODE_AUTO)
3311         return;
3313    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3314    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3316    // nothing to do if we are an end node
3317     if (me->other == NULL) return;
3318     if (other->other == NULL) return;
3320     // nothing to do if we are a cusp node
3321     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3323     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3324     NRPathcode mecode;
3325     if (which_adjust == 1) {
3326         mecode = (NRPathcode)me->other->code;
3327     } else {
3328         mecode = (NRPathcode)node->code;
3329     }
3330     if (mecode == NR_LINETO) return;
3332     if (sp_node_side_is_line(node, other)) {
3333         // other is a line, and we are either smooth or symm
3334        Inkscape::NodePath::Node *othernode = other->other;
3335         double len = Geom::L2(me->pos - node->pos);
3336         Geom::Point delta = node->pos - othernode->pos;
3337         double linelen = Geom::L2(delta);
3338         if (linelen < 1e-18)
3339             return;
3340         me->pos = node->pos + (len / linelen)*delta;
3341         return;
3342     }
3344     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3345         // symmetrize
3346         me->pos = 2 * node->pos - other->pos;
3347         return;
3348     } else {
3349         // smoothify
3350         double len = Geom::L2(me->pos - node->pos);
3351         Geom::Point delta = other->pos - node->pos;
3352         double otherlen = Geom::L2(delta);
3353         if (otherlen < 1e-18) return;
3354         me->pos = node->pos - (len / otherlen) * delta;
3355     }
3358 /**
3359  \brief Adjusts both handles according to node type and line code
3360  */
3361 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3363     g_assert(node);
3365     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3367     /* we are either smooth or symm */
3369     if (node->p.other == NULL) return;
3370     if (node->n.other == NULL) return;
3372     if (node->type == Inkscape::NodePath::NODE_AUTO) {
3373         sp_node_adjust_handles_auto(node);
3374         return;
3375     }
3377     if (sp_node_side_is_line(node, &node->p)) {
3378         sp_node_adjust_handle(node, 1);
3379         return;
3380     }
3382     if (sp_node_side_is_line(node, &node->n)) {
3383         sp_node_adjust_handle(node, -1);
3384         return;
3385     }
3387     /* both are curves */
3388     Geom::Point const delta( node->n.pos - node->p.pos );
3390     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3391         node->p.pos = node->pos - delta / 2;
3392         node->n.pos = node->pos + delta / 2;
3393         return;
3394     }
3396     /* We are smooth */
3397     double plen = Geom::L2(node->p.pos - node->pos);
3398     if (plen < 1e-18) return;
3399     double nlen = Geom::L2(node->n.pos - node->pos);
3400     if (nlen < 1e-18) return;
3401     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3402     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3405 static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node)
3407     if (node->p.other == NULL || node->n.other == NULL) {
3408         node->p.pos = node->pos;
3409         node->n.pos = node->pos;
3410         return;
3411     }
3413     Geom::Point leg_prev = to_2geom(node->p.other->pos - node->pos);
3414     Geom::Point leg_next = to_2geom(node->n.other->pos - node->pos);
3416     double norm_leg_prev = Geom::L2(leg_prev);
3417     double norm_leg_next = Geom::L2(leg_next);
3419     Geom::Point delta;
3420     if (norm_leg_next > 0.0) {
3421         delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
3422         delta.normalize();
3423     }
3425     node->p.pos = node->pos - norm_leg_prev / 3 * delta;
3426     node->n.pos = node->pos + norm_leg_next / 3 * delta;
3429 /**
3430  * Node event callback.
3431  */
3432 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3434     gboolean ret = FALSE;
3435     switch (event->type) {
3436         case GDK_ENTER_NOTIFY:
3437             Inkscape::NodePath::Path::active_node = n;
3438             break;
3439         case GDK_LEAVE_NOTIFY:
3440             Inkscape::NodePath::Path::active_node = NULL;
3441             break;
3442         case GDK_SCROLL:
3443             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3444                 switch (event->scroll.direction) {
3445                     case GDK_SCROLL_UP:
3446                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3447                         break;
3448                     case GDK_SCROLL_DOWN:
3449                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3450                         break;
3451                     default:
3452                         break;
3453                 }
3454                 ret = TRUE;
3455             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3456                 switch (event->scroll.direction) {
3457                     case GDK_SCROLL_UP:
3458                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3459                         break;
3460                     case GDK_SCROLL_DOWN:
3461                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3462                         break;
3463                     default:
3464                         break;
3465                 }
3466                 ret = TRUE;
3467             }
3468             break;
3469         case GDK_KEY_PRESS:
3470             switch (get_group0_keyval (&event->key)) {
3471                 case GDK_space:
3472                     if (event->key.state & GDK_BUTTON1_MASK) {
3473                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3474                         stamp_repr(nodepath);
3475                         ret = TRUE;
3476                     }
3477                     break;
3478                 case GDK_Page_Up:
3479                     if (event->key.state & GDK_CONTROL_MASK) {
3480                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3481                     } else {
3482                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3483                     }
3484                     break;
3485                 case GDK_Page_Down:
3486                     if (event->key.state & GDK_CONTROL_MASK) {
3487                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3488                     } else {
3489                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3490                     }
3491                     break;
3492                 default:
3493                     break;
3494             }
3495             break;
3496         default:
3497             break;
3498     }
3500     return ret;
3503 /**
3504  * Handle keypress on node; directly called.
3505  */
3506 gboolean node_key(GdkEvent *event)
3508     Inkscape::NodePath::Path *np;
3510     // there is no way to verify nodes so set active_node to nil when deleting!!
3511     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3513     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3514         gint ret = FALSE;
3515         switch (get_group0_keyval (&event->key)) {
3516             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3517             case GDK_BackSpace:
3518                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3519                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3520                 sp_nodepath_update_repr(np, _("Delete node"));
3521                 Inkscape::NodePath::Path::active_node = NULL;
3522                 ret = TRUE;
3523                 break;
3524             case GDK_c:
3525                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3526                 ret = TRUE;
3527                 break;
3528             case GDK_s:
3529                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3530                 ret = TRUE;
3531                 break;
3532             case GDK_a:
3533                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_AUTO);
3534                 ret = TRUE;
3535                 break;
3536             case GDK_y:
3537                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3538                 ret = TRUE;
3539                 break;
3540             case GDK_b:
3541                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3542                 ret = TRUE;
3543                 break;
3544         }
3545         return ret;
3546     }
3547     return FALSE;
3550 /**
3551  * Mouseclick on node callback.
3552  */
3553 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3555    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3557     if (state & GDK_CONTROL_MASK) {
3558         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3560         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3561             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3562                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3563             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3564                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3565             } else if (n->type == Inkscape::NodePath::NODE_SYMM) {
3566                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_AUTO);
3567             } else {
3568                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3569             }
3570             sp_nodepath_update_repr(nodepath, _("Change node type"));
3571             sp_nodepath_update_statusbar(nodepath);
3573         } else { //ctrl+alt+click: delete node
3574             GList *node_to_delete = NULL;
3575             node_to_delete = g_list_append(node_to_delete, n);
3576             sp_node_delete_preserve(node_to_delete);
3577         }
3579     } else {
3580         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3581     }
3584 /**
3585  * Mouse grabbed node callback.
3586  */
3587 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3589    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3591     if (!n->selected) {
3592         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3593     }
3595     n->is_dragging = true;
3596     //sp_canvas_set_snap_delay_active(n->subpath->nodepath->desktop->canvas, true);
3597     // Reconstruct and store the location of the mouse pointer at the time when we started dragging (needed for snapping)
3598     n->subpath->nodepath->drag_origin_mouse = knot->grabbed_rel_pos + knot->drag_origin;
3600     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3602     sp_nodepath_remember_origins (n->subpath->nodepath);
3605 /**
3606  * Mouse ungrabbed node callback.
3607  */
3608 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3610    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3612    n->dragging_out = NULL;
3613    n->is_dragging = false;
3614    //sp_canvas_set_snap_delay_active(n->subpath->nodepath->desktop->canvas, false);
3615    n->subpath->nodepath->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
3616    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3618    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3621 /**
3622  * The point on a line, given by its angle, closest to the given point.
3623  * \param p  A point.
3624  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3625  * \param closest  Pointer to the point struct where the result is stored.
3626  * \todo FIXME: use dot product perhaps?
3627  */
3628 static void point_line_closest(Geom::Point *p, double a, Geom::Point *closest)
3630     if (a == HUGE_VAL) { // vertical
3631         *closest = Geom::Point(0, (*p)[Geom::Y]);
3632     } else {
3633         (*closest)[Geom::X] = ( a * (*p)[Geom::Y] + (*p)[Geom::X]) / (a*a + 1);
3634         (*closest)[Geom::Y] = a * (*closest)[Geom::X];
3635     }
3638 /**
3639  * Distance from the point to a line given by its angle.
3640  * \param p  A point.
3641  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3642  */
3643 static double point_line_distance(Geom::Point *p, double a)
3645     Geom::Point c;
3646     point_line_closest(p, a, &c);
3647     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]));
3650 /**
3651  * Callback for node "request" signal.
3652  * \todo fixme: This goes to "moved" event? (lauris)
3653  */
3654 static gboolean
3655 node_request(SPKnot */*knot*/, Geom::Point const &p, guint state, gpointer data)
3657     double yn, xn, yp, xp;
3658     double an, ap, na, pa;
3659     double d_an, d_ap, d_na, d_pa;
3660     gboolean collinear = FALSE;
3661     Geom::Point c;
3662     Geom::Point pr;
3664     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3666     n->subpath->nodepath->desktop->snapindicator->remove_snaptarget();
3668     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3669     if ( (!n->subpath->nodepath->straight_path) &&
3670          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3671            || n->dragging_out ) )
3672     {
3673        Geom::Point mouse = p;
3675        if (!n->dragging_out) {
3676            // This is the first drag-out event; find out which handle to drag out
3677            double appr_n = (n->n.other ? Geom::L2(n->n.other->pos - n->pos) - Geom::L2(n->n.other->pos - p) : -HUGE_VAL);
3678            double appr_p = (n->p.other ? Geom::L2(n->p.other->pos - n->pos) - Geom::L2(n->p.other->pos - p) : -HUGE_VAL);
3680            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3681                return FALSE;
3683            Inkscape::NodePath::NodeSide *opposite;
3684            if (appr_p > appr_n) { // closer to p
3685                n->dragging_out = &n->p;
3686                opposite = &n->n;
3687                n->code = NR_CURVETO;
3688            } else if (appr_p < appr_n) { // closer to n
3689                n->dragging_out = &n->n;
3690                opposite = &n->p;
3691                n->n.other->code = NR_CURVETO;
3692            } else { // p and n nodes are the same
3693                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3694                    n->dragging_out = &n->p;
3695                    opposite = &n->n;
3696                    n->code = NR_CURVETO;
3697                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3698                    n->dragging_out = &n->n;
3699                    opposite = &n->p;
3700                    n->n.other->code = NR_CURVETO;
3701                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3702                    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);
3703                    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);
3704                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3705                        n->dragging_out = &n->n;
3706                        opposite = &n->p;
3707                        n->n.other->code = NR_CURVETO;
3708                    } else { // closer to other's n handle
3709                        n->dragging_out = &n->p;
3710                        opposite = &n->n;
3711                        n->code = NR_CURVETO;
3712                    }
3713                }
3714            }
3716            // if there's another handle, make sure the one we drag out starts parallel to it
3717            if (opposite->pos != n->pos) {
3718                mouse = n->pos - Geom::L2(mouse - n->pos) * Geom::unit_vector(opposite->pos - n->pos);
3719            }
3721            // knots might not be created yet!
3722            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3723            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3724        }
3726        // pass this on to the handle-moved callback
3727        node_handle_moved(n->dragging_out->knot, mouse, state, (gpointer) n);
3728        sp_node_update_handles(n);
3729        return TRUE;
3730    }
3732     if (state & GDK_CONTROL_MASK) { // constrained motion
3734         // calculate relative distances of handles
3735         // n handle:
3736         yn = n->n.pos[Geom::Y] - n->pos[Geom::Y];
3737         xn = n->n.pos[Geom::X] - n->pos[Geom::X];
3738         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3739         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3740             if (n->n.other) { // if there is the next point
3741                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3742                     yn = n->n.other->origin[Geom::Y] - n->origin[Geom::Y]; // use origin because otherwise the direction will change as you drag
3743                     xn = n->n.other->origin[Geom::X] - n->origin[Geom::X];
3744             }
3745         }
3746         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3747         if (yn < 0) { xn = -xn; yn = -yn; }
3749         // p handle:
3750         yp = n->p.pos[Geom::Y] - n->pos[Geom::Y];
3751         xp = n->p.pos[Geom::X] - n->pos[Geom::X];
3752         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3753         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3754             if (n->p.other) {
3755                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3756                     yp = n->p.other->origin[Geom::Y] - n->origin[Geom::Y];
3757                     xp = n->p.other->origin[Geom::X] - n->origin[Geom::X];
3758             }
3759         }
3760         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3761         if (yp < 0) { xp = -xp; yp = -yp; }
3763         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3764             // sliding on handles, only if at least one of the handles is non-vertical
3765             // (otherwise it's the same as ctrl+drag anyway)
3767             // calculate angles of the handles
3768             if (xn == 0) {
3769                 if (yn == 0) { // no handle, consider it the continuation of the other one
3770                     an = 0;
3771                     collinear = TRUE;
3772                 }
3773                 else an = 0; // vertical; set the angle to horizontal
3774             } else an = yn/xn;
3776             if (xp == 0) {
3777                 if (yp == 0) { // no handle, consider it the continuation of the other one
3778                     ap = an;
3779                 }
3780                 else ap = 0; // vertical; set the angle to horizontal
3781             } else  ap = yp/xp;
3783             if (collinear) an = ap;
3785             // angles of the perpendiculars; HUGE_VAL means vertical
3786             if (an == 0) na = HUGE_VAL; else na = -1/an;
3787             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3789             // mouse point relative to the node's original pos
3790             pr = p - n->origin;
3792             // distances to the four lines (two handles and two perpendiculars)
3793             d_an = point_line_distance(&pr, an);
3794             d_na = point_line_distance(&pr, na);
3795             d_ap = point_line_distance(&pr, ap);
3796             d_pa = point_line_distance(&pr, pa);
3798             // find out which line is the closest, save its closest point in c
3799             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3800                 point_line_closest(&pr, an, &c);
3801             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3802                 point_line_closest(&pr, ap, &c);
3803             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3804                 point_line_closest(&pr, na, &c);
3805             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3806                 point_line_closest(&pr, pa, &c);
3807             }
3809             // move the node to the closest point
3810             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3811                                             n->origin[Geom::X] + c[Geom::X] - n->pos[Geom::X],
3812                                             n->origin[Geom::Y] + c[Geom::Y] - n->pos[Geom::Y],
3813                                             true);
3815         } else {  // constraining to hor/vert
3817             if (fabs(p[Geom::X] - n->origin[Geom::X]) > fabs(p[Geom::Y] - n->origin[Geom::Y])) { // snap to hor
3818                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3819                                                 p[Geom::X] - n->pos[Geom::X],
3820                                                 n->origin[Geom::Y] - n->pos[Geom::Y],
3821                                                 true,
3822                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::X]));
3823             } else { // snap to vert
3824                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3825                                                 n->origin[Geom::X] - n->pos[Geom::X],
3826                                                 p[Geom::Y] - n->pos[Geom::Y],
3827                                                 true,
3828                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::Y]));
3829             }
3830         }
3831     } else { // move freely
3832         if (n->is_dragging) {
3833             if (state & GDK_MOD1_MASK) { // sculpt
3834                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, p - n->origin);
3835             } else {
3836                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3837                                             p[Geom::X] - n->pos[Geom::X],
3838                                             p[Geom::Y] - n->pos[Geom::Y],
3839                                             (state & GDK_SHIFT_MASK) == 0);
3840             }
3841         }
3842     }
3844     n->subpath->nodepath->desktop->scroll_to_point(p);
3846     return TRUE;
3849 /**
3850  * Node handle clicked callback.
3851  */
3852 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3854    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3856     if (state & GDK_CONTROL_MASK) { // "delete" handle
3857         if (n->p.knot == knot) {
3858             n->p.pos = n->pos;
3859         } else if (n->n.knot == knot) {
3860             n->n.pos = n->pos;
3861         }
3862         sp_node_update_handles(n);
3863         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3864         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3865         sp_nodepath_update_statusbar(nodepath);
3867     } else { // just select or add to selection, depending in Shift
3868         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3869     }
3872 /**
3873  * Node handle grabbed callback.
3874  */
3875 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3877    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3879     // convert auto -> smooth when dragging handle
3880    if (n->type == Inkscape::NodePath::NODE_AUTO) {
3881         n->type = Inkscape::NodePath::NODE_SMOOTH;
3882         sp_nodepath_update_node_knot (n);
3883    }
3885     if (!n->selected) {
3886         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3887     }
3889     // remember the origin point of the handle
3890     if (n->p.knot == knot) {
3891         n->p.origin_radial = n->p.pos - n->pos;
3892     } else if (n->n.knot == knot) {
3893         n->n.origin_radial = n->n.pos - n->pos;
3894     } else {
3895         g_assert_not_reached();
3896     }
3898     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3901 /**
3902  * Node handle ungrabbed callback.
3903  */
3904 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3906    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3908     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3909     if (n->p.knot == knot) {
3910         n->p.origin_radial.a = 0;
3911         sp_knot_set_position(knot, n->p.pos, state);
3912     } else if (n->n.knot == knot) {
3913         n->n.origin_radial.a = 0;
3914         sp_knot_set_position(knot, n->n.pos, state);
3915     } else {
3916         g_assert_not_reached();
3917     }
3919     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3922 /**
3923  * Node handle "request" signal callback.
3924  */
3925 static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, gpointer data)
3927     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3929     Inkscape::NodePath::NodeSide *me, *opposite;
3930     gint which;
3931     if (n->p.knot == knot) {
3932         me = &n->p;
3933         opposite = &n->n;
3934         which = -1;
3935     } else if (n->n.knot == knot) {
3936         me = &n->n;
3937         opposite = &n->p;
3938         which = 1;
3939     } else {
3940         me = opposite = NULL;
3941         which = 0;
3942         g_assert_not_reached();
3943     }
3945     SPDesktop *desktop = n->subpath->nodepath->desktop;
3946     SnapManager &m = desktop->namedview->snap_manager;
3947     m.setup(desktop, true, n->subpath->nodepath->item);
3948     Inkscape::SnappedPoint s;
3950     if ((state & GDK_SHIFT_MASK) != 0) {
3951         // We will not try to snap when the shift-key is pressed
3952         // so remove the old snap indicator and don't wait for it to time-out
3953         desktop->snapindicator->remove_snaptarget();
3954     }
3956     Inkscape::NodePath::Node *othernode = opposite->other;
3957     Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP);
3958     if (othernode) {
3959         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3960             /* We are smooth node adjacent with line */
3961             Geom::Point const delta = p - n->pos;
3962             Geom::Coord const len = Geom::L2(delta);
3963             Inkscape::NodePath::Node *othernode = opposite->other;
3964             Geom::Point const ndelta = n->pos - othernode->pos;
3965             Geom::Coord const linelen = Geom::L2(ndelta);
3966             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3967                 Geom::Coord const scal = dot(delta, ndelta) / linelen;
3968                 p = n->pos + (scal / linelen) * ndelta;
3969             }
3970             if ((state & GDK_SHIFT_MASK) == 0) {
3971                 s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type, Inkscape::Snapper::ConstraintLine(p, ndelta), false);
3972             }
3973         } else {
3974             if ((state & GDK_SHIFT_MASK) == 0) {
3975                 s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type);
3976             }
3977         }
3978     } else {
3979         if ((state & GDK_SHIFT_MASK) == 0) {
3980             s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type);
3981         }
3982     }
3984     s.getPoint(p);
3986     sp_node_adjust_handle(n, -which);
3988     return FALSE;
3991 /**
3992  * Node handle moved callback.
3993  */
3994 static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data)
3996    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3997    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3999    Inkscape::NodePath::NodeSide *me;
4000    Inkscape::NodePath::NodeSide *other;
4001     if (n->p.knot == knot) {
4002         me = &n->p;
4003         other = &n->n;
4004     } else if (n->n.knot == knot) {
4005         me = &n->n;
4006         other = &n->p;
4007     } else {
4008         me = NULL;
4009         other = NULL;
4010         g_assert_not_reached();
4011     }
4013     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
4014     Radial rme(me->pos - n->pos);
4015     Radial rother(other->pos - n->pos);
4016     Radial rnew(p - n->pos);
4018     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
4019         int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
4020         /* 0 interpreted as "no snapping". */
4022         // 1. Snap to the closest PI/snaps angle, starting from zero.
4023         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
4025         // 2. Snap to the original angle, its opposite and perpendiculars
4026         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
4027             /* The closest PI/2 angle, starting from original angle */
4028             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
4030             // Snap to the closest.
4031             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
4032                        ? a_snapped
4033                        : a_ortho );
4034         }
4036         // 3. Snap to the angle of the opposite line, if any
4037         Inkscape::NodePath::Node *othernode = other->other;
4038         if (othernode) {
4039             Geom::Point other_to_snap(0,0);
4040             if (sp_node_side_is_line(n, other)) {
4041                 other_to_snap = othernode->pos - n->pos;
4042             } else {
4043                 other_to_snap = other->pos - n->pos;
4044             }
4045             if (Geom::L2(other_to_snap) > 1e-3) {
4046                 Radial rother_to_snap(other_to_snap);
4047                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
4048                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
4050                 // Snap to the closest.
4051                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
4052                        ? a_snapped
4053                        : a_oppo );
4054             }
4055         }
4057         rnew.a = a_snapped;
4058     }
4060     if (state & GDK_MOD1_MASK) {
4061         // lock handle length
4062         rnew.r = me->origin_radial.r;
4063     }
4065     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (!n->dragging_out && (state & GDK_SHIFT_MASK)))
4066         && (rme.a != HUGE_VAL) && (rnew.a != HUGE_VAL) && ((fabs(rme.a - rnew.a) > 0.001) || (n->type ==Inkscape::NodePath::NODE_SYMM))) {
4067         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
4068         rother.a += rnew.a - rme.a;
4069         other->pos = Geom::Point(rother) + n->pos;
4070         if (other->knot) {
4071             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
4072             sp_knot_moveto(other->knot, other->pos);
4073         }
4074     }
4076     me->pos = Geom::Point(rnew) + n->pos;
4077     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
4079     // move knot, but without emitting the signal:
4080     // we cannot emit a "moved" signal because we're now processing it
4081     sp_knot_moveto(me->knot, me->pos);
4083     update_object(n->subpath->nodepath);
4085     /* status text */
4086     SPDesktop *desktop = n->subpath->nodepath->desktop;
4087     if (!desktop) return;
4088     SPEventContext *ec = desktop->event_context;
4089     if (!ec) return;
4091     Inkscape::MessageContext *mc = get_message_context(ec);
4093     if (!mc) return;
4095     double degrees = 180 / M_PI * rnew.a;
4096     if (degrees > 180) degrees -= 360;
4097     if (degrees < -180) degrees += 360;
4098     if (prefs->getBool("/options/compassangledisplay/value"))
4099         degrees = angle_to_compass (degrees);
4101     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
4103     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
4104          _("<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);
4106     g_string_free(length, TRUE);
4109 /**
4110  * Node handle event callback.
4111  */
4112 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
4114     gboolean ret = FALSE;
4115     switch (event->type) {
4116         case GDK_KEY_PRESS:
4117             switch (get_group0_keyval (&event->key)) {
4118                 case GDK_space:
4119                     if (event->key.state & GDK_BUTTON1_MASK) {
4120                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
4121                         stamp_repr(nodepath);
4122                         ret = TRUE;
4123                     }
4124                     break;
4125                 default:
4126                     break;
4127             }
4128             break;
4129         case GDK_ENTER_NOTIFY:
4130             // we use an experimentally determined threshold that seems to work fine
4131             if (Geom::L2(n->pos - knot->pos) < 0.75)
4132                 Inkscape::NodePath::Path::active_node = n;
4133             break;
4134         case GDK_LEAVE_NOTIFY:
4135             // we use an experimentally determined threshold that seems to work fine
4136             if (Geom::L2(n->pos - knot->pos) < 0.75)
4137                 Inkscape::NodePath::Path::active_node = NULL;
4138             break;
4139         default:
4140             break;
4141     }
4143     return ret;
4146 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
4147                                  Radial &rme, Radial &rother, gboolean const both)
4149     rme.a += angle;
4150     if ( both
4151          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4152          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4153     {
4154         rother.a += angle;
4155     }
4158 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
4159                                         Radial &rme, Radial &rother, gboolean const both)
4161     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
4163     gdouble r;
4164     if ( both
4165          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4166          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4167     {
4168         r = MAX(rme.r, rother.r);
4169     } else {
4170         r = rme.r;
4171     }
4173     gdouble const weird_angle = atan2(norm_angle, r);
4174 /* Bulia says norm_angle is just the visible distance that the
4175  * object's end must travel on the screen.  Left as 'angle' for want of
4176  * a better name.*/
4178     rme.a += weird_angle;
4179     if ( both
4180          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4181          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4182     {
4183         rother.a += weird_angle;
4184     }
4187 /**
4188  * Rotate one node.
4189  */
4190 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4192     Inkscape::NodePath::NodeSide *me, *other;
4193     bool both = false;
4195     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4196     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4198     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4199         me = &(n->p);
4200         other = &(n->n);
4201     } else if (!n->p.other) {
4202         me = &(n->n);
4203         other = &(n->p);
4204     } else {
4205         if (which > 0) { // right handle
4206             if (xn > xp) {
4207                 me = &(n->n);
4208                 other = &(n->p);
4209             } else {
4210                 me = &(n->p);
4211                 other = &(n->n);
4212             }
4213         } else if (which < 0){ // left handle
4214             if (xn <= xp) {
4215                 me = &(n->n);
4216                 other = &(n->p);
4217             } else {
4218                 me = &(n->p);
4219                 other = &(n->n);
4220             }
4221         } else { // both handles
4222             me = &(n->n);
4223             other = &(n->p);
4224             both = true;
4225         }
4226     }
4228     Radial rme(me->pos - n->pos);
4229     Radial rother(other->pos - n->pos);
4231     if (screen) {
4232         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4233     } else {
4234         node_rotate_one_internal (*n, angle, rme, rother, both);
4235     }
4237     me->pos = n->pos + Geom::Point(rme);
4239     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4240         other->pos =  n->pos + Geom::Point(rother);
4241     }
4243     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4244     // so here we just move all the knots without emitting move signals, for speed
4245     sp_node_update_handles(n, false);
4248 /**
4249  * Rotate selected nodes.
4250  */
4251 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4253     if (!nodepath || !nodepath->selected) return;
4255     if (g_list_length(nodepath->selected) == 1) {
4256        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4257         node_rotate_one (n, angle, which, screen);
4258     } else {
4259        // rotate as an object:
4261         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4262         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4263         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4264             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4265             box.expandTo (n->pos); // contain all selected nodes
4266         }
4268         gdouble rot;
4269         if (screen) {
4270             gdouble const zoom = nodepath->desktop->current_zoom();
4271             gdouble const zmove = angle / zoom;
4272             gdouble const r = Geom::L2(box.max() - box.midpoint());
4273             rot = atan2(zmove, r);
4274         } else {
4275             rot = angle;
4276         }
4278         Geom::Point rot_center;
4279         if (Inkscape::NodePath::Path::active_node == NULL)
4280             rot_center = box.midpoint();
4281         else
4282             rot_center = Inkscape::NodePath::Path::active_node->pos;
4284         Geom::Matrix t =
4285             Geom::Matrix (Geom::Translate(-rot_center)) *
4286             Geom::Matrix (Geom::Rotate(rot)) *
4287             Geom::Matrix (Geom::Translate(rot_center));
4289         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4290             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4291             n->pos *= t;
4292             n->n.pos *= t;
4293             n->p.pos *= t;
4294             sp_node_update_handles(n, false);
4295         }
4296     }
4298     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4301 /**
4302  * Scale one node.
4303  */
4304 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4306     bool both = false;
4307     Inkscape::NodePath::NodeSide *me, *other;
4309     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4310     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4312     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4313         me = &(n->p);
4314         other = &(n->n);
4315         n->code = NR_CURVETO;
4316     } else if (!n->p.other) {
4317         me = &(n->n);
4318         other = &(n->p);
4319         if (n->n.other)
4320             n->n.other->code = NR_CURVETO;
4321     } else {
4322         if (which > 0) { // right handle
4323             if (xn > xp) {
4324                 me = &(n->n);
4325                 other = &(n->p);
4326                 if (n->n.other)
4327                     n->n.other->code = NR_CURVETO;
4328             } else {
4329                 me = &(n->p);
4330                 other = &(n->n);
4331                 n->code = NR_CURVETO;
4332             }
4333         } else if (which < 0){ // left handle
4334             if (xn <= xp) {
4335                 me = &(n->n);
4336                 other = &(n->p);
4337                 if (n->n.other)
4338                     n->n.other->code = NR_CURVETO;
4339             } else {
4340                 me = &(n->p);
4341                 other = &(n->n);
4342                 n->code = NR_CURVETO;
4343             }
4344         } else { // both handles
4345             me = &(n->n);
4346             other = &(n->p);
4347             both = true;
4348             n->code = NR_CURVETO;
4349             if (n->n.other)
4350                 n->n.other->code = NR_CURVETO;
4351         }
4352     }
4354     Radial rme(me->pos - n->pos);
4355     Radial rother(other->pos - n->pos);
4357     rme.r += grow;
4358     if (rme.r < 0) rme.r = 0;
4359     if (rme.a == HUGE_VAL) {
4360         if (me->other) { // if direction is unknown, initialize it towards the next node
4361             Radial rme_next(me->other->pos - n->pos);
4362             rme.a = rme_next.a;
4363         } else { // if there's no next, initialize to 0
4364             rme.a = 0;
4365         }
4366     }
4367     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4368         rother.r += grow;
4369         if (rother.r < 0) rother.r = 0;
4370         if (rother.a == HUGE_VAL) {
4371             rother.a = rme.a + M_PI;
4372         }
4373     }
4375     me->pos = n->pos + Geom::Point(rme);
4377     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4378         other->pos = n->pos + Geom::Point(rother);
4379     }
4381     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4382     // so here we just move all the knots without emitting move signals, for speed
4383     sp_node_update_handles(n, false);
4386 /**
4387  * Scale selected nodes.
4388  */
4389 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4391     if (!nodepath || !nodepath->selected) return;
4393     if (g_list_length(nodepath->selected) == 1) {
4394         // scale handles of the single selected node
4395         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4396         node_scale_one (n, grow, which);
4397     } else {
4398         // scale nodes as an "object":
4400         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4401         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4402         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4403             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4404             box.expandTo (n->pos); // contain all selected nodes
4405         }
4407         if ( Geom::are_near(box.maxExtent(), 0) ) {
4408             SPEventContext *ec = nodepath->desktop->event_context;
4409             if (!ec) return;
4410             Inkscape::MessageContext *mc = get_message_context(ec);
4411             if (!mc) return;
4412             mc->setF(Inkscape::WARNING_MESSAGE,
4413                              _("Cannot scale nodes when all are at the same location."));
4414             return;
4415         }
4416         double scale = (box.maxExtent() + grow)/box.maxExtent();
4419         Geom::Point scale_center;
4420         if (Inkscape::NodePath::Path::active_node == NULL)
4421             scale_center = box.midpoint();
4422         else
4423             scale_center = Inkscape::NodePath::Path::active_node->pos;
4425         Geom::Matrix t =
4426             Geom::Translate(-scale_center) *
4427             Geom::Scale(scale, scale) *
4428             Geom::Translate(scale_center);
4430         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4431             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4432             n->pos *= t;
4433             n->n.pos *= t;
4434             n->p.pos *= t;
4435             sp_node_update_handles(n, false);
4436         }
4437     }
4439     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4442 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4444     if (!nodepath) return;
4445     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4448 /**
4449  * Flip selected nodes horizontally/vertically.
4450  */
4451 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis, boost::optional<Geom::Point> center)
4453     if (!nodepath || !nodepath->selected) return;
4455     if (g_list_length(nodepath->selected) == 1 && !center) {
4456         // flip handles of the single selected node
4457         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4458         double temp = n->p.pos[axis];
4459         n->p.pos[axis] = n->n.pos[axis];
4460         n->n.pos[axis] = temp;
4461         sp_node_update_handles(n, false);
4462     } else {
4463         // scale nodes as an "object":
4465         Geom::Rect box = sp_node_selected_bbox (nodepath);
4466         if (!center) {
4467             center = box.midpoint();
4468         }
4469         Geom::Matrix t =
4470             Geom::Matrix (Geom::Translate(- *center)) *
4471             Geom::Matrix ((axis == Geom::X)? Geom::Scale(-1, 1) : Geom::Scale(1, -1)) *
4472             Geom::Matrix (Geom::Translate(*center));
4474         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4475             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4476             n->pos *= t;
4477             n->n.pos *= t;
4478             n->p.pos *= t;
4479             sp_node_update_handles(n, false);
4480         }
4481     }
4483     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4486 Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4488     g_assert (nodepath->selected);
4490     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4491     Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4492     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4493         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4494         box.expandTo (n->pos); // contain all selected nodes
4495     }
4496     return box;
4499 //-----------------------------------------------
4500 /**
4501  * Return new subpath under given nodepath.
4502  */
4503 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4505     g_assert(nodepath);
4506     g_assert(nodepath->desktop);
4508    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4510     s->nodepath = nodepath;
4511     s->closed = FALSE;
4512     s->nodes = NULL;
4513     s->first = NULL;
4514     s->last = NULL;
4516     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4517     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4518     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4520     return s;
4523 /**
4524  * Destroy nodes in subpath, then subpath itself.
4525  */
4526 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4528     g_assert(subpath);
4529     g_assert(subpath->nodepath);
4530     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4532     while (subpath->nodes) {
4533         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4534     }
4536     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4538     g_free(subpath);
4541 /**
4542  * Link head to tail in subpath.
4543  */
4544 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4546     g_assert(!sp->closed);
4547     g_assert(sp->last != sp->first);
4548     g_assert(sp->first->code == NR_MOVETO);
4550     sp->closed = TRUE;
4552     //Link the head to the tail
4553     sp->first->p.other = sp->last;
4554     sp->last->n.other  = sp->first;
4555     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4556     sp->first          = sp->last;
4558     //Remove the extra end node
4559     sp_nodepath_node_destroy(sp->last->n.other);
4562 /**
4563  * Open closed (loopy) subpath at node.
4564  */
4565 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4567     g_assert(sp->closed);
4568     g_assert(n->subpath == sp);
4569     g_assert(sp->first == sp->last);
4571     /* We create new startpoint, current node will become last one */
4573    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4574                                                 &n->pos, &n->pos, &n->n.pos);
4577     sp->closed        = FALSE;
4579     //Unlink to make a head and tail
4580     sp->first         = new_path;
4581     sp->last          = n;
4582     n->n.other        = NULL;
4583     new_path->p.other = NULL;
4586 /**
4587  * Return new node in subpath with given properties.
4588  * \param pos Position of node.
4589  * \param ppos Handle position in previous direction
4590  * \param npos Handle position in previous direction
4591  */
4592 Inkscape::NodePath::Node *
4593 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)
4595     g_assert(sp);
4596     g_assert(sp->nodepath);
4597     g_assert(sp->nodepath->desktop);
4599     if (nodechunk == NULL)
4600         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4602     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4604     n->subpath  = sp;
4606     if (type != Inkscape::NodePath::NODE_NONE) {
4607         // use the type from sodipodi:nodetypes
4608         n->type = type;
4609     } else {
4610         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4611             // points are (almost) collinear
4612             if (Geom::L2(*pos - *ppos) < 1e-6 || Geom::L2(*pos - *npos) < 1e-6) {
4613                 // endnode, or a node with a retracted handle
4614                 n->type = Inkscape::NodePath::NODE_CUSP;
4615             } else {
4616                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4617             }
4618         } else {
4619             n->type = Inkscape::NodePath::NODE_CUSP;
4620         }
4621     }
4623     n->code     = code;
4624     n->selected = FALSE;
4625     n->pos      = *pos;
4626     n->p.pos    = *ppos;
4627     n->n.pos    = *npos;
4629     n->dragging_out = NULL;
4631     Inkscape::NodePath::Node *prev;
4632     if (next) {
4633         //g_assert(g_list_find(sp->nodes, next));
4634         prev = next->p.other;
4635     } else {
4636         prev = sp->last;
4637     }
4639     if (prev)
4640         prev->n.other = n;
4641     else
4642         sp->first = n;
4644     if (next)
4645         next->p.other = n;
4646     else
4647         sp->last = n;
4649     n->p.other = prev;
4650     n->n.other = next;
4652     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"));
4653     sp_knot_set_position(n->knot, *pos, 0);
4655     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4656     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4657     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4659     sp_nodepath_update_node_knot(n);
4661     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4662     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4663     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4664     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4665     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4666     sp_knot_show(n->knot);
4668     // We only create handle knots and lines on demand
4669     n->p.knot = NULL;
4670     n->p.line = NULL;
4671     n->n.knot = NULL;
4672     n->n.line = NULL;
4674     sp->nodes = g_list_prepend(sp->nodes, n);
4676     return n;
4679 /**
4680  * Destroy node and its knots, link neighbors in subpath.
4681  */
4682 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4684     g_assert(node);
4685     g_assert(node->subpath);
4686     g_assert(SP_IS_KNOT(node->knot));
4688    Inkscape::NodePath::SubPath *sp = node->subpath;
4690     if (node->selected) { // first, deselect
4691         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4692         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4693     }
4695     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4697     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4698     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4699     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4700     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4701     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4702     g_object_unref(G_OBJECT(node->knot));
4704     if (node->p.knot) {
4705         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4706         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4707         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4708         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4709         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4710         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4711         g_object_unref(G_OBJECT(node->p.knot));
4712         node->p.knot = NULL;
4713     }
4715     if (node->n.knot) {
4716         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4717         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4718         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4719         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4720         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4721         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4722         g_object_unref(G_OBJECT(node->n.knot));
4723         node->n.knot = NULL;
4724     }
4726     if (node->p.line)
4727         gtk_object_destroy(GTK_OBJECT(node->p.line));
4728     if (node->n.line)
4729         gtk_object_destroy(GTK_OBJECT(node->n.line));
4731     if (sp->nodes) { // there are others nodes on the subpath
4732         if (sp->closed) {
4733             if (sp->first == node) {
4734                 g_assert(sp->last == node);
4735                 sp->first = node->n.other;
4736                 sp->last = sp->first;
4737             }
4738             node->p.other->n.other = node->n.other;
4739             node->n.other->p.other = node->p.other;
4740         } else {
4741             if (sp->first == node) {
4742                 sp->first = node->n.other;
4743                 sp->first->code = NR_MOVETO;
4744             }
4745             if (sp->last == node) sp->last = node->p.other;
4746             if (node->p.other) node->p.other->n.other = node->n.other;
4747             if (node->n.other) node->n.other->p.other = node->p.other;
4748         }
4749     } else { // this was the last node on subpath
4750         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4751     }
4753     g_mem_chunk_free(nodechunk, node);
4756 /**
4757  * Returns one of the node's two sides.
4758  * \param which Indicates which side.
4759  * \return Pointer to previous node side if which==-1, next if which==1.
4760  */
4761 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4763     g_assert(node);
4764     Inkscape::NodePath::NodeSide * result = 0;
4765     switch (which) {
4766         case -1:
4767             result = &node->p;
4768             break;
4769         case 1:
4770             result = &node->n;
4771             break;
4772         default:
4773             g_assert_not_reached();
4774     }
4776     return result;
4779 /**
4780  * Return the other side of the node, given one of its sides.
4781  */
4782 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4784     g_assert(node);
4785     Inkscape::NodePath::NodeSide *result = 0;
4787     if (me == &node->p) {
4788         result = &node->n;
4789     } else if (me == &node->n) {
4790         result = &node->p;
4791     } else {
4792         g_assert_not_reached();
4793     }
4795     return result;
4798 /**
4799  * Return NRPathcode on the given side of the node.
4800  */
4801 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4803     g_assert(node);
4805     NRPathcode result = NR_END;
4806     if (me == &node->p) {
4807         if (node->p.other) {
4808             result = (NRPathcode)node->code;
4809         } else {
4810             result = NR_MOVETO;
4811         }
4812     } else if (me == &node->n) {
4813         if (node->n.other) {
4814             result = (NRPathcode)node->n.other->code;
4815         } else {
4816             result = NR_MOVETO;
4817         }
4818     } else {
4819         g_assert_not_reached();
4820     }
4822     return result;
4825 /**
4826  * Return node with the given index
4827  */
4828 Inkscape::NodePath::Node *
4829 sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index)
4831     Inkscape::NodePath::Node *e = NULL;
4833     if (!nodepath) {
4834         return e;
4835     }
4837     //find segment
4838     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4840         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4841         int n = g_list_length(sp->nodes);
4842         if (sp->closed) {
4843             n++;
4844         }
4846         //if the piece belongs to this subpath grab it
4847         //otherwise move onto the next subpath
4848         if (index < n) {
4849             e = sp->first;
4850             for (int i = 0; i < index; ++i) {
4851                 e = e->n.other;
4852             }
4853             break;
4854         } else {
4855             if (sp->closed) {
4856                 index -= (n+1);
4857             } else {
4858                 index -= n;
4859             }
4860         }
4861     }
4863     return e;
4866 /**
4867  * Returns plain text meaning of node type.
4868  */
4869 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4871     unsigned retracted = 0;
4872     bool endnode = false;
4874     for (int which = -1; which <= 1; which += 2) {
4875         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4876         if (side->other && Geom::L2(side->pos - node->pos) < 1e-6)
4877             retracted ++;
4878         if (!side->other)
4879             endnode = true;
4880     }
4882     if (retracted == 0) {
4883         if (endnode) {
4884                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4885                 return _("end node");
4886         } else {
4887             switch (node->type) {
4888                 case Inkscape::NodePath::NODE_CUSP:
4889                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4890                     return _("cusp");
4891                 case Inkscape::NodePath::NODE_SMOOTH:
4892                     // TRANSLATORS: "smooth" is an adjective here
4893                     return _("smooth");
4894                 case Inkscape::NodePath::NODE_AUTO:
4895                     return _("auto");
4896                 case Inkscape::NodePath::NODE_SYMM:
4897                     return _("symmetric");
4898             }
4899         }
4900     } else if (retracted == 1) {
4901         if (endnode) {
4902             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4903             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4904         } else {
4905             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4906         }
4907     } else {
4908         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4909     }
4911     return NULL;
4914 /**
4915  * Handles content of statusbar as long as node tool is active.
4916  */
4917 void
4918 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4920     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");
4921     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4923     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4924     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4925     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4926     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4928     SPDesktop *desktop = NULL;
4929     if (nodepath) {
4930         desktop = nodepath->desktop;
4931     } else {
4932         desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above
4933     }
4935     SPEventContext *ec = desktop->event_context;
4936     if (!ec) return;
4938     Inkscape::MessageContext *mc = get_message_context(ec);
4939     if (!mc) return;
4941     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4943     if (selected_nodes == 0) {
4944         Inkscape::Selection *sel = desktop->selection;
4945         if (!sel || sel->isEmpty()) {
4946             mc->setF(Inkscape::NORMAL_MESSAGE,
4947                      _("Select a single object to edit its nodes or handles."));
4948         } else {
4949             if (nodepath) {
4950             mc->setF(Inkscape::NORMAL_MESSAGE,
4951                      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.",
4952                               "<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.",
4953                               total_nodes),
4954                      total_nodes);
4955             } else {
4956                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4957                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4958                 } else {
4959                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4960                 }
4961             }
4962         }
4963     } else if (nodepath && selected_nodes == 1) {
4964         mc->setF(Inkscape::NORMAL_MESSAGE,
4965                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4966                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4967                           total_nodes),
4968                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4969     } else {
4970         if (selected_subpaths > 1) {
4971             mc->setF(Inkscape::NORMAL_MESSAGE,
4972                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4973                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4974                               total_nodes),
4975                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4976         } else {
4977             mc->setF(Inkscape::NORMAL_MESSAGE,
4978                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4979                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4980                               total_nodes),
4981                      selected_nodes, total_nodes, when_selected);
4982         }
4983     }
4986 /*
4987  * returns a *copy* of the curve of that object.
4988  */
4989 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4990     if (!object)
4991         return NULL;
4993     SPCurve *curve = NULL;
4994     if (SP_IS_PATH(object)) {
4995         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4996         curve = curve_new->copy();
4997     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4998         const gchar *svgd = object->repr->attribute(key);
4999         if (svgd) {
5000             Geom::PathVector pv = sp_svg_read_pathv(svgd);
5001             SPCurve *curve_new = new SPCurve(pv);
5002             if (curve_new) {
5003                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
5004             }
5005         }
5006     }
5008     return curve;
5011 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
5012     if (!np || !np->object || !curve)
5013         return;
5015     if (SP_IS_PATH(np->object)) {
5016         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
5017             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
5018         } else {
5019             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
5020         }
5021     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
5022         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(np->object)->get_lpe();
5023         if (lpe) {
5024             Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( lpe->getParameter(np->repr_key) );
5025             if (pathparam) {
5026                 pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
5027                 np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
5028             }
5029         }
5030     }
5033 /*
5034 SPCanvasItem *
5035 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
5036     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
5038 */
5041 /// \todo this code to generate a helper canvasitem from an spcurve should be moved to different file
5042 SPCanvasItem *
5043 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const Geom::Matrix & i2d, guint32 color = 0xff0000ff) {
5044     SPCurve *flash_curve = curve->copy();
5045     flash_curve->transform(i2d);
5046     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
5047     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
5048     // unless we also flash the nodes...
5049     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
5050     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
5051     sp_canvas_item_show(canvasitem);
5052     flash_curve->unref();
5053     return canvasitem;
5056 SPCanvasItem *
5057 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item) {
5058     if (!item || !desktop) {
5059         return NULL;
5060     }
5062     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5063     guint32 color = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
5065     Geom::Matrix i2d = sp_item_i2d_affine(item);
5067     SPCurve *curve = NULL;
5068     if (SP_IS_PATH(item)) {
5069         curve = sp_path_get_curve_for_edit(SP_PATH(item));
5070     } else if ( SP_IS_SHAPE(item) && SP_SHAPE(item)->curve ) {
5071         curve = sp_shape_get_curve (SP_SHAPE(item));
5072     } else if ( SP_IS_TEXT(item) ) {
5073         curve = SP_TEXT(item)->getNormalizedBpath();
5074     } else {
5075         g_warning ("-----> sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item): TODO: generate the helper path for this item type!\n");
5076         return NULL;
5077     }
5079     SPCanvasItem * helperpath = sp_nodepath_generate_helperpath(desktop, curve, i2d, color);
5081     curve->unref();
5083     return helperpath;
5087 // TODO: Merge this with sp_nodepath_make_helper_item()!
5088 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
5089     np->show_helperpath = show;
5091     if (show) {
5092         SPCurve *helper_curve = np->curve->copy();
5093         helper_curve->transform(np->i2d);
5094         if (!np->helper_path) {
5095             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
5097             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
5098             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);
5099             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
5100             sp_canvas_item_move_to_z(np->helper_path, 0);
5101             sp_canvas_item_show(np->helper_path);
5102         } else {
5103             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
5104         }
5105         helper_curve->unref();
5106     } else {
5107         if (np->helper_path) {
5108             GtkObject *temp = np->helper_path;
5109             np->helper_path = NULL;
5110             gtk_object_destroy(temp);
5111         }
5112     }
5115 /* sp_nodepath_make_straight_path:
5116  *   Prevents user from curving the path by dragging a segment or activating handles etc.
5117  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
5118  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
5119  */
5120 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
5121     np->straight_path = true;
5122     np->show_handles = false;
5123     g_message("add code to make the path straight.");
5124     // do sp_nodepath_convert_node_type on all nodes?
5125     // coding tip: search for this text : "Make selected segments lines"
5128 /*
5129   Local Variables:
5130   mode:c++
5131   c-file-style:"stroustrup"
5132   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5133   indent-tabs-mode:nil
5134   fill-column:99
5135   End:
5136 */
5137 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :