Code

5b2a738d3dc2c96e4d007a0bb0064b01a5149c8a
[inkscape.git] / src / nodepath.cpp
1 #define __SP_NODEPATH_C__
3 /** \file
4  * Path handler in node edit mode
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *
10  * Portions of this code are in public domain; node sculpting functions written by bulia byak are under GNU GPL
11  */
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
17 #include <gdk/gdkkeysyms.h>
18 #include "display/canvas-bpath.h"
19 #include "display/curve.h"
20 #include "display/sp-ctrlline.h"
21 #include "display/sodipodi-ctrl.h"
22 #include "display/sp-canvas-util.h"
23 #include <glibmm/i18n.h>
24 #include "2geom/pathvector.h"
25 #include "2geom/sbasis-to-bezier.h"
26 #include "2geom/bezier-curve.h"
27 #include "2geom/hvlinesegment.h"
28 #include "helper/units.h"
29 #include "helper/geom.h"
30 #include "knot.h"
31 #include "inkscape.h"
32 #include "document.h"
33 #include "sp-namedview.h"
34 #include "desktop.h"
35 #include "desktop-handles.h"
36 #include "snap.h"
37 #include "message-stack.h"
38 #include "message-context.h"
39 #include "node-context.h"
40 #include "lpe-tool-context.h"
41 #include "shape-editor.h"
42 #include "selection-chemistry.h"
43 #include "selection.h"
44 #include "xml/repr.h"
45 #include "preferences.h"
46 #include "sp-metrics.h"
47 #include "sp-path.h"
48 #include "sp-text.h"
49 #include "sp-shape.h"
50 #include "libnr/nr-matrix-ops.h"
51 #include "svg/svg.h"
52 #include "verbs.h"
53 #include <2geom/bezier-utils.h>
54 #include <vector>
55 #include <algorithm>
56 #include <cstring>
57 #include <cmath>
58 #include "live_effects/lpeobject.h"
59 #include "live_effects/lpeobject-reference.h"
60 #include "live_effects/effect.h"
61 #include "live_effects/parameter/parameter.h"
62 #include "live_effects/parameter/path.h"
63 #include "util/mathfns.h"
64 #include "display/snap-indicator.h"
65 #include "snapped-point.h"
67 namespace Geom { class Matrix; }
69 /// \todo
70 /// evil evil evil. FIXME: conflict of two different Path classes!
71 /// There is a conflict in the namespace between two classes named Path.
72 /// #include "sp-flowtext.h"
73 /// #include "sp-flowregion.h"
75 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
76 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
77 GType sp_flowregion_get_type (void);
78 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
79 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
80 GType sp_flowtext_get_type (void);
81 // end evil workaround
83 #include "helper/stlport.h"
86 /// \todo fixme: Implement these via preferences */
88 #define NODE_FILL          0xbfbfbf00
89 #define NODE_STROKE        0x000000ff
90 #define NODE_FILL_HI       0xff000000
91 #define NODE_STROKE_HI     0x000000ff
92 #define NODE_FILL_SEL      0x0000ffff
93 #define NODE_STROKE_SEL    0x000000ff
94 #define NODE_FILL_SEL_HI   0xff000000
95 #define NODE_STROKE_SEL_HI 0x000000ff
96 #define KNOT_FILL          0xffffffff
97 #define KNOT_STROKE        0x000000ff
98 #define KNOT_FILL_HI       0xff000000
99 #define KNOT_STROKE_HI     0x000000ff
101 static GMemChunk *nodechunk = NULL;
103 /* Creation from object */
105 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t);
106 static Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length);
108 /* Object updating */
110 static void stamp_repr(Inkscape::NodePath::Path *np);
111 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
112 static gchar *create_typestr(Inkscape::NodePath::Path *np);
114 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
116 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
118 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
120 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
122 /* Adjust handle placement, if the node or the other handle is moved */
123 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
124 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
125 static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node);
127 /* Node event callbacks */
128 static void node_clicked(SPKnot *knot, guint state, gpointer data);
129 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
130 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
131 static gboolean node_request(SPKnot *knot, Geom::Point const &p, guint state, gpointer data);
133 /* Handle event callbacks */
134 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
135 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
136 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
137 static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, gpointer data);
138 static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data);
139 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
141 /* Constructors and destructors */
143 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
144 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
145 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
146 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
147 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
148                                          Geom::Point *ppos, Geom::Point *pos, Geom::Point *npos);
149 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
151 /* Helpers */
153 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
154 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
155 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
157 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
158 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
160 // active_node indicates mouseover node
161 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
163 static SPCanvasItem *
164 sp_nodepath_make_helper_item(Inkscape::NodePath::Path *np, /*SPDesktop *desktop, */const SPCurve *curve, bool show = false, guint32 color = 0xff0000ff) {
165     SPCurve *helper_curve = curve->copy();
166     helper_curve->transform(np->i2d);
167     SPCanvasItem *helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
168     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helper_path), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
169     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helper_path), 0, SP_WIND_RULE_NONZERO);
170     sp_canvas_item_move_to_z(helper_path, 0);
171     if (show) {
172         sp_canvas_item_show(helper_path);
173     }
174     helper_curve->unref();
175     return helper_path;
178 static void
179 sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) {
180     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> > helper_path_vec;
181     if (!SP_IS_LPE_ITEM(np->item)) {
182         g_print ("Only LPEItems can have helperpaths!\n");
183         return;
184     }
186     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
187     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
188     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
189         Inkscape::LivePathEffect::LPEObjectReference *lperef = (*i);
190         Inkscape::LivePathEffect::Effect *lpe = lperef->lpeobject->get_lpe();
191         if (lpe) {
192             // create new canvas items from the effect's helper paths
193             std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
194             for (std::vector<Geom::PathVector>::iterator j = hpaths.begin(); j != hpaths.end(); ++j) {
195                 SPCurve *helper_curve = new SPCurve(*j);
196                 SPCanvasItem * canvasitem = sp_nodepath_make_helper_item(np, helper_curve, true, 0x509050dd);
197                 np->helper_path_vec[lpe].push_back(canvasitem);
198                 helper_curve->unref();
199             }
200         }
201     }
204 static void
205 sp_nodepath_destroy_helperpaths(Inkscape::NodePath::Path *np) {
206     for (HelperPathList::iterator i = np->helper_path_vec.begin(); i != np->helper_path_vec.end(); ++i) {
207         for (std::vector<SPCanvasItem *>::iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) {
208             GtkObject *temp = *j;
209             *j = NULL;
210             gtk_object_destroy(temp);
211         }
212     }
213     np->helper_path_vec.clear();
216 /** updates canvas items from the effect's helper paths */
217 void
218 sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np) {
219     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> > helper_path_vec;
220     if (!SP_IS_LPE_ITEM(np->item)) {
221         g_print ("Only LPEItems can have helperpaths!\n");
222         return;
223     }
225     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
226     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
228     /* The number or type or LPEs may have changed, so we need to clear and recreate our
229      * helper_path_vec to make sure it is in sync */
230     sp_nodepath_destroy_helperpaths(np);
231     sp_nodepath_create_helperpaths(np);
233     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
234         Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->get_lpe();
235         if (lpe) {
236             std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
237             for (unsigned int j = 0; j < hpaths.size(); ++j) {
238                 SPCurve *curve = new SPCurve(hpaths[j]);
239                 curve->transform(np->i2d);
240                 sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH((np->helper_path_vec[lpe])[j]), curve);
241                 curve = curve->unref();
242             }
243         }
244     }
247 /**
248  * \brief Creates new nodepath from item
249  *
250  * If repr_key_in is not NULL, object *has* to be a LivePathEffectObject !
251  *
252  * \todo create proper constructor for nodepath::path, this method returns null a constructor cannot so this cannot be simply converted to constructor.
253  */
254 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
256     if (repr_key_in) {
257         g_assert(IS_LIVEPATHEFFECT(object));
258     }
260     Inkscape::XML::Node *repr = object->repr;
262     /** \todo
263      * FIXME: remove this. We don't want to edit paths inside flowtext.
264      * Instead we will build our flowtext with cloned paths, so that the
265      * real paths are outside the flowtext and thus editable as usual.
266      */
267     if (SP_IS_FLOWTEXT(object)) {
268         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
269             if SP_IS_FLOWREGION(child) {
270                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
271                 if (grandchild && SP_IS_PATH(grandchild)) {
272                     object = SP_ITEM(grandchild);
273                     break;
274                 }
275             }
276         }
277     }
279     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
281     if (curve == NULL) {
282         return NULL;
283     }
285     if (curve->get_segment_count() < 1) {
286         curve->unref();
287         return NULL; // prevent crash for one-node paths
288     }
290     //Create new nodepath
291     Inkscape::NodePath::Path *np = new Inkscape::NodePath::Path();
292     if (!np) {
293         curve->unref();
294         return NULL;
295     }
297     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
299     // Set defaults
300     np->desktop     = desktop;
301     np->object      = object;
302     np->subpaths    = NULL;
303     np->selected    = NULL;
304     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
305     np->local_change = 0;
306     np->show_handles = show_handles;
307     np->helper_path = NULL;
308     np->helperpath_rgba = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
309     np->helperpath_width = 1.0;
310     np->curve = curve->copy();
311     np->show_helperpath = prefs->getBool("/tools/nodes/show_helperpath");
312     if (SP_IS_LPE_ITEM(object)) {
313         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
314         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
315             np->show_helperpath = true;
316         }
317     }
318     np->straight_path = false;
319     if (IS_LIVEPATHEFFECT(object) && item) {
320         np->item = item;
321     } else {
322         np->item = SP_ITEM(object);
323     }
325     np->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
327     // we need to update item's transform from the repr here,
328     // because they may be out of sync when we respond
329     // to a change in repr by regenerating nodepath     --bb
330     sp_object_read_attr(SP_OBJECT(np->item), "transform");
332     np->i2d  = sp_item_i2d_affine(np->item);
333     np->d2i  = np->i2d.inverse();
335     np->repr = repr;
336     if (repr_key_in) { // apparently the object is an LPEObject
337         np->repr_key = g_strdup(repr_key_in);
338         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
339         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(object)->get_lpe();
340         if (!lpe) {
341             g_error("sp_nodepath_new: lpeobject without real lpe passed as argument!");
342             delete np;
343         }
344         Inkscape::LivePathEffect::Parameter *lpeparam = lpe->getParameter(repr_key_in);
345         if (lpeparam) {
346             lpeparam->param_setup_nodepath(np);
347         }
348     } else {
349         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
350         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
351             np->repr_key = g_strdup("inkscape:original-d");
353             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
354             if (lpe) {
355                 lpe->setup_nodepath(np);
356             }
357         } else {
358             np->repr_key = g_strdup("d");
359         }
360     }
362     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
363      * So for example a closed rectangle has a nodetypestring of length 5.
364      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
365     Geom::PathVector pathv_sanitized = pathv_to_linear_and_cubic_beziers(np->curve->get_pathvector());
366     np->curve->set_pathvector(pathv_sanitized);
367     guint length = np->curve->get_segment_count();
368     for (Geom::PathVector::const_iterator pit = pathv_sanitized.begin(); pit != pathv_sanitized.end(); ++pit) {
369         length += pit->empty() ? 0 : 1;
370     }
372     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
373     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
375     // create the subpath(s) from the bpath
376     subpaths_from_pathvector(np, pathv_sanitized, typestr);
378     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
379     np->subpaths = g_list_reverse(np->subpaths);
381     delete[] typestr;
382     curve->unref();
384     // Draw helper curve
385     if (np->show_helperpath) {
386         np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true, np->helperpath_rgba);
387     }
389     sp_nodepath_create_helperpaths(np);
391     return np;
394 /**
395  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
396  */
397 Inkscape::NodePath::Path::~Path() {
398     while (this->subpaths) {
399         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) this->subpaths->data);
400     }
402     //Inform the ShapeEditor that made me, if any, that I am gone.
403     if (this->shape_editor)
404         this->shape_editor->nodepath_destroyed();
406     g_assert(!this->selected);
408     if (this->helper_path) {
409         GtkObject *temp = this->helper_path;
410         this->helper_path = NULL;
411         gtk_object_destroy(temp);
412     }
413     if (this->curve) {
414         this->curve->unref();
415         this->curve = NULL;
416     }
418     if (this->repr_key) {
419         g_free(this->repr_key);
420         this->repr_key = NULL;
421     }
422     if (this->repr_nodetypes_key) {
423         g_free(this->repr_nodetypes_key);
424         this->repr_nodetypes_key = NULL;
425     }
427     sp_nodepath_destroy_helperpaths(this);
429     this->desktop = NULL;
432 /**
433  *  Return the node count of a given NodeSubPath.
434  */
435 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
437     int nodeCount = 0;
439     if (subpath) {
440         nodeCount = g_list_length(subpath->nodes);
441     }
443     return nodeCount;
446 /**
447  *  Return the node count of a given NodePath.
448  */
449 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
451     gint nodeCount = 0;
452     if (np) {
453         for (GList *item = np->subpaths ; item ; item=item->next) {
454             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
455             nodeCount += g_list_length(subpath->nodes);
456         }
457     }
458     return nodeCount;
461 /**
462  *  Return the subpath count of a given NodePath.
463  */
464 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
466     gint nodeCount = 0;
467     if (np) {
468         nodeCount = g_list_length(np->subpaths);
469     }
470     return nodeCount;
473 /**
474  *  Return the selected node count of a given NodePath.
475  */
476 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
478     gint nodeCount = 0;
479     if (np) {
480         nodeCount = g_list_length(np->selected);
481     }
482     return nodeCount;
485 /**
486  *  Return the number of subpaths where nodes are selected in a given NodePath.
487  */
488 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
490     gint nodeCount = 0;
491     if (np && np->selected) {
492         if (!np->selected->next) {
493             nodeCount = 1;
494         } else {
495             for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
496                 Inkscape::NodePath::SubPath *subpath = static_cast<Inkscape::NodePath::SubPath *>(spl->data);
497                 for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
498                     Inkscape::NodePath::Node *node = static_cast<Inkscape::NodePath::Node *>(nl->data);
499                     if (node->selected) {
500                         nodeCount++;
501                         break;
502                     }
503                 }
504             }
505         }
506     }
507     return nodeCount;
510 /**
511  * Clean up a nodepath after editing.
512  *
513  * Currently we are deleting trivial subpaths.
514  */
515 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
517     GList *badSubPaths = NULL;
519     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
520     for (GList *l = nodepath->subpaths; l ; l=l->next) {
521        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
522        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
523             badSubPaths = g_list_append(badSubPaths, sp);
524     }
526     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
527     //also removes the subpath from nodepath->subpaths
528     for (GList *l = badSubPaths; l ; l=l->next) {
529        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
530         sp_nodepath_subpath_destroy(sp);
531     }
533     g_list_free(badSubPaths);
536 /**
537  * Create new nodepaths from pathvector, make it subpaths of np.
538  * \param t The node type array.
539  */
540 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
542     guint i = 0;  // index into node type array
543     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
544         if (pit->empty())
545             continue;  // don't add single knot paths
547         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
549         Geom::Point ppos = pit->initialPoint() * np->i2d;
550         NRPathcode pcode = NR_MOVETO;
552         /* 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)*/
553         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
554             if( dynamic_cast<Geom::LineSegment const*>(&*cit) ||
555                 dynamic_cast<Geom::HLineSegment const*>(&*cit) ||
556                 dynamic_cast<Geom::VLineSegment const*>(&*cit) )
557             {
558                 Geom::Point pos = cit->initialPoint() * (Geom::Matrix)np->i2d;
559                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
561                 ppos = cit->finalPoint() * (Geom::Matrix)np->i2d;
562                 pcode = NR_LINETO;
563             }
564             else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
565                 std::vector<Geom::Point> points = cubic_bezier->points();
566                 Geom::Point pos = points[0] * (Geom::Matrix)np->i2d;
567                 Geom::Point npos = points[1] * (Geom::Matrix)np->i2d;
568                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
570                 ppos = points[2] * (Geom::Matrix)np->i2d;
571                 pcode = NR_CURVETO;
572             }
573         }
575         if (pit->closed()) {
576             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
577             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
578              * If the length is zero, don't add it to the nodepath. */
579             Geom::Curve const &closing_seg = pit->back_closed();
580             // Don't use !closing_seg.isDegenerate() as it is too precise, and does not account for floating point rounding probs (LP bug #257289)
581             if ( ! are_near(closing_seg.initialPoint(), closing_seg.finalPoint()) ) {
582                 Geom::Point pos = closing_seg.finalPoint() * (Geom::Matrix)np->i2d;
583                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
584             }
586             sp_nodepath_subpath_close(sp);
587         }
588     }
591 /**
592  * Convert from sodipodi:nodetypes to new style type array.
593  */
594 static
595 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
597     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
599     guint pos = 0;
601     if (types) {
602         for (guint i = 0; types[i] && ( i < length ); i++) {
603             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
604             if (types[i] != '\0') {
605                 switch (types[i]) {
606                     case 's':
607                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
608                         break;
609                     case 'a':
610                         typestr[pos++] =Inkscape::NodePath::NODE_AUTO;
611                         break;
612                     case 'z':
613                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
614                         break;
615                     case 'c':
616                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
617                         break;
618                     default:
619                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
620                         break;
621                 }
622             }
623         }
624     }
626     while (pos < length) {
627         typestr[pos++] = Inkscape::NodePath::NODE_NONE;
628     }
630     return typestr;
633 /**
634  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
635  * updated but repr is not (for speed). Used during curve and node drag.
636  */
637 static void update_object(Inkscape::NodePath::Path *np)
639     g_assert(np);
641     np->curve->unref();
642     np->curve = create_curve(np);
644     sp_nodepath_set_curve(np, np->curve);
646     if (np->show_helperpath) {
647         SPCurve * helper_curve = np->curve->copy();
648         helper_curve->transform(np->i2d);
649         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
650         helper_curve->unref();
651     }
653     // updating helperpaths of LPEItems is now done in sp_lpe_item_update();
654     //sp_nodepath_update_helperpaths(np);
656     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
657     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
658     np->shape_editor->update_knotholder();
661 /**
662  * Update XML path node with data from path object.
663  */
664 static void update_repr_internal(Inkscape::NodePath::Path *np)
666     g_assert(np);
668     Inkscape::XML::Node *repr = np->object->repr;
670     np->curve->unref();
671     np->curve = create_curve(np);
673     gchar *typestr = create_typestr(np);
674     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
676     // determine if path has an effect applied and write to correct "d" attribute.
677     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
678         np->local_change++;
679         repr->setAttribute(np->repr_key, svgpath);
680     }
682     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
683         np->local_change++;
684         repr->setAttribute(np->repr_nodetypes_key, typestr);
685     }
687     g_free(svgpath);
688     g_free(typestr);
690     if (np->show_helperpath) {
691         SPCurve * helper_curve = np->curve->copy();
692         helper_curve->transform(np->i2d);
693         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
694         helper_curve->unref();
695     }
697     // TODO: do we need this call here? after all, update_object() should have been called just before
698     //sp_nodepath_update_helperpaths(np);
701 /**
702  * Update XML path node with data from path object, commit changes forever.
703  */
704 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
706     //fixme: np can be NULL, so check before proceeding
707     g_return_if_fail(np != NULL);
709     update_repr_internal(np);
710     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
712     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
713                      annotation);
716 /**
717  * Update XML path node with data from path object, commit changes with undo.
718  */
719 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
721     update_repr_internal(np);
722     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
723                            annotation);
726 /**
727  * Make duplicate of path, replace corresponding XML node in tree, commit.
728  */
729 static void stamp_repr(Inkscape::NodePath::Path *np)
731     g_assert(np);
733     Inkscape::XML::Node *old_repr = np->object->repr;
734     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
736     // remember the position of the item
737     gint pos = old_repr->position();
738     // remember parent
739     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
741     SPCurve *curve = create_curve(np);
742     gchar *typestr = create_typestr(np);
744     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
746     new_repr->setAttribute(np->repr_key, svgpath);
747     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
749     // add the new repr to the parent
750     parent->appendChild(new_repr);
751     // move to the saved position
752     new_repr->setPosition(pos > 0 ? pos : 0);
754     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
755                      _("Stamp"));
757     Inkscape::GC::release(new_repr);
758     g_free(svgpath);
759     g_free(typestr);
760     curve->unref();
763 /**
764  * Create curve from path.
765  */
766 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
768     SPCurve *curve = new SPCurve();
770     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
771        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
772        curve->moveto(sp->first->pos * np->d2i);
773        Inkscape::NodePath::Node *n = sp->first->n.other;
774         while (n) {
775             Geom::Point const end_pt = n->pos * np->d2i;
776             if (!IS_FINITE(n->pos[0]) || !IS_FINITE(n->pos[1])){
777                 g_message("niet finite");
778             }
779             switch (n->code) {
780                 case NR_LINETO:
781                     curve->lineto(end_pt);
782                     break;
783                 case NR_CURVETO:
784                     curve->curveto(n->p.other->n.pos * np->d2i,
785                                      n->p.pos * np->d2i,
786                                      end_pt);
787                     break;
788                 default:
789                     g_assert_not_reached();
790                     break;
791             }
792             if (n != sp->last) {
793                 n = n->n.other;
794             } else {
795                 n = NULL;
796             }
797         }
798         if (sp->closed) {
799             curve->closepath();
800         }
801     }
803     return curve;
806 /**
807  * Convert path type string to sodipodi:nodetypes style.
808  */
809 static gchar *create_typestr(Inkscape::NodePath::Path *np)
811     gchar *typestr = g_new(gchar, 32);
812     gint len = 32;
813     gint pos = 0;
815     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
816        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
818         if (pos >= len) {
819             typestr = g_renew(gchar, typestr, len + 32);
820             len += 32;
821         }
823         typestr[pos++] = 'c';
825        Inkscape::NodePath::Node *n;
826         n = sp->first->n.other;
827         while (n) {
828             gchar code;
830             switch (n->type) {
831                 case Inkscape::NodePath::NODE_CUSP:
832                     code = 'c';
833                     break;
834                 case Inkscape::NodePath::NODE_SMOOTH:
835                     code = 's';
836                     break;
837                 case Inkscape::NodePath::NODE_AUTO:
838                     code = 'a';
839                     break;
840                 case Inkscape::NodePath::NODE_SYMM:
841                     code = 'z';
842                     break;
843                 default:
844                     g_assert_not_reached();
845                     code = '\0';
846                     break;
847             }
849             if (pos >= len) {
850                 typestr = g_renew(gchar, typestr, len + 32);
851                 len += 32;
852             }
854             typestr[pos++] = code;
856             if (n != sp->last) {
857                 n = n->n.other;
858             } else {
859                 n = NULL;
860             }
861         }
862     }
864     if (pos >= len) {
865         typestr = g_renew(gchar, typestr, len + 1);
866         len += 1;
867     }
869     typestr[pos++] = '\0';
871     return typestr;
874 // Returns different message contexts depending on the current context. This function should only
875 // be called when ec is either a SPNodeContext or SPLPEToolContext, thus we return NULL in all
876 // other cases.
877 static Inkscape::MessageContext *
878 get_message_context(SPEventContext *ec)
880     Inkscape::MessageContext *mc = 0;
882     if (SP_IS_NODE_CONTEXT(ec)) {
883         mc = SP_NODE_CONTEXT(ec)->_node_message_context;
884     } else if (SP_IS_LPETOOL_CONTEXT(ec)) {
885         mc = SP_LPETOOL_CONTEXT(ec)->_lpetool_message_context;
886     } else {
887         g_warning ("Nodepath should only be present in Node tool or Geometric tool.");
888     }
890     return mc;
893 /**
894  \brief Fills node and handle positions for three nodes, splitting line
895   marked by end at distance t.
896  */
897 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
899     g_assert(new_path != NULL);
900     g_assert(end      != NULL);
902     g_assert(end->p.other == new_path);
903    Inkscape::NodePath::Node *start = new_path->p.other;
904     g_assert(start);
906     if (end->code == NR_LINETO) {
907         new_path->type =Inkscape::NodePath::NODE_CUSP;
908         new_path->code = NR_LINETO;
909         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
910     } else {
911         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
912         new_path->code = NR_CURVETO;
913         gdouble s      = 1 - t;
914         for (int dim = 0; dim < 2; dim++) {
915             Geom::Coord const f000 = start->pos[dim];
916             Geom::Coord const f001 = start->n.pos[dim];
917             Geom::Coord const f011 = end->p.pos[dim];
918             Geom::Coord const f111 = end->pos[dim];
919             Geom::Coord const f00t = s * f000 + t * f001;
920             Geom::Coord const f01t = s * f001 + t * f011;
921             Geom::Coord const f11t = s * f011 + t * f111;
922             Geom::Coord const f0tt = s * f00t + t * f01t;
923             Geom::Coord const f1tt = s * f01t + t * f11t;
924             Geom::Coord const fttt = s * f0tt + t * f1tt;
925             start->n.pos[dim]    = f00t;
926             new_path->p.pos[dim] = f0tt;
927             new_path->pos[dim]   = fttt;
928             new_path->n.pos[dim] = f1tt;
929             end->p.pos[dim]      = f11t;
930         }
931     }
934 /**
935  * Adds new node on direct line between two nodes, activates handles of all
936  * three nodes.
937  */
938 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
940     g_assert(end);
941     g_assert(end->subpath);
942     g_assert(g_list_find(end->subpath->nodes, end));
944    Inkscape::NodePath::Node *start = end->p.other;
945     g_assert( start->n.other == end );
946    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
947                                                end,
948                                                (NRPathcode)end->code == NR_LINETO?
949                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
950                                                (NRPathcode)end->code,
951                                                &start->pos, &start->pos, &start->n.pos);
952     sp_nodepath_line_midpoint(newnode, end, t);
954     sp_node_adjust_handles(start);
955     sp_node_update_handles(start);
956     sp_node_update_handles(newnode);
957     sp_node_adjust_handles(end);
958     sp_node_update_handles(end);
960     return newnode;
963 /**
964 \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
965 */
966 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
968     g_assert(node);
969     g_assert(node->subpath);
970     g_assert(g_list_find(node->subpath->nodes, node));
972     Inkscape::NodePath::Node* result = 0;
973     Inkscape::NodePath::SubPath *sp = node->subpath;
974     Inkscape::NodePath::Path *np    = sp->nodepath;
976     if (sp->closed) {
977         sp_nodepath_subpath_open(sp, node);
978         result = sp->first;
979     } else if ( (node == sp->first) || (node == sp->last ) ){
980         // no break for end nodes
981         result = 0;
982     } else {
983         // create a new subpath
984         Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
986         // duplicate the break node as start of the new subpath
987         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL,
988                                                                  static_cast<Inkscape::NodePath::NodeType>(node->type),
989                                                                  NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
991         // attach rest of curve to new node
992         g_assert(node->n.other);
993         newnode->n.other = node->n.other; node->n.other = NULL;
994         newnode->n.other->p.other = newnode;
995         newsubpath->last = sp->last;
996         sp->last = node;
997         node = newnode;
998         while (node->n.other) {
999             node = node->n.other;
1000             node->subpath = newsubpath;
1001             sp->nodes = g_list_remove(sp->nodes, node);
1002             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
1003         }
1006         result = newnode;
1007     }
1008     return result;
1011 /**
1012  * Duplicate node and connect to neighbours.
1013  */
1014 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
1016     g_assert(node);
1017     g_assert(node->subpath);
1018     g_assert(g_list_find(node->subpath->nodes, node));
1020    Inkscape::NodePath::SubPath *sp = node->subpath;
1022     NRPathcode code = (NRPathcode) node->code;
1023     if (code == NR_MOVETO) { // if node is the endnode,
1024         node->code = NR_LINETO; // new one is inserted before it, so change that to line
1025     }
1027     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
1029     if (!node->n.other || !node->p.other) { // if node is an endnode, select it
1030         return node;
1031     } else {
1032         return newnode; // otherwise select the newly created node
1033     }
1036 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
1038     node->p.pos = (node->pos + (node->pos - node->n.pos));
1041 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
1043     node->n.pos = (node->pos + (node->pos - node->p.pos));
1046 /**
1047  * Change line type at node, with side effects on neighbours.
1048  */
1049 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
1051     g_assert(end);
1052     g_assert(end->subpath);
1053     g_assert(end->p.other);
1055     if (end->code != static_cast<guint>(code) ) {
1056         Inkscape::NodePath::Node *start = end->p.other;
1058         end->code = code;
1060         if (code == NR_LINETO) {
1061             if (start->code == NR_LINETO) {
1062                 sp_nodepath_set_node_type(start, Inkscape::NodePath::NODE_CUSP);
1063             }
1064             if (end->n.other) {
1065                 if (end->n.other->code == NR_LINETO) {
1066                     sp_nodepath_set_node_type(end, Inkscape::NodePath::NODE_CUSP);
1067                 }
1068             }
1070             if (start->type == Inkscape::NodePath::NODE_AUTO)
1071                 start->type = Inkscape::NodePath::NODE_SMOOTH;
1072             if (end->type == Inkscape::NodePath::NODE_AUTO)
1073                 end->type = Inkscape::NodePath::NODE_SMOOTH;
1075             start->n.pos = start->pos;
1076             end->p.pos = end->pos;
1078             sp_node_adjust_handle(start, -1);
1079             sp_node_adjust_handle(end, 1);
1081         } else {
1082             Geom::Point delta = end->pos - start->pos;
1083             start->n.pos = start->pos + delta / 3;
1084             end->p.pos = end->pos - delta / 3;
1085             sp_node_adjust_handle(start, 1);
1086             sp_node_adjust_handle(end, -1);
1087         }
1089         sp_node_update_handles(start);
1090         sp_node_update_handles(end);
1091     }
1094 static void
1095 sp_nodepath_update_node_knot(Inkscape::NodePath::Node *node)
1097     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1098         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1099         node->knot->setSize (node->selected? 11 : 9);
1100         sp_knot_update_ctrl(node->knot);
1101     } else if (node->type == Inkscape::NodePath::NODE_AUTO) {
1102         node->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1103         node->knot->setSize (node->selected? 11 : 9);
1104         sp_knot_update_ctrl(node->knot);
1105     } else {
1106         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1107         node->knot->setSize (node->selected? 9 : 7);
1108         sp_knot_update_ctrl(node->knot);
1109     }
1113 /**
1114  * Change node type, and its handles accordingly.
1115  */
1116 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1118     g_assert(node);
1119     g_assert(node->subpath);
1121     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1122         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1123             type =Inkscape::NodePath::NODE_CUSP;
1124         }
1125     }
1127     node->type = type;
1129     sp_nodepath_update_node_knot(node);
1131     // if one of handles is mouseovered, preserve its position
1132     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1133         sp_node_adjust_handle(node, 1);
1134     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1135         sp_node_adjust_handle(node, -1);
1136     } else {
1137         sp_node_adjust_handles(node);
1138     }
1140     sp_node_update_handles(node);
1142     sp_nodepath_update_statusbar(node->subpath->nodepath);
1144     return node;
1147 bool
1148 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1150 // TODO clean up multiple returns
1151         Inkscape::NodePath::Node *othernode = side->other;
1152         if (!othernode)
1153             return false;
1154         NRPathcode const code = sp_node_path_code_from_side(node, side);
1155         if (code == NR_LINETO)
1156             return true;
1157         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1158         if (&node->p == side) {
1159             other_to_me = &othernode->n;
1160         } else if (&node->n == side) {
1161             other_to_me = &othernode->p;
1162         }
1163         if (!other_to_me)
1164             return false;
1165         bool is_line =
1166              (Geom::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1167               Geom::L2(node->pos - side->pos) < 1e-6);
1168         return is_line;
1171 /**
1172  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1173  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1174  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too.
1175  * If already cusp and set to cusp, retracts handles.
1176 */
1177 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1179     if (type == Inkscape::NodePath::NODE_AUTO) {
1180         if (node->p.other != NULL)
1181             node->code = NR_CURVETO;
1182         if (node->n.other != NULL)
1183             node->n.other->code = NR_CURVETO;
1184     }
1186     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1188 /*
1189   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1191         if (two_handles) {
1192             // do nothing, adjust_handles called via set_node_type will line them up
1193         } else if (one_handle) {
1194             if (opposite_to_handle_is_line) {
1195                 if (lined_up) {
1196                     // already half-smooth; pull opposite handle too making it fully smooth
1197                 } else {
1198                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1199                 }
1200             } else {
1201                 // pull opposite handle in line with the existing one
1202             }
1203         } else if (no_handles) {
1204             if (both_segments_are_lines OR both_segments_are_curves) {
1205                 //pull both handles
1206             } else {
1207                 // pull the handle opposite to line segment, making node half-smooth
1208             }
1209         }
1210 */
1211         bool p_has_handle = (Geom::L2(node->pos  - node->p.pos) > 1e-6);
1212         bool n_has_handle = (Geom::L2(node->pos  - node->n.pos) > 1e-6);
1213         bool p_is_line = sp_node_side_is_line(node, &node->p);
1214         bool n_is_line = sp_node_side_is_line(node, &node->n);
1216         if (p_has_handle && n_has_handle) {
1217             // do nothing, adjust_handles will line them up
1218         } else if (p_has_handle || n_has_handle) {
1219             if (p_has_handle && n_is_line) {
1220                 Radial line (node->n.other->pos - node->pos);
1221                 Radial handle (node->pos - node->p.pos);
1222                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1223                     // already half-smooth; pull opposite handle too making it fully smooth
1224                     node->n.other->code = NR_CURVETO;
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->code = NR_CURVETO;
1235                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1236                 } else {
1237                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1238                 }
1239             } else if (p_has_handle && node->n.other) {
1240                 // pull n handle
1241                 node->n.other->code = NR_CURVETO;
1242                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1243                     Geom::L2(node->p.pos - node->pos) :
1244                     Geom::L2(node->n.other->pos - node->pos) / 3;
1245                 node->n.pos = node->pos - (len / Geom::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1246             } else if (n_has_handle && node->p.other) {
1247                 // pull p handle
1248                 node->code = NR_CURVETO;
1249                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1250                     Geom::L2(node->n.pos - node->pos) :
1251                     Geom::L2(node->p.other->pos - node->pos) / 3;
1252                 node->p.pos = node->pos - (len / Geom::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1253             }
1254         } else if (!p_has_handle && !n_has_handle) {
1255             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1256                 // no handles, but both segments are either lnes or curves:
1257                 //pull both handles
1259                 // convert both to curves:
1260                 node->code = NR_CURVETO;
1261                 node->n.other->code = NR_CURVETO;
1263                 sp_node_adjust_handles_auto(node);
1264             } else {
1265                 // pull the handle opposite to line segment, making it half-smooth
1266                 if (p_is_line && node->n.other) {
1267                     if (type != Inkscape::NodePath::NODE_SYMM) {
1268                         // pull n handle
1269                         node->n.other->code = NR_CURVETO;
1270                         double len =  Geom::L2(node->n.other->pos - node->pos) / 3;
1271                         node->n.pos = node->pos + (len / Geom::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1272                     }
1273                 } else if (n_is_line && node->p.other) {
1274                     if (type != Inkscape::NodePath::NODE_SYMM) {
1275                         // pull p handle
1276                         node->code = NR_CURVETO;
1277                         double len =  Geom::L2(node->p.other->pos - node->pos) / 3;
1278                         node->p.pos = node->pos + (len / Geom::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1279                     }
1280                 }
1281             }
1282         }
1283     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1284         // cusping a cusp: retract nodes
1285         node->p.pos = node->pos;
1286         node->n.pos = node->pos;
1287     }
1289     sp_nodepath_set_node_type (node, type);
1292 /**
1293  * Move node to point, and adjust its and neighbouring handles.
1294  */
1295 void sp_node_moveto(Inkscape::NodePath::Node *node, Geom::Point p)
1297     if (node->type == Inkscape::NodePath::NODE_AUTO) {
1298         node->pos = p;
1299         sp_node_adjust_handles_auto(node);
1300     } else {
1301         Geom::Point delta = p - node->pos;
1302         node->pos = p;
1304         node->p.pos += delta;
1305         node->n.pos += delta;
1306     }
1308     Inkscape::NodePath::Node *node_p = NULL;
1309     Inkscape::NodePath::Node *node_n = NULL;
1311     if (node->p.other) {
1312         if (node->code == NR_LINETO) {
1313             sp_node_adjust_handle(node, 1);
1314             sp_node_adjust_handle(node->p.other, -1);
1315             node_p = node->p.other;
1316         }
1317         if (!node->p.other->selected && node->p.other->type == Inkscape::NodePath::NODE_AUTO) {
1318             sp_node_adjust_handles_auto(node->p.other);
1319             node_p = node->p.other;
1320         }
1321     }
1322     if (node->n.other) {
1323         if (node->n.other->code == NR_LINETO) {
1324             sp_node_adjust_handle(node, -1);
1325             sp_node_adjust_handle(node->n.other, 1);
1326             node_n = node->n.other;
1327         }
1328         if (!node->n.other->selected && node->n.other->type == Inkscape::NodePath::NODE_AUTO) {
1329             sp_node_adjust_handles_auto(node->n.other);
1330             node_n = node->n.other;
1331         }
1332     }
1334     // this function is only called from batch movers that will update display at the end
1335     // themselves, so here we just move all the knots without emitting move signals, for speed
1336     sp_node_update_handles(node, false);
1337     if (node_n) {
1338         sp_node_update_handles(node_n, false);
1339     }
1340     if (node_p) {
1341         sp_node_update_handles(node_p, false);
1342     }
1345 /**
1346  * Call sp_node_moveto() for node selection and handle possible snapping.
1347  */
1348 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, Geom::Coord dx, Geom::Coord dy,
1349                                             bool const snap, bool constrained = false,
1350                                             Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point())
1352     Geom::Point delta(dx, dy);
1353     Geom::Point best_pt = delta;
1354     Inkscape::SnappedPoint best;
1356     if (snap) {
1357         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1358          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we
1359          * must provide that information. */
1361         // Build a list of the unselected nodes to which the snapper should snap
1362         std::vector<std::pair<Geom::Point, int> > unselected_nodes;
1363         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1364             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1365             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1366                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1367                 if (!node->selected) {
1368                     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));
1369                 }
1370             }
1371         }
1373         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1375         // When only the node closest to the mouse pointer is to be snapped
1376         // then we will not even try to snap to other points and discard those immediately
1377         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1378         bool closest_only = prefs->getBool("/options/snapclosestonly/value", false);
1380         Inkscape::NodePath::Node *closest_node = NULL;
1381         Geom::Coord closest_dist = NR_HUGE;
1383         if (closest_only) {
1384                 for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1385                         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1386                         Geom::Coord dist = Geom::L2(nodepath->drag_origin_mouse - n->origin);
1387                         if (dist < closest_dist) {
1388                                 closest_node = n;
1389                                 closest_dist = dist;
1390                         }
1391                 }
1392         }
1394         // Iterate through all selected nodes
1395         m.setup(nodepath->desktop, false, nodepath->item, &unselected_nodes);
1396         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1397             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1398             if (!closest_only || n == closest_node) { //try to snap either all selected nodes or only the closest one
1399                     Inkscape::SnappedPoint s;
1400                     Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP);
1401                     if (constrained) {
1402                         Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1403                         dedicated_constraint.setPoint(n->pos);
1404                         s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type, dedicated_constraint, false);
1405                     } else {
1406                         s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type);
1407                     }
1409                     if (s.getSnapped()) {
1410                         s.setPointerDistance(Geom::L2(nodepath->drag_origin_mouse - n->origin));
1411                         if (!s.isOtherSnapBetter(best, true)) {
1412                                 best = s;
1413                                 best_pt = from_2geom(s.getPoint()) - n->pos;
1414                         }
1415                     }
1416             }
1417         }
1419         if (best.getSnapped()) {
1420             nodepath->desktop->snapindicator->set_new_snaptarget(best);
1421         } else {
1422             nodepath->desktop->snapindicator->remove_snaptarget();
1423         }
1424     }
1426     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1427         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1428         sp_node_moveto(n, n->pos + best_pt);
1429     }
1431     // do not update repr here so that node dragging is acceptably fast
1432     update_object(nodepath);
1435 /**
1436 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1437 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1438 near x = 0.
1439  */
1440 double
1441 sculpt_profile (double x, double alpha, guint profile)
1443     double result = 1;
1445     if (x >= 1) {
1446         result = 0;
1447     } else if (x <= 0) {
1448         result = 1;
1449     } else {
1450         switch (profile) {
1451             case SCULPT_PROFILE_LINEAR:
1452                 result = 1 - x;
1453                 break;
1454             case SCULPT_PROFILE_BELL:
1455                 result = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1456                 break;
1457             case SCULPT_PROFILE_ELLIPTIC:
1458                 result = sqrt(1 - x*x);
1459                 break;
1460             default:
1461                 g_assert_not_reached();
1462         }
1463     }
1465     return result;
1468 double
1469 bezier_length (Geom::Point a, Geom::Point ah, Geom::Point bh, Geom::Point b)
1471     // extremely primitive for now, don't have time to look for the real one
1472     double lower = Geom::L2(b - a);
1473     double upper = Geom::L2(ah - a) + Geom::L2(bh - ah) + Geom::L2(bh - b);
1474     return (lower + upper)/2;
1477 void
1478 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, Geom::Point delta, Geom::Point delta_n, Geom::Point delta_p)
1480     n->pos = n->origin + delta;
1481     n->n.pos = n->n.origin + delta_n;
1482     n->p.pos = n->p.origin + delta_p;
1483     sp_node_adjust_handles(n);
1484     sp_node_update_handles(n, false);
1487 /**
1488  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1489  * on how far they are from the dragged node n.
1490  */
1491 static void
1492 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, Geom::Point delta)
1494     g_assert (n);
1495     g_assert (nodepath);
1496     g_assert (n->subpath->nodepath == nodepath);
1498     double pressure = n->knot->pressure;
1499     if (pressure == 0)
1500         pressure = 0.5; // default
1501     pressure = CLAMP (pressure, 0.2, 0.8);
1503     // map pressure to alpha = 1/5 ... 5
1504     double alpha = 1 - 2 * fabs(pressure - 0.5);
1505     if (pressure > 0.5)
1506         alpha = 1/alpha;
1508     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1509     guint profile = prefs->getInt("/tools/nodes/sculpting_profile", SCULPT_PROFILE_BELL);
1511     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1512         // Only one subpath has selected nodes:
1513         // use linear mode, where the distance from n to node being dragged is calculated along the path
1515         double n_sel_range = 0, p_sel_range = 0;
1516         guint n_nodes = 0, p_nodes = 0;
1517         guint n_sel_nodes = 0, p_sel_nodes = 0;
1519         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1520         {
1521             double n_range = 0, p_range = 0;
1522             bool n_going = true, p_going = true;
1523             Inkscape::NodePath::Node *n_node = n;
1524             Inkscape::NodePath::Node *p_node = n;
1525             do {
1526                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1527                 if (n_node && n_going)
1528                     n_node = n_node->n.other;
1529                 if (n_node == NULL) {
1530                     n_going = false;
1531                 } else {
1532                     n_nodes ++;
1533                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1534                     if (n_node->selected) {
1535                         n_sel_nodes ++;
1536                         n_sel_range = n_range;
1537                     }
1538                     if (n_node == p_node) {
1539                         n_going = false;
1540                         p_going = false;
1541                     }
1542                 }
1543                 if (p_node && p_going)
1544                     p_node = p_node->p.other;
1545                 if (p_node == NULL) {
1546                     p_going = false;
1547                 } else {
1548                     p_nodes ++;
1549                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1550                     if (p_node->selected) {
1551                         p_sel_nodes ++;
1552                         p_sel_range = p_range;
1553                     }
1554                     if (p_node == n_node) {
1555                         n_going = false;
1556                         p_going = false;
1557                     }
1558                 }
1559             } while (n_going || p_going);
1560         }
1562         // Second pass: actually move nodes in this subpath
1563         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1564         {
1565             double n_range = 0, p_range = 0;
1566             bool n_going = true, p_going = true;
1567             Inkscape::NodePath::Node *n_node = n;
1568             Inkscape::NodePath::Node *p_node = n;
1569             do {
1570                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1571                 if (n_node && n_going)
1572                     n_node = n_node->n.other;
1573                 if (n_node == NULL) {
1574                     n_going = false;
1575                 } else {
1576                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1577                     if (n_node->selected) {
1578                         sp_nodepath_move_node_and_handles (n_node,
1579                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1580                                                            sculpt_profile ((n_range + Geom::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1581                                                            sculpt_profile ((n_range - Geom::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1582                     }
1583                     if (n_node == p_node) {
1584                         n_going = false;
1585                         p_going = false;
1586                     }
1587                 }
1588                 if (p_node && p_going)
1589                     p_node = p_node->p.other;
1590                 if (p_node == NULL) {
1591                     p_going = false;
1592                 } else {
1593                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1594                     if (p_node->selected) {
1595                         sp_nodepath_move_node_and_handles (p_node,
1596                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1597                                                            sculpt_profile ((p_range - Geom::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1598                                                            sculpt_profile ((p_range + Geom::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1599                     }
1600                     if (p_node == n_node) {
1601                         n_going = false;
1602                         p_going = false;
1603                     }
1604                 }
1605             } while (n_going || p_going);
1606         }
1608     } else {
1609         // Multiple subpaths have selected nodes:
1610         // use spatial mode, where the distance from n to node being dragged is measured directly as Geom::L2.
1611         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1612         // fix the pear-like shape when sculpting e.g. a ring
1614         // First pass: calculate range
1615         gdouble direct_range = 0;
1616         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1617             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1618             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1619                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1620                 if (node->selected) {
1621                     direct_range = MAX(direct_range, Geom::L2(node->origin - n->origin));
1622                 }
1623             }
1624         }
1626         // Second pass: actually move nodes
1627         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1628             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1629             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1630                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1631                 if (node->selected) {
1632                     if (direct_range > 1e-6) {
1633                         sp_nodepath_move_node_and_handles (node,
1634                                                        sculpt_profile (Geom::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1635                                                        sculpt_profile (Geom::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1636                                                        sculpt_profile (Geom::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1637                     } else {
1638                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1639                     }
1641                 }
1642             }
1643         }
1644     }
1646     // do not update repr here so that node dragging is acceptably fast
1647     update_object(nodepath);
1651 /**
1652  * Move node selection to point, adjust its and neighbouring handles,
1653  * handle possible snapping, and commit the change with possible undo.
1654  */
1655 void
1656 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1658     if (!nodepath) return;
1660     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1662     if (dx == 0) {
1663         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1664     } else if (dy == 0) {
1665         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1666     } else {
1667         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1668     }
1671 /**
1672  * Move node selection off screen and commit the change.
1673  */
1674 void
1675 sp_node_selected_move_screen(SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1677     // borrowed from sp_selection_move_screen in selection-chemistry.c
1678     // we find out the current zoom factor and divide deltas by it
1680     gdouble zoom = desktop->current_zoom();
1681     gdouble zdx = dx / zoom;
1682     gdouble zdy = dy / zoom;
1684     if (!nodepath) return;
1686     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1688     if (dx == 0) {
1689         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1690     } else if (dy == 0) {
1691         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1692     } else {
1693         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1694     }
1697 /**
1698  * Move selected nodes to the absolute position given
1699  */
1700 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis)
1702     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1703         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1704         Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]);
1705         sp_node_moveto(n, npos);
1706     }
1708     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1711 /**
1712  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return Geom::Nothing
1713  */
1714 boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1716     boost::optional<Geom::Coord> no_coord;
1717     g_return_val_if_fail(nodepath->selected, no_coord);
1719     // determine coordinate of first selected node
1720     GList *nsel = nodepath->selected;
1721     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1722     Geom::Coord coord = n->pos[axis];
1723     bool coincide = true;
1725     // compare it to the coordinates of all the other selected nodes
1726     for (GList *l = nsel->next; l != NULL; l = l->next) {
1727         n = (Inkscape::NodePath::Node *) l->data;
1728         if (n->pos[axis] != coord) {
1729             coincide = false;
1730         }
1731     }
1732     if (coincide) {
1733         return coord;
1734     } else {
1735         Geom::Rect bbox = sp_node_selected_bbox(nodepath);
1736         // currently we return the coordinate of the bounding box midpoint because I don't know how
1737         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1738         return bbox.midpoint()[axis];
1739     }
1742 /** If they don't yet exist, creates knot and line for the given side of the node */
1743 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1745     if (!side->knot) {
1746         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"));
1748         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1749         side->knot->setSize (7);
1750         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1751         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1752         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1753         sp_knot_update_ctrl(side->knot);
1755         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1756         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1757         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1758         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1759         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1760         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1761     }
1763     if (!side->line) {
1764         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1765                                         SP_TYPE_CTRLLINE, NULL);
1766     }
1769 /**
1770  * Ensure the given handle of the node is visible/invisible, update its screen position
1771  */
1772 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1774     g_assert(node != NULL);
1776    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1777     NRPathcode code = sp_node_path_code_from_side(node, side);
1779     show_handle = show_handle && (code == NR_CURVETO) && (Geom::L2(side->pos - node->pos) > 1e-6);
1781     if (show_handle) {
1782         if (!side->knot) { // No handle knot at all
1783             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1784             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1785             side->knot->pos = side->pos;
1786             if (side->knot->item)
1787                 SP_CTRL(side->knot->item)->moveto(side->pos);
1788             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1789             sp_knot_show(side->knot);
1790         } else {
1791             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1792                 if (fire_move_signals) {
1793                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1794                 } else {
1795                     sp_knot_moveto(side->knot, side->pos);
1796                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1797                 }
1798             }
1799             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1800                 sp_knot_show(side->knot);
1801             }
1802         }
1803         sp_canvas_item_show(side->line);
1804     } else {
1805         if (side->knot) {
1806             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1807                 sp_knot_hide(side->knot);
1808             }
1809         }
1810         if (side->line) {
1811             sp_canvas_item_hide(side->line);
1812         }
1813     }
1816 /**
1817  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1818  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1819  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1820  * updated; otherwise, just move the knots silently (used in batch moves).
1821  */
1822 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1824     g_assert(node != NULL);
1826     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1827         sp_knot_show(node->knot);
1828     }
1830     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1831         if (fire_move_signals)
1832             sp_knot_set_position(node->knot, node->pos, 0);
1833         else
1834             sp_knot_moveto(node->knot, node->pos);
1835     }
1837     gboolean show_handles = node->selected;
1838     if (node->p.other != NULL) {
1839         if (node->p.other->selected) show_handles = TRUE;
1840     }
1841     if (node->n.other != NULL) {
1842         if (node->n.other->selected) show_handles = TRUE;
1843     }
1845     if (node->subpath->nodepath->show_handles == false)
1846         show_handles = FALSE;
1848     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1849     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1852 /**
1853  * Call sp_node_update_handles() for all nodes on subpath.
1854  */
1855 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1857     g_assert(subpath != NULL);
1859     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1860         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1861     }
1864 /**
1865  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1866  */
1867 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1869     g_assert(nodepath != NULL);
1871     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1872         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1873     }
1876 void
1877 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1879     if (nodepath) {
1880         nodepath->show_handles = show;
1881         sp_nodepath_update_handles(nodepath);
1882     }
1885 /**
1886  * Adds all selected nodes in nodepath to list.
1887  */
1888 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1890     StlConv<Node *>::list(l, selected);
1891 /// \todo this adds a copying, rework when the selection becomes a stl list
1894 /**
1895  * Align selected nodes on the specified axis.
1896  */
1897 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1899     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1900         return;
1901     }
1903     if ( !nodepath->selected->next ) { // only one node selected
1904         return;
1905     }
1906    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1907     Geom::Point dest(pNode->pos);
1908     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1909         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1910         if (pNode) {
1911             dest[axis] = pNode->pos[axis];
1912             sp_node_moveto(pNode, dest);
1913         }
1914     }
1916     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1919 /// Helper struct.
1920 struct NodeSort
1922    Inkscape::NodePath::Node *_node;
1923     Geom::Coord _coord;
1924     /// \todo use vectorof pointers instead of calling copy ctor
1925     NodeSort(Inkscape::NodePath::Node *node, Geom::Dim2 axis) :
1926         _node(node), _coord(node->pos[axis])
1927     {}
1929 };
1931 static bool operator<(NodeSort const &a, NodeSort const &b)
1933     return (a._coord < b._coord);
1936 /**
1937  * Distribute selected nodes on the specified axis.
1938  */
1939 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1941     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1942         return;
1943     }
1945     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1946         return;
1947     }
1949    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1950     std::vector<NodeSort> sorted;
1951     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1952         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1953         if (pNode) {
1954             NodeSort n(pNode, axis);
1955             sorted.push_back(n);
1956             //dest[axis] = pNode->pos[axis];
1957             //sp_node_moveto(pNode, dest);
1958         }
1959     }
1960     std::sort(sorted.begin(), sorted.end());
1961     unsigned int len = sorted.size();
1962     //overall bboxes span
1963     float dist = (sorted.back()._coord -
1964                   sorted.front()._coord);
1965     //new distance between each bbox
1966     float step = (dist) / (len - 1);
1967     float pos = sorted.front()._coord;
1968     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1969           it < sorted.end();
1970           it ++ )
1971     {
1972         Geom::Point dest((*it)._node->pos);
1973         dest[axis] = pos;
1974         sp_node_moveto((*it)._node, dest);
1975         pos += step;
1976     }
1978     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1982 /**
1983  * Call sp_nodepath_line_add_node() for all selected segments.
1984  */
1985 void
1986 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1988     if (!nodepath) {
1989         return;
1990     }
1992     GList *nl = NULL;
1994     int n_added = 0;
1996     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1997        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1998         g_assert(t->selected);
1999         if (t->p.other && t->p.other->selected) {
2000             nl = g_list_prepend(nl, t);
2001         }
2002     }
2004     while (nl) {
2005        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
2006        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
2007        sp_nodepath_node_select(n, TRUE, FALSE);
2008        n_added ++;
2009        nl = g_list_remove(nl, t);
2010     }
2012     /** \todo fixme: adjust ? */
2013     sp_nodepath_update_handles(nodepath);
2015     if (n_added > 1) {
2016         sp_nodepath_update_repr(nodepath, _("Add nodes"));
2017     } else if (n_added > 0) {
2018         sp_nodepath_update_repr(nodepath, _("Add node"));
2019     }
2021     sp_nodepath_update_statusbar(nodepath);
2024 /**
2025  * Select segment nearest to point
2026  */
2027 void
2028 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p, bool toggle)
2030     if (!nodepath) {
2031         return;
2032     }
2034     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2035     Geom::PathVector const &pathv = curve->get_pathvector();
2036     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2037     if (!pvpos) {
2038         g_print ("Possible error?\n");
2039         return;
2040     }
2042     // calculate index for nodepath's representation.
2043     unsigned int segment_index = floor(pvpos->t) + 1;
2044     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2045         segment_index += pathv[i].size() + 1;
2046         if (pathv[i].closed()) {
2047             segment_index += 1;
2048         }
2049     }
2051     curve->unref();
2053     //find segment to segment
2054     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2056     //fixme: this can return NULL, so check before proceeding.
2057     g_return_if_fail(e != NULL);
2059     gboolean force = FALSE;
2060     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
2061         force = TRUE;
2062     }
2063     sp_nodepath_node_select(e, (gboolean) toggle, force);
2064     if (e->p.other)
2065         sp_nodepath_node_select(e->p.other, TRUE, force);
2067     sp_nodepath_update_handles(nodepath);
2069     sp_nodepath_update_statusbar(nodepath);
2072 /**
2073  * Add a node nearest to point
2074  */
2075 void
2076 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p)
2078     if (!nodepath) {
2079         return;
2080     }
2082     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2083     Geom::PathVector const &pathv = curve->get_pathvector();
2084     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2085     if (!pvpos) {
2086         g_print ("Possible error?\n");
2087         return;
2088     }
2090     // calculate index for nodepath's representation.
2091     double int_part;
2092     double t = std::modf(pvpos->t, &int_part);
2093     unsigned int segment_index = (unsigned int)int_part + 1;
2094     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2095         segment_index += pathv[i].size() + 1;
2096         if (pathv[i].closed()) {
2097             segment_index += 1;
2098         }
2099     }
2101     curve->unref();
2103     //find segment to split
2104     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2105     if (!e) {
2106         return;
2107     }
2109     //don't know why but t seems to flip for lines
2110     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2111         t = 1.0 - t;
2112     }
2114     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2115     sp_nodepath_node_select(n, FALSE, TRUE);
2117     /* fixme: adjust ? */
2118     sp_nodepath_update_handles(nodepath);
2120     sp_nodepath_update_repr(nodepath, _("Add node"));
2122     sp_nodepath_update_statusbar(nodepath);
2125 /*
2126  * Adjusts a segment so that t moves by a certain delta for dragging
2127  * converts lines to curves
2128  *
2129  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2130  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2131  */
2132 void
2133 sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, Geom::Point delta)
2135     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node);
2137     //fixme: e and e->p can be NULL, so check for those before proceeding
2138     g_return_if_fail(e != NULL);
2139     g_return_if_fail(&e->p != NULL);
2141     if (e->type == Inkscape::NodePath::NODE_AUTO) {
2142         e->type = Inkscape::NodePath::NODE_SMOOTH;
2143         sp_nodepath_update_node_knot (e);
2144     }
2145     if (e->p.other->type == Inkscape::NodePath::NODE_AUTO) {
2146         e->p.other->type = Inkscape::NodePath::NODE_SMOOTH;
2147         sp_nodepath_update_node_knot (e->p.other);
2148     }
2150     /* feel good is an arbitrary parameter that distributes the delta between handles
2151      * if t of the drag point is less than 1/6 distance form the endpoint only
2152      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2153      */
2154     double feel_good;
2155     if (t <= 1.0 / 6.0)
2156         feel_good = 0;
2157     else if (t <= 0.5)
2158         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2159     else if (t <= 5.0 / 6.0)
2160         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2161     else
2162         feel_good = 1;
2164     //if we're dragging a line convert it to a curve
2165     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2166         sp_nodepath_set_line_type(e, NR_CURVETO);
2167     }
2169     Geom::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2170     Geom::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2171     e->p.other->n.pos += offsetcoord0;
2172     e->p.pos += offsetcoord1;
2174     // adjust handles of adjacent nodes where necessary
2175     sp_node_adjust_handle(e,1);
2176     sp_node_adjust_handle(e->p.other,-1);
2178     sp_nodepath_update_handles(e->subpath->nodepath);
2180     update_object(e->subpath->nodepath);
2182     sp_nodepath_update_statusbar(e->subpath->nodepath);
2186 /**
2187  * Call sp_nodepath_break() for all selected segments.
2188  */
2189 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2191     if (!nodepath) return;
2193     GList *tempin = g_list_copy(nodepath->selected);
2194     GList *temp = NULL;
2195     for (GList *l = tempin; l != NULL; l = l->next) {
2196        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2197        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2198         if (nn == NULL) continue; // no break, no new node
2199         temp = g_list_prepend(temp, nn);
2200     }
2201     g_list_free(tempin);
2203     if (temp) {
2204         sp_nodepath_deselect(nodepath);
2205     }
2206     for (GList *l = temp; l != NULL; l = l->next) {
2207         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2208     }
2210     sp_nodepath_update_handles(nodepath);
2212     sp_nodepath_update_repr(nodepath, _("Break path"));
2215 /**
2216  * Duplicate the selected node(s).
2217  */
2218 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2220     if (!nodepath) {
2221         return;
2222     }
2224     GList *temp = NULL;
2225     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2226        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2227        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2228         if (nn == NULL) continue; // could not duplicate
2229         temp = g_list_prepend(temp, nn);
2230     }
2232     if (temp) {
2233         sp_nodepath_deselect(nodepath);
2234     }
2235     for (GList *l = temp; l != NULL; l = l->next) {
2236         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2237     }
2239     sp_nodepath_update_handles(nodepath);
2241     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2244 /**
2245  *  Internal function to join two nodes by merging them into one.
2246  */
2247 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2249     /* a and b are endpoints */
2251     // if one of the two nodes is mouseovered, fix its position
2252     Geom::Point c;
2253     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2254         c = a->pos;
2255     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2256         c = b->pos;
2257     } else {
2258         // otherwise, move joined node to the midpoint
2259         c = (a->pos + b->pos) / 2;
2260     }
2262     if (a->subpath == b->subpath) {
2263        Inkscape::NodePath::SubPath *sp = a->subpath;
2264         sp_nodepath_subpath_close(sp);
2265         sp_node_moveto (sp->first, c);
2267         sp_nodepath_update_handles(sp->nodepath);
2268         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2269         return;
2270     }
2272     /* a and b are separate subpaths */
2273     Inkscape::NodePath::SubPath *sa = a->subpath;
2274     Inkscape::NodePath::SubPath *sb = b->subpath;
2275     Geom::Point p;
2276     Inkscape::NodePath::Node *n;
2277     NRPathcode code;
2278     if (a == sa->first) {
2279         // we will now reverse sa, so that a is its last node, not first, and drop that node
2280         p = sa->first->n.pos;
2281         code = (NRPathcode)sa->first->n.other->code;
2282         // create new subpath
2283        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2284        // create a first moveto node on it
2285         n = sa->last;
2286         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2287         n = n->p.other;
2288         if (n == sa->first) n = NULL;
2289         while (n) {
2290             // copy the rest of the nodes from sa to t, going backwards
2291             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2292             n = n->p.other;
2293             if (n == sa->first) n = NULL;
2294         }
2295         // replace sa with t
2296         sp_nodepath_subpath_destroy(sa);
2297         sa = t;
2298     } else if (a == sa->last) {
2299         // a is already last, just drop it
2300         p = sa->last->p.pos;
2301         code = (NRPathcode)sa->last->code;
2302         sp_nodepath_node_destroy(sa->last);
2303     } else {
2304         code = NR_END;
2305         g_assert_not_reached();
2306     }
2308     if (b == sb->first) {
2309         // copy all nodes from b to a, forward
2310         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2311         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2312             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2313         }
2314     } else if (b == sb->last) {
2315         // copy all nodes from b to a, backward
2316         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2317         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2318             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2319         }
2320     } else {
2321         g_assert_not_reached();
2322     }
2323     /* and now destroy sb */
2325     sp_nodepath_subpath_destroy(sb);
2327     sp_nodepath_update_handles(sa->nodepath);
2329     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2331     sp_nodepath_update_statusbar(nodepath);
2334 /**
2335  *  Internal function to join two nodes by adding a segment between them.
2336  */
2337 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2339     if (a->subpath == b->subpath) {
2340        Inkscape::NodePath::SubPath *sp = a->subpath;
2342         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2343         sp->closed = TRUE;
2345         sp->first->p.other = sp->last;
2346         sp->last->n.other  = sp->first;
2348         sp_node_handle_mirror_p_to_n(sp->last);
2349         sp_node_handle_mirror_n_to_p(sp->first);
2351         sp->first->code = sp->last->code;
2352         sp->first       = sp->last;
2354         sp_nodepath_update_handles(sp->nodepath);
2356         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2358         return;
2359     }
2361     /* a and b are separate subpaths */
2362     Inkscape::NodePath::SubPath *sa = a->subpath;
2363     Inkscape::NodePath::SubPath *sb = b->subpath;
2365     Inkscape::NodePath::Node *n;
2366     Geom::Point p;
2367     NRPathcode code;
2368     if (a == sa->first) {
2369         code = (NRPathcode) sa->first->n.other->code;
2370        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2371         n = sa->last;
2372         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2373         for (n = n->p.other; n != NULL; n = n->p.other) {
2374             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2375         }
2376         sp_nodepath_subpath_destroy(sa);
2377         sa = t;
2378     } else if (a == sa->last) {
2379         code = (NRPathcode)sa->last->code;
2380     } else {
2381         code = NR_END;
2382         g_assert_not_reached();
2383     }
2385     if (b == sb->first) {
2386         n = sb->first;
2387         sp_node_handle_mirror_p_to_n(sa->last);
2388         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2389         sp_node_handle_mirror_n_to_p(sa->last);
2390         for (n = n->n.other; n != NULL; n = n->n.other) {
2391             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2392         }
2393     } else if (b == sb->last) {
2394         n = sb->last;
2395         sp_node_handle_mirror_p_to_n(sa->last);
2396         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2397         sp_node_handle_mirror_n_to_p(sa->last);
2398         for (n = n->p.other; n != NULL; n = n->p.other) {
2399             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2400         }
2401     } else {
2402         g_assert_not_reached();
2403     }
2404     /* and now destroy sb */
2406     sp_nodepath_subpath_destroy(sb);
2408     sp_nodepath_update_handles(sa->nodepath);
2410     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2413 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2415 /**
2416  * Internal function to handle joining two nodes.
2417  */
2418 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2420     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2422     if (g_list_length(nodepath->selected) != 2) {
2423         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2424         return;
2425     }
2427     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2428     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2430     g_assert(a != b);
2431     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2432         // someone tried to join an orphan node (i.e. a single-node subpath).
2433         // this is not worth an error message, just fail silently.
2434         return;
2435     }
2437     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2438         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2439         return;
2440     }
2442     switch(mode) {
2443         case NODE_JOIN_ENDPOINTS:
2444             do_node_selected_join(nodepath, a, b);
2445             break;
2446         case NODE_JOIN_SEGMENT:
2447             do_node_selected_join_segment(nodepath, a, b);
2448             break;
2449     }
2452 /**
2453  *  Join two nodes by merging them into one.
2454  */
2455 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2457     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2460 /**
2461  *  Join two nodes by adding a segment between them.
2462  */
2463 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2465     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2468 /**
2469  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2470  */
2471 void sp_node_delete_preserve(GList *nodes_to_delete)
2473     GSList *nodepaths = NULL;
2475     while (nodes_to_delete) {
2476         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2477         Inkscape::NodePath::SubPath *sp = node->subpath;
2478         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2479         Inkscape::NodePath::Node *sample_cursor = NULL;
2480         Inkscape::NodePath::Node *sample_end = NULL;
2481         Inkscape::NodePath::Node *delete_cursor = node;
2482         bool just_delete = false;
2484         //find the start of this contiguous selection
2485         //move left to the first node that is not selected
2486         //or the start of the non-closed path
2487         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2488             delete_cursor = curr;
2489         }
2491         //just delete at the beginning of an open path
2492         if (!delete_cursor->p.other) {
2493             sample_cursor = delete_cursor;
2494             just_delete = true;
2495         } else {
2496             sample_cursor = delete_cursor->p.other;
2497         }
2499         //calculate points for each segment
2500         int rate = 5;
2501         float period = 1.0 / rate;
2502         std::vector<Geom::Point> data;
2503         if (!just_delete) {
2504             data.push_back(sample_cursor->pos);
2505             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2506                 //just delete at the end of an open path
2507                 if (!sp->closed && curr == sp->last) {
2508                     just_delete = true;
2509                     break;
2510                 }
2512                 //sample points on the contiguous selected segment
2513                 Geom::Point *bez;
2514                 bez = new Geom::Point [4];
2515                 bez[0] = curr->pos;
2516                 bez[1] = curr->n.pos;
2517                 bez[2] = curr->n.other->p.pos;
2518                 bez[3] = curr->n.other->pos;
2519                 for (int i=1; i<rate; i++) {
2520                     gdouble t = i * period;
2521                     Geom::Point p = bezier_pt(3, bez, t);
2522                     data.push_back(p);
2523                 }
2524                 data.push_back(curr->n.other->pos);
2526                 sample_end = curr->n.other;
2527                 //break if we've come full circle or hit the end of the selection
2528                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2529                     break;
2530                 }
2531             }
2532         }
2534         if (!just_delete) {
2535             //calculate the best fitting single segment and adjust the endpoints
2536             Geom::Point *adata;
2537             adata = new Geom::Point [data.size()];
2538             copy(data.begin(), data.end(), adata);
2540             Geom::Point *bez;
2541             bez = new Geom::Point [4];
2542             //would decreasing error create a better fitting approximation?
2543             gdouble error = 1.0;
2544             gint ret;
2545             ret = Geom::bezier_fit_cubic (bez, adata, data.size(), error);
2547             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2548             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2549             //the resulting nodes behave as expected.
2550             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2551                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2552             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2553                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2555             //adjust endpoints
2556             sample_cursor->n.pos = bez[1];
2557             sample_end->p.pos = bez[2];
2558         }
2560         //destroy this contiguous selection
2561         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2562             Inkscape::NodePath::Node *temp = delete_cursor;
2563             if (delete_cursor->n.other == delete_cursor) {
2564                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2565                 delete_cursor = NULL;
2566             } else {
2567                 delete_cursor = delete_cursor->n.other;
2568             }
2569             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2570             sp_nodepath_node_destroy(temp);
2571         }
2573         sp_nodepath_update_handles(nodepath);
2575         if (!g_slist_find(nodepaths, nodepath))
2576             nodepaths = g_slist_prepend (nodepaths, nodepath);
2577     }
2579     for (GSList *i = nodepaths; i; i = i->next) {
2580         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2581         // different nodepaths will give us one undo event per nodepath
2582         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2584         // if the entire nodepath is removed, delete the selected object.
2585         if (nodepath->subpaths == NULL ||
2586             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2587             //at least 2
2588             sp_nodepath_get_node_count(nodepath) < 2) {
2589             SPDocument *document = sp_desktop_document (nodepath->desktop);
2590             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2591             //delete this nodepath's object, not the entire selection! (though at this time, this
2592             //does not matter)
2593             sp_selection_delete(nodepath->desktop);
2594             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2595                               _("Delete nodes"));
2596         } else {
2597             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2598             sp_nodepath_update_statusbar(nodepath);
2599         }
2600     }
2602     g_slist_free (nodepaths);
2605 /**
2606  * Delete one or more selected nodes.
2607  */
2608 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2610     if (!nodepath) return;
2611     if (!nodepath->selected) return;
2613     /** \todo fixme: do it the right way */
2614     while (nodepath->selected) {
2615        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2616         sp_nodepath_node_destroy(node);
2617     }
2620     //clean up the nodepath (such as for trivial subpaths)
2621     sp_nodepath_cleanup(nodepath);
2623     sp_nodepath_update_handles(nodepath);
2625     // if the entire nodepath is removed, delete the selected object.
2626     if (nodepath->subpaths == NULL ||
2627         sp_nodepath_get_node_count(nodepath) < 2) {
2628         SPDocument *document = sp_desktop_document (nodepath->desktop);
2629         sp_selection_delete(nodepath->desktop);
2630         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2631                           _("Delete nodes"));
2632         return;
2633     }
2635     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2637     sp_nodepath_update_statusbar(nodepath);
2640 /**
2641  * Delete one or more segments between two selected nodes.
2642  * This is the code for 'split'.
2643  */
2644 void
2645 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2647    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2648    Inkscape::NodePath::Node *curr, *next;     //Iterators
2650     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2652     if (g_list_length(nodepath->selected) != 2) {
2653         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2654                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2655         return;
2656     }
2658     //Selected nodes, not inclusive
2659    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2660    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2662     if ( ( a==b)                       ||  //same node
2663          (a->subpath  != b->subpath )  ||  //not the same path
2664          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2665          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2666     {
2667         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2668                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2669         return;
2670     }
2672     //###########################################
2673     //# BEGIN EDITS
2674     //###########################################
2675     //##################################
2676     //# CLOSED PATH
2677     //##################################
2678     if (a->subpath->closed) {
2681         gboolean reversed = FALSE;
2683         //Since we can go in a circle, we need to find the shorter distance.
2684         //  a->b or b->a
2685         start = end = NULL;
2686         int distance    = 0;
2687         int minDistance = 0;
2688         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2689             if (curr==b) {
2690                 //printf("a to b:%d\n", distance);
2691                 start = a;//go from a to b
2692                 end   = b;
2693                 minDistance = distance;
2694                 //printf("A to B :\n");
2695                 break;
2696             }
2697             distance++;
2698         }
2700         //try again, the other direction
2701         distance = 0;
2702         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2703             if (curr==a) {
2704                 //printf("b to a:%d\n", distance);
2705                 if (distance < minDistance) {
2706                     start    = b;  //we go from b to a
2707                     end      = a;
2708                     reversed = TRUE;
2709                     //printf("B to A\n");
2710                 }
2711                 break;
2712             }
2713             distance++;
2714         }
2717         //Copy everything from 'end' to 'start' to a new subpath
2718        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2719         for (curr=end ; curr ; curr=curr->n.other) {
2720             NRPathcode code = (NRPathcode) curr->code;
2721             if (curr == end)
2722                 code = NR_MOVETO;
2723             sp_nodepath_node_new(t, NULL,
2724                                  (Inkscape::NodePath::NodeType)curr->type, code,
2725                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2726             if (curr == start)
2727                 break;
2728         }
2729         sp_nodepath_subpath_destroy(a->subpath);
2732     }
2736     //##################################
2737     //# OPEN PATH
2738     //##################################
2739     else {
2741         //We need to get the direction of the list between A and B
2742         //Can we walk from a to b?
2743         start = end = NULL;
2744         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2745             if (curr==b) {
2746                 start = a;  //did it!  we go from a to b
2747                 end   = b;
2748                 //printf("A to B\n");
2749                 break;
2750             }
2751         }
2752         if (!start) {//didn't work?  let's try the other direction
2753             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2754                 if (curr==a) {
2755                     start = b;  //did it!  we go from b to a
2756                     end   = a;
2757                     //printf("B to A\n");
2758                     break;
2759                 }
2760             }
2761         }
2762         if (!start) {
2763             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2764                                                      _("Cannot find path between nodes."));
2765             return;
2766         }
2770         //Copy everything after 'end' to a new subpath
2771        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2772         for (curr=end ; curr ; curr=curr->n.other) {
2773             NRPathcode code = (NRPathcode) curr->code;
2774             if (curr == end)
2775                 code = NR_MOVETO;
2776             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2777                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2778         }
2780         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2781         for (curr = start->n.other ; curr  ; curr=next) {
2782             next = curr->n.other;
2783             sp_nodepath_node_destroy(curr);
2784         }
2786     }
2787     //###########################################
2788     //# END EDITS
2789     //###########################################
2791     //clean up the nodepath (such as for trivial subpaths)
2792     sp_nodepath_cleanup(nodepath);
2794     sp_nodepath_update_handles(nodepath);
2796     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2798     sp_nodepath_update_statusbar(nodepath);
2801 /**
2802  * Call sp_nodepath_set_line() for all selected segments.
2803  */
2804 void
2805 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2807     if (nodepath == NULL) return;
2809     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2810        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2811         g_assert(n->selected);
2812         if (n->p.other && n->p.other->selected) {
2813             sp_nodepath_set_line_type(n, code);
2814         }
2815     }
2817     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2820 /**
2821  * Call sp_nodepath_convert_node_type() for all selected nodes.
2822  */
2823 void
2824 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2826     if (nodepath == NULL) return;
2828     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2830     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2831         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2832     }
2834     sp_nodepath_update_repr(nodepath, _("Change node type"));
2837 /**
2838  * Change select status of node, update its own and neighbour handles.
2839  */
2840 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2842     node->selected = selected;
2844     if (selected) {
2845         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 11 : 9);
2846         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2847         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2848         sp_knot_update_ctrl(node->knot);
2849     } else {
2850         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 9 : 7);
2851         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2852         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2853         sp_knot_update_ctrl(node->knot);
2854     }
2856     sp_node_update_handles(node);
2857     if (node->n.other) sp_node_update_handles(node->n.other);
2858     if (node->p.other) sp_node_update_handles(node->p.other);
2861 /**
2862 \brief Select a node
2863 \param node     The node to select
2864 \param incremental   If true, add to selection, otherwise deselect others
2865 \param override   If true, always select this node, otherwise toggle selected status
2866 */
2867 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2869     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2871     if (incremental) {
2872         if (override) {
2873             if (!g_list_find(nodepath->selected, node)) {
2874                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2875             }
2876             sp_node_set_selected(node, TRUE);
2877         } else { // toggle
2878             if (node->selected) {
2879                 g_assert(g_list_find(nodepath->selected, node));
2880                 nodepath->selected = g_list_remove(nodepath->selected, node);
2881             } else {
2882                 g_assert(!g_list_find(nodepath->selected, node));
2883                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2884             }
2885             sp_node_set_selected(node, !node->selected);
2886         }
2887     } else {
2888         sp_nodepath_deselect(nodepath);
2889         nodepath->selected = g_list_prepend(nodepath->selected, node);
2890         sp_node_set_selected(node, TRUE);
2891     }
2893     sp_nodepath_update_statusbar(nodepath);
2897 /**
2898 \brief Deselect all nodes in the nodepath
2899 */
2900 void
2901 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2903     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2905     while (nodepath->selected) {
2906         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2907         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2908     }
2909     sp_nodepath_update_statusbar(nodepath);
2912 /**
2913 \brief Select or invert selection of all nodes in the nodepath
2914 */
2915 void
2916 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2918     if (!nodepath) return;
2920     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2921        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2922         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2923            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2924            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2925         }
2926     }
2929 /**
2930  * If nothing selected, does the same as sp_nodepath_select_all();
2931  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2932  * (i.e., similar to "select all in layer", with the "selected" subpaths
2933  * being treated as "layers" in the path).
2934  */
2935 void
2936 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2938     if (!nodepath) return;
2940     if (g_list_length (nodepath->selected) == 0) {
2941         sp_nodepath_select_all (nodepath, invert);
2942         return;
2943     }
2945     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2946     GSList *subpaths = NULL;
2948     for (GList *l = copy; l != NULL; l = l->next) {
2949         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2950         Inkscape::NodePath::SubPath *subpath = n->subpath;
2951         if (!g_slist_find (subpaths, subpath))
2952             subpaths = g_slist_prepend (subpaths, subpath);
2953     }
2955     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2956         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2957         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2958             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2959             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2960         }
2961     }
2963     g_slist_free (subpaths);
2964     g_list_free (copy);
2967 /**
2968  * \brief Select the node after the last selected; if none is selected,
2969  * select the first within path.
2970  */
2971 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2973     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2975    Inkscape::NodePath::Node *last = NULL;
2976     if (nodepath->selected) {
2977         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2978            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2979             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2980             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2981                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2982                 if (node->selected) {
2983                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2984                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2985                             if (spl->next) { // there's a next subpath
2986                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2987                                 last = subpath_next->first;
2988                             } else if (spl->prev) { // there's a previous subpath
2989                                 last = NULL; // to be set later to the first node of first subpath
2990                             } else {
2991                                 last = node->n.other;
2992                             }
2993                         } else {
2994                             last = node->n.other;
2995                         }
2996                     } else {
2997                         if (node->n.other) {
2998                             last = node->n.other;
2999                         } else {
3000                             if (spl->next) { // there's a next subpath
3001                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
3002                                 last = subpath_next->first;
3003                             } else if (spl->prev) { // there's a previous subpath
3004                                 last = NULL; // to be set later to the first node of first subpath
3005                             } else {
3006                                 last = (Inkscape::NodePath::Node *) subpath->first;
3007                             }
3008                         }
3009                     }
3010                 }
3011             }
3012         }
3013         sp_nodepath_deselect(nodepath);
3014     }
3016     if (last) { // there's at least one more node after selected
3017         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3018     } else { // no more nodes, select the first one in first subpath
3019        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
3020         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
3021     }
3024 /**
3025  * \brief Select the node before the first selected; if none is selected,
3026  * select the last within path
3027  */
3028 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
3030     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
3032    Inkscape::NodePath::Node *last = NULL;
3033     if (nodepath->selected) {
3034         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
3035            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3036             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
3037                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3038                 if (node->selected) {
3039                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
3040                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
3041                             if (spl->prev) { // there's a prev subpath
3042                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3043                                 last = subpath_prev->last;
3044                             } else if (spl->next) { // there's a next subpath
3045                                 last = NULL; // to be set later to the last node of last subpath
3046                             } else {
3047                                 last = node->p.other;
3048                             }
3049                         } else {
3050                             last = node->p.other;
3051                         }
3052                     } else {
3053                         if (node->p.other) {
3054                             last = node->p.other;
3055                         } else {
3056                             if (spl->prev) { // there's a prev subpath
3057                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3058                                 last = subpath_prev->last;
3059                             } else if (spl->next) { // there's a next subpath
3060                                 last = NULL; // to be set later to the last node of last subpath
3061                             } else {
3062                                 last = (Inkscape::NodePath::Node *) subpath->last;
3063                             }
3064                         }
3065                     }
3066                 }
3067             }
3068         }
3069         sp_nodepath_deselect(nodepath);
3070     }
3072     if (last) { // there's at least one more node before selected
3073         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3074     } else { // no more nodes, select the last one in last subpath
3075         GList *spl = g_list_last(nodepath->subpaths);
3076        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3077         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
3078     }
3081 /**
3082  * \brief Select all nodes that are within the rectangle.
3083  */
3084 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, Geom::Rect const &b, gboolean incremental)
3086     if (!incremental) {
3087         sp_nodepath_deselect(nodepath);
3088     }
3090     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3091        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3092         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3093            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3095             if (b.contains(node->pos)) {
3096                 sp_nodepath_node_select(node, TRUE, TRUE);
3097             }
3098         }
3099     }
3103 void
3104 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3106     g_assert (n);
3107     g_assert (nodepath);
3108     g_assert (n->subpath->nodepath == nodepath);
3110     if (g_list_length (nodepath->selected) == 0) {
3111         if (grow > 0) {
3112             sp_nodepath_node_select(n, TRUE, TRUE);
3113         }
3114         return;
3115     }
3117     if (g_list_length (nodepath->selected) == 1) {
3118         if (grow < 0) {
3119             sp_nodepath_deselect (nodepath);
3120             return;
3121         }
3122     }
3124         double n_sel_range = 0, p_sel_range = 0;
3125             Inkscape::NodePath::Node *farthest_n_node = n;
3126             Inkscape::NodePath::Node *farthest_p_node = n;
3128         // Calculate ranges
3129         {
3130             double n_range = 0, p_range = 0;
3131             bool n_going = true, p_going = true;
3132             Inkscape::NodePath::Node *n_node = n;
3133             Inkscape::NodePath::Node *p_node = n;
3134             do {
3135                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3136                 if (n_node && n_going)
3137                     n_node = n_node->n.other;
3138                 if (n_node == NULL) {
3139                     n_going = false;
3140                 } else {
3141                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3142                     if (n_node->selected) {
3143                         n_sel_range = n_range;
3144                         farthest_n_node = n_node;
3145                     }
3146                     if (n_node == p_node) {
3147                         n_going = false;
3148                         p_going = false;
3149                     }
3150                 }
3151                 if (p_node && p_going)
3152                     p_node = p_node->p.other;
3153                 if (p_node == NULL) {
3154                     p_going = false;
3155                 } else {
3156                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3157                     if (p_node->selected) {
3158                         p_sel_range = p_range;
3159                         farthest_p_node = p_node;
3160                     }
3161                     if (p_node == n_node) {
3162                         n_going = false;
3163                         p_going = false;
3164                     }
3165                 }
3166             } while (n_going || p_going);
3167         }
3169     if (grow > 0) {
3170         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3171                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3172         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3173                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3174         }
3175     } else {
3176         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3177                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3178         } else if (farthest_p_node && farthest_p_node->selected) {
3179                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3180         }
3181     }
3184 void
3185 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3187     g_assert (n);
3188     g_assert (nodepath);
3189     g_assert (n->subpath->nodepath == nodepath);
3191     if (g_list_length (nodepath->selected) == 0) {
3192         if (grow > 0) {
3193             sp_nodepath_node_select(n, TRUE, TRUE);
3194         }
3195         return;
3196     }
3198     if (g_list_length (nodepath->selected) == 1) {
3199         if (grow < 0) {
3200             sp_nodepath_deselect (nodepath);
3201             return;
3202         }
3203     }
3205     Inkscape::NodePath::Node *farthest_selected = NULL;
3206     double farthest_dist = 0;
3208     Inkscape::NodePath::Node *closest_unselected = NULL;
3209     double closest_dist = NR_HUGE;
3211     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3212        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3213         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3214            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3215            if (node == n)
3216                continue;
3217            if (node->selected) {
3218                if (Geom::L2(node->pos - n->pos) > farthest_dist) {
3219                    farthest_dist = Geom::L2(node->pos - n->pos);
3220                    farthest_selected = node;
3221                }
3222            } else {
3223                if (Geom::L2(node->pos - n->pos) < closest_dist) {
3224                    closest_dist = Geom::L2(node->pos - n->pos);
3225                    closest_unselected = node;
3226                }
3227            }
3228         }
3229     }
3231     if (grow > 0) {
3232         if (closest_unselected) {
3233             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3234         }
3235     } else {
3236         if (farthest_selected) {
3237             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3238         }
3239     }
3243 /**
3244 \brief  Saves all nodes' and handles' current positions in their origin members
3245 */
3246 void
3247 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3249     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3250        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3251         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3252            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3253            n->origin = n->pos;
3254            n->p.origin = n->p.pos;
3255            n->n.origin = n->n.pos;
3256         }
3257     }
3260 /**
3261 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3262 */
3263 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3265     GList *r = NULL;
3266     if (nodepath->selected) {
3267         guint i = 0;
3268         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3269             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3270             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3271                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3272                 i++;
3273                 if (node->selected) {
3274                     r = g_list_append(r, GINT_TO_POINTER(i));
3275                 }
3276             }
3277         }
3278     }
3279     return r;
3282 /**
3283 \brief  Restores selection by selecting nodes whose positions are in the list
3284 */
3285 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3287     sp_nodepath_deselect(nodepath);
3289     guint i = 0;
3290     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3291        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3292         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3293            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3294             i++;
3295             if (g_list_find(r, GINT_TO_POINTER(i))) {
3296                 sp_nodepath_node_select(node, TRUE, TRUE);
3297             }
3298         }
3299     }
3303 /**
3304 \brief Adjusts handle according to node type and line code.
3305 */
3306 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3308     g_assert(node);
3310     // nothing to do for auto nodes (sp_node_adjust_handles() does the job)
3311     if (node->type == Inkscape::NodePath::NODE_AUTO)
3312         return;
3314    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3315    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3317    // nothing to do if we are an end node
3318     if (me->other == NULL) return;
3319     if (other->other == NULL) return;
3321     // nothing to do if we are a cusp node
3322     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3324     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3325     NRPathcode mecode;
3326     if (which_adjust == 1) {
3327         mecode = (NRPathcode)me->other->code;
3328     } else {
3329         mecode = (NRPathcode)node->code;
3330     }
3331     if (mecode == NR_LINETO) return;
3333     if (sp_node_side_is_line(node, other)) {
3334         // other is a line, and we are either smooth or symm
3335        Inkscape::NodePath::Node *othernode = other->other;
3336         double len = Geom::L2(me->pos - node->pos);
3337         Geom::Point delta = node->pos - othernode->pos;
3338         double linelen = Geom::L2(delta);
3339         if (linelen < 1e-18)
3340             return;
3341         me->pos = node->pos + (len / linelen)*delta;
3342         return;
3343     }
3345     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3346         // symmetrize
3347         me->pos = 2 * node->pos - other->pos;
3348         return;
3349     } else {
3350         // smoothify
3351         double len = Geom::L2(me->pos - node->pos);
3352         Geom::Point delta = other->pos - node->pos;
3353         double otherlen = Geom::L2(delta);
3354         if (otherlen < 1e-18) return;
3355         me->pos = node->pos - (len / otherlen) * delta;
3356     }
3359 /**
3360  \brief Adjusts both handles according to node type and line code
3361  */
3362 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3364     g_assert(node);
3366     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3368     /* we are either smooth or symm */
3370     if (node->p.other == NULL) return;
3371     if (node->n.other == NULL) return;
3373     if (node->type == Inkscape::NodePath::NODE_AUTO) {
3374         sp_node_adjust_handles_auto(node);
3375         return;
3376     }
3378     if (sp_node_side_is_line(node, &node->p)) {
3379         sp_node_adjust_handle(node, 1);
3380         return;
3381     }
3383     if (sp_node_side_is_line(node, &node->n)) {
3384         sp_node_adjust_handle(node, -1);
3385         return;
3386     }
3388     /* both are curves */
3389     Geom::Point const delta( node->n.pos - node->p.pos );
3391     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3392         node->p.pos = node->pos - delta / 2;
3393         node->n.pos = node->pos + delta / 2;
3394         return;
3395     }
3397     /* We are smooth */
3398     double plen = Geom::L2(node->p.pos - node->pos);
3399     if (plen < 1e-18) return;
3400     double nlen = Geom::L2(node->n.pos - node->pos);
3401     if (nlen < 1e-18) return;
3402     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3403     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3406 static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node)
3408     if (node->p.other == NULL || node->n.other == NULL) {
3409         node->p.pos = node->pos;
3410         node->n.pos = node->pos;
3411         return;
3412     }
3414     Geom::Point leg_prev = to_2geom(node->p.other->pos - node->pos);
3415     Geom::Point leg_next = to_2geom(node->n.other->pos - node->pos);
3417     double norm_leg_prev = Geom::L2(leg_prev);
3418     double norm_leg_next = Geom::L2(leg_next);
3420     Geom::Point delta;
3421     if (norm_leg_next > 0.0) {
3422         delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
3423         delta.normalize();
3424     }
3426     node->p.pos = node->pos - norm_leg_prev / 3 * delta;
3427     node->n.pos = node->pos + norm_leg_next / 3 * delta;
3430 /**
3431  * Node event callback.
3432  */
3433 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3435     gboolean ret = FALSE;
3436     switch (event->type) {
3437         case GDK_ENTER_NOTIFY:
3438             Inkscape::NodePath::Path::active_node = n;
3439             break;
3440         case GDK_LEAVE_NOTIFY:
3441             Inkscape::NodePath::Path::active_node = NULL;
3442             break;
3443         case GDK_SCROLL:
3444             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3445                 switch (event->scroll.direction) {
3446                     case GDK_SCROLL_UP:
3447                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3448                         break;
3449                     case GDK_SCROLL_DOWN:
3450                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3451                         break;
3452                     default:
3453                         break;
3454                 }
3455                 ret = TRUE;
3456             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3457                 switch (event->scroll.direction) {
3458                     case GDK_SCROLL_UP:
3459                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3460                         break;
3461                     case GDK_SCROLL_DOWN:
3462                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3463                         break;
3464                     default:
3465                         break;
3466                 }
3467                 ret = TRUE;
3468             }
3469             break;
3470         case GDK_KEY_PRESS:
3471             switch (get_group0_keyval (&event->key)) {
3472                 case GDK_space:
3473                     if (event->key.state & GDK_BUTTON1_MASK) {
3474                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3475                         stamp_repr(nodepath);
3476                         ret = TRUE;
3477                     }
3478                     break;
3479                 case GDK_Page_Up:
3480                     if (event->key.state & GDK_CONTROL_MASK) {
3481                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3482                     } else {
3483                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3484                     }
3485                     break;
3486                 case GDK_Page_Down:
3487                     if (event->key.state & GDK_CONTROL_MASK) {
3488                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3489                     } else {
3490                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3491                     }
3492                     break;
3493                 default:
3494                     break;
3495             }
3496             break;
3497         default:
3498             break;
3499     }
3501     return ret;
3504 /**
3505  * Handle keypress on node; directly called.
3506  */
3507 gboolean node_key(GdkEvent *event)
3509     Inkscape::NodePath::Path *np;
3511     // there is no way to verify nodes so set active_node to nil when deleting!!
3512     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3514     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3515         gint ret = FALSE;
3516         switch (get_group0_keyval (&event->key)) {
3517             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3518             case GDK_BackSpace:
3519                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3520                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3521                 sp_nodepath_update_repr(np, _("Delete node"));
3522                 Inkscape::NodePath::Path::active_node = NULL;
3523                 ret = TRUE;
3524                 break;
3525             case GDK_c:
3526                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3527                 ret = TRUE;
3528                 break;
3529             case GDK_s:
3530                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3531                 ret = TRUE;
3532                 break;
3533             case GDK_a:
3534                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_AUTO);
3535                 ret = TRUE;
3536                 break;
3537             case GDK_y:
3538                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3539                 ret = TRUE;
3540                 break;
3541             case GDK_b:
3542                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3543                 ret = TRUE;
3544                 break;
3545         }
3546         return ret;
3547     }
3548     return FALSE;
3551 /**
3552  * Mouseclick on node callback.
3553  */
3554 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3556    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3558     if (state & GDK_CONTROL_MASK) {
3559         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3561         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3562             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3563                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3564             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3565                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3566             } else if (n->type == Inkscape::NodePath::NODE_SYMM) {
3567                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_AUTO);
3568             } else {
3569                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3570             }
3571             sp_nodepath_update_repr(nodepath, _("Change node type"));
3572             sp_nodepath_update_statusbar(nodepath);
3574         } else { //ctrl+alt+click: delete node
3575             GList *node_to_delete = NULL;
3576             node_to_delete = g_list_append(node_to_delete, n);
3577             sp_node_delete_preserve(node_to_delete);
3578         }
3580     } else {
3581         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3582     }
3585 /**
3586  * Mouse grabbed node callback.
3587  */
3588 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3590    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3592     if (!n->selected) {
3593         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3594     }
3596     n->is_dragging = 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    n->subpath->nodepath->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
3615    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3617    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3620 /**
3621  * The point on a line, given by its angle, closest to the given point.
3622  * \param p  A point.
3623  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3624  * \param closest  Pointer to the point struct where the result is stored.
3625  * \todo FIXME: use dot product perhaps?
3626  */
3627 static void point_line_closest(Geom::Point *p, double a, Geom::Point *closest)
3629     if (a == HUGE_VAL) { // vertical
3630         *closest = Geom::Point(0, (*p)[Geom::Y]);
3631     } else {
3632         (*closest)[Geom::X] = ( a * (*p)[Geom::Y] + (*p)[Geom::X]) / (a*a + 1);
3633         (*closest)[Geom::Y] = a * (*closest)[Geom::X];
3634     }
3637 /**
3638  * Distance from the point to a line given by its angle.
3639  * \param p  A point.
3640  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3641  */
3642 static double point_line_distance(Geom::Point *p, double a)
3644     Geom::Point c;
3645     point_line_closest(p, a, &c);
3646     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]));
3649 /**
3650  * Callback for node "request" signal.
3651  * \todo fixme: This goes to "moved" event? (lauris)
3652  */
3653 static gboolean
3654 node_request(SPKnot */*knot*/, Geom::Point const &p, guint state, gpointer data)
3656     double yn, xn, yp, xp;
3657     double an, ap, na, pa;
3658     double d_an, d_ap, d_na, d_pa;
3659     gboolean collinear = FALSE;
3660     Geom::Point c;
3661     Geom::Point pr;
3663     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3665     n->subpath->nodepath->desktop->snapindicator->remove_snaptarget();
3667     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3668     if ( (!n->subpath->nodepath->straight_path) &&
3669          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3670            || n->dragging_out ) )
3671     {
3672        Geom::Point mouse = p;
3674        if (!n->dragging_out) {
3675            // This is the first drag-out event; find out which handle to drag out
3676            double appr_n = (n->n.other ? Geom::L2(n->n.other->pos - n->pos) - Geom::L2(n->n.other->pos - p) : -HUGE_VAL);
3677            double appr_p = (n->p.other ? Geom::L2(n->p.other->pos - n->pos) - Geom::L2(n->p.other->pos - p) : -HUGE_VAL);
3679            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3680                return FALSE;
3682            Inkscape::NodePath::NodeSide *opposite;
3683            if (appr_p > appr_n) { // closer to p
3684                n->dragging_out = &n->p;
3685                opposite = &n->n;
3686                n->code = NR_CURVETO;
3687            } else if (appr_p < appr_n) { // closer to n
3688                n->dragging_out = &n->n;
3689                opposite = &n->p;
3690                n->n.other->code = NR_CURVETO;
3691            } else { // p and n nodes are the same
3692                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3693                    n->dragging_out = &n->p;
3694                    opposite = &n->n;
3695                    n->code = NR_CURVETO;
3696                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3697                    n->dragging_out = &n->n;
3698                    opposite = &n->p;
3699                    n->n.other->code = NR_CURVETO;
3700                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3701                    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);
3702                    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);
3703                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3704                        n->dragging_out = &n->n;
3705                        opposite = &n->p;
3706                        n->n.other->code = NR_CURVETO;
3707                    } else { // closer to other's n handle
3708                        n->dragging_out = &n->p;
3709                        opposite = &n->n;
3710                        n->code = NR_CURVETO;
3711                    }
3712                }
3713            }
3715            // if there's another handle, make sure the one we drag out starts parallel to it
3716            if (opposite->pos != n->pos) {
3717                mouse = n->pos - Geom::L2(mouse - n->pos) * Geom::unit_vector(opposite->pos - n->pos);
3718            }
3720            // knots might not be created yet!
3721            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3722            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3723        }
3725        // pass this on to the handle-moved callback
3726        node_handle_moved(n->dragging_out->knot, mouse, state, (gpointer) n);
3727        sp_node_update_handles(n);
3728        return TRUE;
3729    }
3731     if (state & GDK_CONTROL_MASK) { // constrained motion
3733         // calculate relative distances of handles
3734         // n handle:
3735         yn = n->n.pos[Geom::Y] - n->pos[Geom::Y];
3736         xn = n->n.pos[Geom::X] - n->pos[Geom::X];
3737         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3738         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3739             if (n->n.other) { // if there is the next point
3740                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3741                     yn = n->n.other->origin[Geom::Y] - n->origin[Geom::Y]; // use origin because otherwise the direction will change as you drag
3742                     xn = n->n.other->origin[Geom::X] - n->origin[Geom::X];
3743             }
3744         }
3745         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3746         if (yn < 0) { xn = -xn; yn = -yn; }
3748         // p handle:
3749         yp = n->p.pos[Geom::Y] - n->pos[Geom::Y];
3750         xp = n->p.pos[Geom::X] - n->pos[Geom::X];
3751         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3752         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3753             if (n->p.other) {
3754                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3755                     yp = n->p.other->origin[Geom::Y] - n->origin[Geom::Y];
3756                     xp = n->p.other->origin[Geom::X] - n->origin[Geom::X];
3757             }
3758         }
3759         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3760         if (yp < 0) { xp = -xp; yp = -yp; }
3762         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3763             // sliding on handles, only if at least one of the handles is non-vertical
3764             // (otherwise it's the same as ctrl+drag anyway)
3766             // calculate angles of the handles
3767             if (xn == 0) {
3768                 if (yn == 0) { // no handle, consider it the continuation of the other one
3769                     an = 0;
3770                     collinear = TRUE;
3771                 }
3772                 else an = 0; // vertical; set the angle to horizontal
3773             } else an = yn/xn;
3775             if (xp == 0) {
3776                 if (yp == 0) { // no handle, consider it the continuation of the other one
3777                     ap = an;
3778                 }
3779                 else ap = 0; // vertical; set the angle to horizontal
3780             } else  ap = yp/xp;
3782             if (collinear) an = ap;
3784             // angles of the perpendiculars; HUGE_VAL means vertical
3785             if (an == 0) na = HUGE_VAL; else na = -1/an;
3786             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3788             // mouse point relative to the node's original pos
3789             pr = p - n->origin;
3791             // distances to the four lines (two handles and two perpendiculars)
3792             d_an = point_line_distance(&pr, an);
3793             d_na = point_line_distance(&pr, na);
3794             d_ap = point_line_distance(&pr, ap);
3795             d_pa = point_line_distance(&pr, pa);
3797             // find out which line is the closest, save its closest point in c
3798             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3799                 point_line_closest(&pr, an, &c);
3800             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3801                 point_line_closest(&pr, ap, &c);
3802             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3803                 point_line_closest(&pr, na, &c);
3804             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3805                 point_line_closest(&pr, pa, &c);
3806             }
3808             // move the node to the closest point
3809             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3810                                             n->origin[Geom::X] + c[Geom::X] - n->pos[Geom::X],
3811                                             n->origin[Geom::Y] + c[Geom::Y] - n->pos[Geom::Y],
3812                                             true);
3814         } else {  // constraining to hor/vert
3816             if (fabs(p[Geom::X] - n->origin[Geom::X]) > fabs(p[Geom::Y] - n->origin[Geom::Y])) { // snap to hor
3817                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3818                                                 p[Geom::X] - n->pos[Geom::X],
3819                                                 n->origin[Geom::Y] - n->pos[Geom::Y],
3820                                                 true,
3821                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::X]));
3822             } else { // snap to vert
3823                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3824                                                 n->origin[Geom::X] - n->pos[Geom::X],
3825                                                 p[Geom::Y] - n->pos[Geom::Y],
3826                                                 true,
3827                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::Y]));
3828             }
3829         }
3830     } else { // move freely
3831         if (n->is_dragging) {
3832             if (state & GDK_MOD1_MASK) { // sculpt
3833                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, p - n->origin);
3834             } else {
3835                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3836                                             p[Geom::X] - n->pos[Geom::X],
3837                                             p[Geom::Y] - n->pos[Geom::Y],
3838                                             (state & GDK_SHIFT_MASK) == 0);
3839             }
3840         }
3841     }
3843     n->subpath->nodepath->desktop->scroll_to_point(p);
3845     return TRUE;
3848 /**
3849  * Node handle clicked callback.
3850  */
3851 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3853    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3855     if (state & GDK_CONTROL_MASK) { // "delete" handle
3856         if (n->p.knot == knot) {
3857             n->p.pos = n->pos;
3858         } else if (n->n.knot == knot) {
3859             n->n.pos = n->pos;
3860         }
3861         sp_node_update_handles(n);
3862         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3863         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3864         sp_nodepath_update_statusbar(nodepath);
3866     } else { // just select or add to selection, depending in Shift
3867         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3868     }
3871 /**
3872  * Node handle grabbed callback.
3873  */
3874 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3876    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3878     // convert auto -> smooth when dragging handle
3879    if (n->type == Inkscape::NodePath::NODE_AUTO) {
3880         n->type = Inkscape::NodePath::NODE_SMOOTH;
3881         sp_nodepath_update_node_knot (n);
3882    }
3884     if (!n->selected) {
3885         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3886     }
3888     // remember the origin point of the handle
3889     if (n->p.knot == knot) {
3890         n->p.origin_radial = n->p.pos - n->pos;
3891     } else if (n->n.knot == knot) {
3892         n->n.origin_radial = n->n.pos - n->pos;
3893     } else {
3894         g_assert_not_reached();
3895     }
3897     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3900 /**
3901  * Node handle ungrabbed callback.
3902  */
3903 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3905    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3907     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3908     if (n->p.knot == knot) {
3909         n->p.origin_radial.a = 0;
3910         sp_knot_set_position(knot, n->p.pos, state);
3911     } else if (n->n.knot == knot) {
3912         n->n.origin_radial.a = 0;
3913         sp_knot_set_position(knot, n->n.pos, state);
3914     } else {
3915         g_assert_not_reached();
3916     }
3918     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3921 /**
3922  * Node handle "request" signal callback.
3923  */
3924 static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, gpointer data)
3926     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3928     Inkscape::NodePath::NodeSide *me, *opposite;
3929     gint which;
3930     if (n->p.knot == knot) {
3931         me = &n->p;
3932         opposite = &n->n;
3933         which = -1;
3934     } else if (n->n.knot == knot) {
3935         me = &n->n;
3936         opposite = &n->p;
3937         which = 1;
3938     } else {
3939         me = opposite = NULL;
3940         which = 0;
3941         g_assert_not_reached();
3942     }
3944     SPDesktop *desktop = n->subpath->nodepath->desktop;
3945     SnapManager &m = desktop->namedview->snap_manager;
3946     m.setup(desktop, true, n->subpath->nodepath->item);
3947     Inkscape::SnappedPoint s;
3949     if ((state & GDK_SHIFT_MASK) != 0) {
3950         // We will not try to snap when the shift-key is pressed
3951         // so remove the old snap indicator and don't wait for it to time-out
3952         desktop->snapindicator->remove_snaptarget();
3953     }
3955     Inkscape::NodePath::Node *othernode = opposite->other;
3956     Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP);
3957     if (othernode) {
3958         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3959             /* We are smooth node adjacent with line */
3960             Geom::Point const delta = p - n->pos;
3961             Geom::Coord const len = Geom::L2(delta);
3962             Inkscape::NodePath::Node *othernode = opposite->other;
3963             Geom::Point const ndelta = n->pos - othernode->pos;
3964             Geom::Coord const linelen = Geom::L2(ndelta);
3965             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3966                 Geom::Coord const scal = dot(delta, ndelta) / linelen;
3967                 p = n->pos + (scal / linelen) * ndelta;
3968             }
3969             if ((state & GDK_SHIFT_MASK) == 0) {
3970                 s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type, Inkscape::Snapper::ConstraintLine(p, ndelta), false);
3971             }
3972         } else {
3973             if ((state & GDK_SHIFT_MASK) == 0) {
3974                 s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type);
3975             }
3976         }
3977     } else {
3978         if ((state & GDK_SHIFT_MASK) == 0) {
3979             s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type);
3980         }
3981     }
3983     s.getPoint(p);
3985     sp_node_adjust_handle(n, -which);
3987     return FALSE;
3990 /**
3991  * Node handle moved callback.
3992  */
3993 static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data)
3995    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3996    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3998    Inkscape::NodePath::NodeSide *me;
3999    Inkscape::NodePath::NodeSide *other;
4000     if (n->p.knot == knot) {
4001         me = &n->p;
4002         other = &n->n;
4003     } else if (n->n.knot == knot) {
4004         me = &n->n;
4005         other = &n->p;
4006     } else {
4007         me = NULL;
4008         other = NULL;
4009         g_assert_not_reached();
4010     }
4012     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
4013     Radial rme(me->pos - n->pos);
4014     Radial rother(other->pos - n->pos);
4015     Radial rnew(p - n->pos);
4017     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
4018         int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
4019         /* 0 interpreted as "no snapping". */
4021         // 1. Snap to the closest PI/snaps angle, starting from zero.
4022         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
4024         // 2. Snap to the original angle, its opposite and perpendiculars
4025         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
4026             /* The closest PI/2 angle, starting from original angle */
4027             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
4029             // Snap to the closest.
4030             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
4031                        ? a_snapped
4032                        : a_ortho );
4033         }
4035         // 3. Snap to the angle of the opposite line, if any
4036         Inkscape::NodePath::Node *othernode = other->other;
4037         if (othernode) {
4038             Geom::Point other_to_snap(0,0);
4039             if (sp_node_side_is_line(n, other)) {
4040                 other_to_snap = othernode->pos - n->pos;
4041             } else {
4042                 other_to_snap = other->pos - n->pos;
4043             }
4044             if (Geom::L2(other_to_snap) > 1e-3) {
4045                 Radial rother_to_snap(other_to_snap);
4046                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
4047                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
4049                 // Snap to the closest.
4050                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
4051                        ? a_snapped
4052                        : a_oppo );
4053             }
4054         }
4056         rnew.a = a_snapped;
4057     }
4059     if (state & GDK_MOD1_MASK) {
4060         // lock handle length
4061         rnew.r = me->origin_radial.r;
4062     }
4064     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (!n->dragging_out && (state & GDK_SHIFT_MASK)))
4065         && (rme.a != HUGE_VAL) && (rnew.a != HUGE_VAL) && ((fabs(rme.a - rnew.a) > 0.001) || (n->type ==Inkscape::NodePath::NODE_SYMM))) {
4066         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
4067         rother.a += rnew.a - rme.a;
4068         other->pos = Geom::Point(rother) + n->pos;
4069         if (other->knot) {
4070             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
4071             sp_knot_moveto(other->knot, other->pos);
4072         }
4073     }
4075     me->pos = Geom::Point(rnew) + n->pos;
4076     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
4078     // move knot, but without emitting the signal:
4079     // we cannot emit a "moved" signal because we're now processing it
4080     sp_knot_moveto(me->knot, me->pos);
4082     update_object(n->subpath->nodepath);
4084     /* status text */
4085     SPDesktop *desktop = n->subpath->nodepath->desktop;
4086     if (!desktop) return;
4087     SPEventContext *ec = desktop->event_context;
4088     if (!ec) return;
4090     Inkscape::MessageContext *mc = get_message_context(ec);
4092     if (!mc) return;
4094     double degrees = 180 / M_PI * rnew.a;
4095     if (degrees > 180) degrees -= 360;
4096     if (degrees < -180) degrees += 360;
4097     if (prefs->getBool("/options/compassangledisplay/value"))
4098         degrees = angle_to_compass (degrees);
4100     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
4102     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
4103          _("<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);
4105     g_string_free(length, TRUE);
4108 /**
4109  * Node handle event callback.
4110  */
4111 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
4113     gboolean ret = FALSE;
4114     switch (event->type) {
4115         case GDK_KEY_PRESS:
4116             switch (get_group0_keyval (&event->key)) {
4117                 case GDK_space:
4118                     if (event->key.state & GDK_BUTTON1_MASK) {
4119                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
4120                         stamp_repr(nodepath);
4121                         ret = TRUE;
4122                     }
4123                     break;
4124                 default:
4125                     break;
4126             }
4127             break;
4128         case GDK_ENTER_NOTIFY:
4129             // we use an experimentally determined threshold that seems to work fine
4130             if (Geom::L2(n->pos - knot->pos) < 0.75)
4131                 Inkscape::NodePath::Path::active_node = n;
4132             break;
4133         case GDK_LEAVE_NOTIFY:
4134             // we use an experimentally determined threshold that seems to work fine
4135             if (Geom::L2(n->pos - knot->pos) < 0.75)
4136                 Inkscape::NodePath::Path::active_node = NULL;
4137             break;
4138         default:
4139             break;
4140     }
4142     return ret;
4145 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
4146                                  Radial &rme, Radial &rother, gboolean const both)
4148     rme.a += angle;
4149     if ( both
4150          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4151          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4152     {
4153         rother.a += angle;
4154     }
4157 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
4158                                         Radial &rme, Radial &rother, gboolean const both)
4160     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
4162     gdouble r;
4163     if ( both
4164          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4165          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4166     {
4167         r = MAX(rme.r, rother.r);
4168     } else {
4169         r = rme.r;
4170     }
4172     gdouble const weird_angle = atan2(norm_angle, r);
4173 /* Bulia says norm_angle is just the visible distance that the
4174  * object's end must travel on the screen.  Left as 'angle' for want of
4175  * a better name.*/
4177     rme.a += weird_angle;
4178     if ( both
4179          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4180          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4181     {
4182         rother.a += weird_angle;
4183     }
4186 /**
4187  * Rotate one node.
4188  */
4189 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4191     Inkscape::NodePath::NodeSide *me, *other;
4192     bool both = false;
4194     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4195     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4197     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4198         me = &(n->p);
4199         other = &(n->n);
4200     } else if (!n->p.other) {
4201         me = &(n->n);
4202         other = &(n->p);
4203     } else {
4204         if (which > 0) { // right handle
4205             if (xn > xp) {
4206                 me = &(n->n);
4207                 other = &(n->p);
4208             } else {
4209                 me = &(n->p);
4210                 other = &(n->n);
4211             }
4212         } else if (which < 0){ // left handle
4213             if (xn <= xp) {
4214                 me = &(n->n);
4215                 other = &(n->p);
4216             } else {
4217                 me = &(n->p);
4218                 other = &(n->n);
4219             }
4220         } else { // both handles
4221             me = &(n->n);
4222             other = &(n->p);
4223             both = true;
4224         }
4225     }
4227     Radial rme(me->pos - n->pos);
4228     Radial rother(other->pos - n->pos);
4230     if (screen) {
4231         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4232     } else {
4233         node_rotate_one_internal (*n, angle, rme, rother, both);
4234     }
4236     me->pos = n->pos + Geom::Point(rme);
4238     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4239         other->pos =  n->pos + Geom::Point(rother);
4240     }
4242     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4243     // so here we just move all the knots without emitting move signals, for speed
4244     sp_node_update_handles(n, false);
4247 /**
4248  * Rotate selected nodes.
4249  */
4250 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4252     if (!nodepath || !nodepath->selected) return;
4254     if (g_list_length(nodepath->selected) == 1) {
4255        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4256         node_rotate_one (n, angle, which, screen);
4257     } else {
4258        // rotate as an object:
4260         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4261         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4262         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4263             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4264             box.expandTo (n->pos); // contain all selected nodes
4265         }
4267         gdouble rot;
4268         if (screen) {
4269             gdouble const zoom = nodepath->desktop->current_zoom();
4270             gdouble const zmove = angle / zoom;
4271             gdouble const r = Geom::L2(box.max() - box.midpoint());
4272             rot = atan2(zmove, r);
4273         } else {
4274             rot = angle;
4275         }
4277         Geom::Point rot_center;
4278         if (Inkscape::NodePath::Path::active_node == NULL)
4279             rot_center = box.midpoint();
4280         else
4281             rot_center = Inkscape::NodePath::Path::active_node->pos;
4283         Geom::Matrix t =
4284             Geom::Matrix (Geom::Translate(-rot_center)) *
4285             Geom::Matrix (Geom::Rotate(rot)) *
4286             Geom::Matrix (Geom::Translate(rot_center));
4288         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4289             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4290             n->pos *= t;
4291             n->n.pos *= t;
4292             n->p.pos *= t;
4293             sp_node_update_handles(n, false);
4294         }
4295     }
4297     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4300 /**
4301  * Scale one node.
4302  */
4303 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4305     bool both = false;
4306     Inkscape::NodePath::NodeSide *me, *other;
4308     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4309     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4311     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4312         me = &(n->p);
4313         other = &(n->n);
4314         n->code = NR_CURVETO;
4315     } else if (!n->p.other) {
4316         me = &(n->n);
4317         other = &(n->p);
4318         if (n->n.other)
4319             n->n.other->code = NR_CURVETO;
4320     } else {
4321         if (which > 0) { // right handle
4322             if (xn > xp) {
4323                 me = &(n->n);
4324                 other = &(n->p);
4325                 if (n->n.other)
4326                     n->n.other->code = NR_CURVETO;
4327             } else {
4328                 me = &(n->p);
4329                 other = &(n->n);
4330                 n->code = NR_CURVETO;
4331             }
4332         } else if (which < 0){ // left handle
4333             if (xn <= xp) {
4334                 me = &(n->n);
4335                 other = &(n->p);
4336                 if (n->n.other)
4337                     n->n.other->code = NR_CURVETO;
4338             } else {
4339                 me = &(n->p);
4340                 other = &(n->n);
4341                 n->code = NR_CURVETO;
4342             }
4343         } else { // both handles
4344             me = &(n->n);
4345             other = &(n->p);
4346             both = true;
4347             n->code = NR_CURVETO;
4348             if (n->n.other)
4349                 n->n.other->code = NR_CURVETO;
4350         }
4351     }
4353     Radial rme(me->pos - n->pos);
4354     Radial rother(other->pos - n->pos);
4356     rme.r += grow;
4357     if (rme.r < 0) rme.r = 0;
4358     if (rme.a == HUGE_VAL) {
4359         if (me->other) { // if direction is unknown, initialize it towards the next node
4360             Radial rme_next(me->other->pos - n->pos);
4361             rme.a = rme_next.a;
4362         } else { // if there's no next, initialize to 0
4363             rme.a = 0;
4364         }
4365     }
4366     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4367         rother.r += grow;
4368         if (rother.r < 0) rother.r = 0;
4369         if (rother.a == HUGE_VAL) {
4370             rother.a = rme.a + M_PI;
4371         }
4372     }
4374     me->pos = n->pos + Geom::Point(rme);
4376     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4377         other->pos = n->pos + Geom::Point(rother);
4378     }
4380     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4381     // so here we just move all the knots without emitting move signals, for speed
4382     sp_node_update_handles(n, false);
4385 /**
4386  * Scale selected nodes.
4387  */
4388 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4390     if (!nodepath || !nodepath->selected) return;
4392     if (g_list_length(nodepath->selected) == 1) {
4393         // scale handles of the single selected node
4394         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4395         node_scale_one (n, grow, which);
4396     } else {
4397         // scale nodes as an "object":
4399         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4400         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4401         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4402             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4403             box.expandTo (n->pos); // contain all selected nodes
4404         }
4406         if ( Geom::are_near(box.maxExtent(), 0) ) {
4407             SPEventContext *ec = nodepath->desktop->event_context;
4408             if (!ec) return;
4409             Inkscape::MessageContext *mc = get_message_context(ec);
4410             if (!mc) return;
4411             mc->setF(Inkscape::WARNING_MESSAGE,
4412                              _("Cannot scale nodes when all are at the same location."));
4413             return;
4414         }
4415         double scale = (box.maxExtent() + grow)/box.maxExtent();
4418         Geom::Point scale_center;
4419         if (Inkscape::NodePath::Path::active_node == NULL)
4420             scale_center = box.midpoint();
4421         else
4422             scale_center = Inkscape::NodePath::Path::active_node->pos;
4424         Geom::Matrix t =
4425             Geom::Translate(-scale_center) *
4426             Geom::Scale(scale, scale) *
4427             Geom::Translate(scale_center);
4429         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4430             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4431             n->pos *= t;
4432             n->n.pos *= t;
4433             n->p.pos *= t;
4434             sp_node_update_handles(n, false);
4435         }
4436     }
4438     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4441 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4443     if (!nodepath) return;
4444     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4447 /**
4448  * Flip selected nodes horizontally/vertically.
4449  */
4450 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis, boost::optional<Geom::Point> center)
4452     if (!nodepath || !nodepath->selected) return;
4454     if (g_list_length(nodepath->selected) == 1 && !center) {
4455         // flip handles of the single selected node
4456         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4457         double temp = n->p.pos[axis];
4458         n->p.pos[axis] = n->n.pos[axis];
4459         n->n.pos[axis] = temp;
4460         sp_node_update_handles(n, false);
4461     } else {
4462         // scale nodes as an "object":
4464         Geom::Rect box = sp_node_selected_bbox (nodepath);
4465         if (!center) {
4466             center = box.midpoint();
4467         }
4468         Geom::Matrix t =
4469             Geom::Matrix (Geom::Translate(- *center)) *
4470             Geom::Matrix ((axis == Geom::X)? Geom::Scale(-1, 1) : Geom::Scale(1, -1)) *
4471             Geom::Matrix (Geom::Translate(*center));
4473         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4474             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4475             n->pos *= t;
4476             n->n.pos *= t;
4477             n->p.pos *= t;
4478             sp_node_update_handles(n, false);
4479         }
4480     }
4482     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4485 Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4487     g_assert (nodepath->selected);
4489     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4490     Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4491     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4492         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4493         box.expandTo (n->pos); // contain all selected nodes
4494     }
4495     return box;
4498 //-----------------------------------------------
4499 /**
4500  * Return new subpath under given nodepath.
4501  */
4502 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4504     g_assert(nodepath);
4505     g_assert(nodepath->desktop);
4507    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4509     s->nodepath = nodepath;
4510     s->closed = FALSE;
4511     s->nodes = NULL;
4512     s->first = NULL;
4513     s->last = NULL;
4515     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4516     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4517     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4519     return s;
4522 /**
4523  * Destroy nodes in subpath, then subpath itself.
4524  */
4525 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4527     g_assert(subpath);
4528     g_assert(subpath->nodepath);
4529     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4531     while (subpath->nodes) {
4532         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4533     }
4535     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4537     g_free(subpath);
4540 /**
4541  * Link head to tail in subpath.
4542  */
4543 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4545     g_assert(!sp->closed);
4546     g_assert(sp->last != sp->first);
4547     g_assert(sp->first->code == NR_MOVETO);
4549     sp->closed = TRUE;
4551     //Link the head to the tail
4552     sp->first->p.other = sp->last;
4553     sp->last->n.other  = sp->first;
4554     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4555     sp->first          = sp->last;
4557     //Remove the extra end node
4558     sp_nodepath_node_destroy(sp->last->n.other);
4561 /**
4562  * Open closed (loopy) subpath at node.
4563  */
4564 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4566     g_assert(sp->closed);
4567     g_assert(n->subpath == sp);
4568     g_assert(sp->first == sp->last);
4570     /* We create new startpoint, current node will become last one */
4572    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4573                                                 &n->pos, &n->pos, &n->n.pos);
4576     sp->closed        = FALSE;
4578     //Unlink to make a head and tail
4579     sp->first         = new_path;
4580     sp->last          = n;
4581     n->n.other        = NULL;
4582     new_path->p.other = NULL;
4585 /**
4586  * Return new node in subpath with given properties.
4587  * \param pos Position of node.
4588  * \param ppos Handle position in previous direction
4589  * \param npos Handle position in previous direction
4590  */
4591 Inkscape::NodePath::Node *
4592 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)
4594     g_assert(sp);
4595     g_assert(sp->nodepath);
4596     g_assert(sp->nodepath->desktop);
4598     if (nodechunk == NULL)
4599         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4601     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4603     n->subpath  = sp;
4605     if (type != Inkscape::NodePath::NODE_NONE) {
4606         // use the type from sodipodi:nodetypes
4607         n->type = type;
4608     } else {
4609         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4610             // points are (almost) collinear
4611             if (Geom::L2(*pos - *ppos) < 1e-6 || Geom::L2(*pos - *npos) < 1e-6) {
4612                 // endnode, or a node with a retracted handle
4613                 n->type = Inkscape::NodePath::NODE_CUSP;
4614             } else {
4615                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4616             }
4617         } else {
4618             n->type = Inkscape::NodePath::NODE_CUSP;
4619         }
4620     }
4622     n->code     = code;
4623     n->selected = FALSE;
4624     n->pos      = *pos;
4625     n->p.pos    = *ppos;
4626     n->n.pos    = *npos;
4628     n->dragging_out = NULL;
4630     Inkscape::NodePath::Node *prev;
4631     if (next) {
4632         //g_assert(g_list_find(sp->nodes, next));
4633         prev = next->p.other;
4634     } else {
4635         prev = sp->last;
4636     }
4638     if (prev)
4639         prev->n.other = n;
4640     else
4641         sp->first = n;
4643     if (next)
4644         next->p.other = n;
4645     else
4646         sp->last = n;
4648     n->p.other = prev;
4649     n->n.other = next;
4651     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"));
4652     sp_knot_set_position(n->knot, *pos, 0);
4654     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4655     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4656     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4658     sp_nodepath_update_node_knot(n);
4660     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4661     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4662     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4663     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4664     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4665     sp_knot_show(n->knot);
4667     // We only create handle knots and lines on demand
4668     n->p.knot = NULL;
4669     n->p.line = NULL;
4670     n->n.knot = NULL;
4671     n->n.line = NULL;
4673     sp->nodes = g_list_prepend(sp->nodes, n);
4675     return n;
4678 /**
4679  * Destroy node and its knots, link neighbors in subpath.
4680  */
4681 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4683     g_assert(node);
4684     g_assert(node->subpath);
4685     g_assert(SP_IS_KNOT(node->knot));
4687    Inkscape::NodePath::SubPath *sp = node->subpath;
4689     if (node->selected) { // first, deselect
4690         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4691         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4692     }
4694     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4696     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4697     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4698     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4699     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4700     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4701     g_object_unref(G_OBJECT(node->knot));
4703     if (node->p.knot) {
4704         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4705         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4706         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4707         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4708         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4709         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4710         g_object_unref(G_OBJECT(node->p.knot));
4711         node->p.knot = NULL;
4712     }
4714     if (node->n.knot) {
4715         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4716         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4717         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4718         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4719         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4720         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4721         g_object_unref(G_OBJECT(node->n.knot));
4722         node->n.knot = NULL;
4723     }
4725     if (node->p.line)
4726         gtk_object_destroy(GTK_OBJECT(node->p.line));
4727     if (node->n.line)
4728         gtk_object_destroy(GTK_OBJECT(node->n.line));
4730     if (sp->nodes) { // there are others nodes on the subpath
4731         if (sp->closed) {
4732             if (sp->first == node) {
4733                 g_assert(sp->last == node);
4734                 sp->first = node->n.other;
4735                 sp->last = sp->first;
4736             }
4737             node->p.other->n.other = node->n.other;
4738             node->n.other->p.other = node->p.other;
4739         } else {
4740             if (sp->first == node) {
4741                 sp->first = node->n.other;
4742                 sp->first->code = NR_MOVETO;
4743             }
4744             if (sp->last == node) sp->last = node->p.other;
4745             if (node->p.other) node->p.other->n.other = node->n.other;
4746             if (node->n.other) node->n.other->p.other = node->p.other;
4747         }
4748     } else { // this was the last node on subpath
4749         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4750     }
4752     g_mem_chunk_free(nodechunk, node);
4755 /**
4756  * Returns one of the node's two sides.
4757  * \param which Indicates which side.
4758  * \return Pointer to previous node side if which==-1, next if which==1.
4759  */
4760 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4762     g_assert(node);
4763     Inkscape::NodePath::NodeSide * result = 0;
4764     switch (which) {
4765         case -1:
4766             result = &node->p;
4767             break;
4768         case 1:
4769             result = &node->n;
4770             break;
4771         default:
4772             g_assert_not_reached();
4773     }
4775     return result;
4778 /**
4779  * Return the other side of the node, given one of its sides.
4780  */
4781 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4783     g_assert(node);
4784     Inkscape::NodePath::NodeSide *result = 0;
4786     if (me == &node->p) {
4787         result = &node->n;
4788     } else if (me == &node->n) {
4789         result = &node->p;
4790     } else {
4791         g_assert_not_reached();
4792     }
4794     return result;
4797 /**
4798  * Return NRPathcode on the given side of the node.
4799  */
4800 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4802     g_assert(node);
4804     NRPathcode result = NR_END;
4805     if (me == &node->p) {
4806         if (node->p.other) {
4807             result = (NRPathcode)node->code;
4808         } else {
4809             result = NR_MOVETO;
4810         }
4811     } else if (me == &node->n) {
4812         if (node->n.other) {
4813             result = (NRPathcode)node->n.other->code;
4814         } else {
4815             result = NR_MOVETO;
4816         }
4817     } else {
4818         g_assert_not_reached();
4819     }
4821     return result;
4824 /**
4825  * Return node with the given index
4826  */
4827 Inkscape::NodePath::Node *
4828 sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index)
4830     Inkscape::NodePath::Node *e = NULL;
4832     if (!nodepath) {
4833         return e;
4834     }
4836     //find segment
4837     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4839         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4840         int n = g_list_length(sp->nodes);
4841         if (sp->closed) {
4842             n++;
4843         }
4845         //if the piece belongs to this subpath grab it
4846         //otherwise move onto the next subpath
4847         if (index < n) {
4848             e = sp->first;
4849             for (int i = 0; i < index; ++i) {
4850                 e = e->n.other;
4851             }
4852             break;
4853         } else {
4854             if (sp->closed) {
4855                 index -= (n+1);
4856             } else {
4857                 index -= n;
4858             }
4859         }
4860     }
4862     return e;
4865 /**
4866  * Returns plain text meaning of node type.
4867  */
4868 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4870     unsigned retracted = 0;
4871     bool endnode = false;
4873     for (int which = -1; which <= 1; which += 2) {
4874         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4875         if (side->other && Geom::L2(side->pos - node->pos) < 1e-6)
4876             retracted ++;
4877         if (!side->other)
4878             endnode = true;
4879     }
4881     if (retracted == 0) {
4882         if (endnode) {
4883                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4884                 return _("end node");
4885         } else {
4886             switch (node->type) {
4887                 case Inkscape::NodePath::NODE_CUSP:
4888                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4889                     return _("cusp");
4890                 case Inkscape::NodePath::NODE_SMOOTH:
4891                     // TRANSLATORS: "smooth" is an adjective here
4892                     return _("smooth");
4893                 case Inkscape::NodePath::NODE_AUTO:
4894                     return _("auto");
4895                 case Inkscape::NodePath::NODE_SYMM:
4896                     return _("symmetric");
4897             }
4898         }
4899     } else if (retracted == 1) {
4900         if (endnode) {
4901             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4902             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4903         } else {
4904             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4905         }
4906     } else {
4907         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4908     }
4910     return NULL;
4913 /**
4914  * Handles content of statusbar as long as node tool is active.
4915  */
4916 void
4917 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4919     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");
4920     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4922     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4923     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4924     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4925     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4927     SPDesktop *desktop = NULL;
4928     if (nodepath) {
4929         desktop = nodepath->desktop;
4930     } else {
4931         desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above
4932     }
4934     SPEventContext *ec = desktop->event_context;
4935     if (!ec) return;
4937     Inkscape::MessageContext *mc = get_message_context(ec);
4938     if (!mc) return;
4940     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4942     if (selected_nodes == 0) {
4943         Inkscape::Selection *sel = desktop->selection;
4944         if (!sel || sel->isEmpty()) {
4945             mc->setF(Inkscape::NORMAL_MESSAGE,
4946                      _("Select a single object to edit its nodes or handles."));
4947         } else {
4948             if (nodepath) {
4949             mc->setF(Inkscape::NORMAL_MESSAGE,
4950                      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.",
4951                               "<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.",
4952                               total_nodes),
4953                      total_nodes);
4954             } else {
4955                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4956                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4957                 } else {
4958                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4959                 }
4960             }
4961         }
4962     } else if (nodepath && selected_nodes == 1) {
4963         mc->setF(Inkscape::NORMAL_MESSAGE,
4964                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4965                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4966                           total_nodes),
4967                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4968     } else {
4969         if (selected_subpaths > 1) {
4970             mc->setF(Inkscape::NORMAL_MESSAGE,
4971                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4972                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4973                               total_nodes),
4974                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4975         } else {
4976             mc->setF(Inkscape::NORMAL_MESSAGE,
4977                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4978                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4979                               total_nodes),
4980                      selected_nodes, total_nodes, when_selected);
4981         }
4982     }
4985 /*
4986  * returns a *copy* of the curve of that object.
4987  */
4988 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4989     if (!object)
4990         return NULL;
4992     SPCurve *curve = NULL;
4993     if (SP_IS_PATH(object)) {
4994         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4995         curve = curve_new->copy();
4996     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4997         const gchar *svgd = object->repr->attribute(key);
4998         if (svgd) {
4999             Geom::PathVector pv = sp_svg_read_pathv(svgd);
5000             SPCurve *curve_new = new SPCurve(pv);
5001             if (curve_new) {
5002                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
5003             }
5004         }
5005     }
5007     return curve;
5010 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
5011     if (!np || !np->object || !curve)
5012         return;
5014     if (SP_IS_PATH(np->object)) {
5015         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
5016             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
5017         } else {
5018             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
5019         }
5020     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
5021         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(np->object)->get_lpe();
5022         if (lpe) {
5023             Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( lpe->getParameter(np->repr_key) );
5024             if (pathparam) {
5025                 pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
5026                 np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
5027             }
5028         }
5029     }
5032 /*
5033 SPCanvasItem *
5034 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
5035     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
5037 */
5040 /// \todo this code to generate a helper canvasitem from an spcurve should be moved to different file
5041 SPCanvasItem *
5042 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const Geom::Matrix & i2d, guint32 color = 0xff0000ff) {
5043     SPCurve *flash_curve = curve->copy();
5044     flash_curve->transform(i2d);
5045     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
5046     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
5047     // unless we also flash the nodes...
5048     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
5049     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
5050     sp_canvas_item_show(canvasitem);
5051     flash_curve->unref();
5052     return canvasitem;
5055 SPCanvasItem *
5056 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item) {
5057     if (!item || !desktop) {
5058         return NULL;
5059     }
5061     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5062     guint32 color = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
5064     Geom::Matrix i2d = sp_item_i2d_affine(item);
5066     SPCurve *curve = NULL;
5067     if (SP_IS_PATH(item)) {
5068         curve = sp_path_get_curve_for_edit(SP_PATH(item));
5069     } else if ( SP_IS_SHAPE(item) && SP_SHAPE(item)->curve ) {
5070         curve = sp_shape_get_curve (SP_SHAPE(item));
5071     } else if ( SP_IS_TEXT(item) ) {
5072         // do not display helperpath for text - we cannot do anything with it in Node tool anyway
5073         // curve = SP_TEXT(item)->getNormalizedBpath();
5074         return NULL;
5075     } else {
5076         g_warning ("-----> sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item): TODO: generate the helper path for this item type!\n");
5077         return NULL;
5078     }
5080     SPCanvasItem * helperpath = sp_nodepath_generate_helperpath(desktop, curve, i2d, color);
5082     curve->unref();
5084     return helperpath;
5088 // TODO: Merge this with sp_nodepath_make_helper_item()!
5089 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
5090     np->show_helperpath = show;
5092     if (show) {
5093         SPCurve *helper_curve = np->curve->copy();
5094         helper_curve->transform(np->i2d);
5095         if (!np->helper_path) {
5096             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
5098             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
5099             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);
5100             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
5101             sp_canvas_item_move_to_z(np->helper_path, 0);
5102             sp_canvas_item_show(np->helper_path);
5103         } else {
5104             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
5105         }
5106         helper_curve->unref();
5107     } else {
5108         if (np->helper_path) {
5109             GtkObject *temp = np->helper_path;
5110             np->helper_path = NULL;
5111             gtk_object_destroy(temp);
5112         }
5113     }
5116 /* sp_nodepath_make_straight_path:
5117  *   Prevents user from curving the path by dragging a segment or activating handles etc.
5118  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
5119  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
5120  */
5121 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
5122     np->straight_path = true;
5123     np->show_handles = false;
5124     g_message("add code to make the path straight.");
5125     // do sp_nodepath_convert_node_type on all nodes?
5126     // coding tip: search for this text : "Make selected segments lines"
5129 /*
5130   Local Variables:
5131   mode:c++
5132   c-file-style:"stroustrup"
5133   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5134   indent-tabs-mode:nil
5135   fill-column:99
5136   End:
5137 */
5138 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :