Code

Avoid crash by uninitialized perspectives.
[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 
1205                   OR both_segments_are_curves 
1206                   OR one_is_line_but_the_curveside_node_is_selected_and_has_two_handles) {
1207                 //pull both handles
1208             } else {
1209                 // pull the handle opposite to line segment, making node half-smooth
1210             }
1211         }
1212 */
1213         bool p_has_handle = (Geom::L2(node->pos  - node->p.pos) > 1e-6);
1214         bool n_has_handle = (Geom::L2(node->pos  - node->n.pos) > 1e-6);
1215         bool p_is_line = sp_node_side_is_line(node, &node->p);
1216         bool n_is_line = sp_node_side_is_line(node, &node->n);
1218 #define NODE_HAS_BOTH_HANDLES(node) ((Geom::L2(node->pos  - node->n.pos) > 1e-6) && (Geom::L2(node->pos  - node->p.pos) > 1e-6))
1220         if (p_has_handle && n_has_handle) {
1221             // do nothing, adjust_handles will line them up
1222         } else if (p_has_handle || n_has_handle) {
1223             if (p_has_handle && n_is_line) {
1224                 Radial line (node->n.other->pos - node->pos);
1225                 Radial handle (node->pos - node->p.pos);
1226                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1227                     // already half-smooth; pull opposite handle too making it fully smooth
1228                     node->n.other->code = NR_CURVETO;
1229                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1230                 } else {
1231                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1232                 }
1233             } else if (n_has_handle && p_is_line) {
1234                 Radial line (node->p.other->pos - node->pos);
1235                 Radial handle (node->pos - node->n.pos);
1236                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1237                     // already half-smooth; pull opposite handle too making it fully smooth
1238                     node->code = NR_CURVETO;
1239                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1240                 } else {
1241                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1242                 }
1243             } else if (p_has_handle && node->n.other) {
1244                 // pull n handle
1245                 node->n.other->code = NR_CURVETO;
1246                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1247                     Geom::L2(node->p.pos - node->pos) :
1248                     Geom::L2(node->n.other->pos - node->pos) / 3;
1249                 node->n.pos = node->pos - (len / Geom::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1250             } else if (n_has_handle && node->p.other) {
1251                 // pull p handle
1252                 node->code = NR_CURVETO;
1253                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1254                     Geom::L2(node->n.pos - node->pos) :
1255                     Geom::L2(node->p.other->pos - node->pos) / 3;
1256                 node->p.pos = node->pos - (len / Geom::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1257             }
1258         } else if (!p_has_handle && !n_has_handle) {
1259             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other) || 
1260                 (n_is_line && node->p.other && node->p.other->selected && NODE_HAS_BOTH_HANDLES(node->p.other)) ||
1261                 (p_is_line && node->n.other && node->n.other->selected && NODE_HAS_BOTH_HANDLES(node->n.other)) 
1262             ) {
1263                 // no handles, but: both segments are either lines or curves; or: one is line and the
1264                 // node at the other side is selected (so it was just smoothed too!) and now has both
1265                 // handles: then pull both handles here
1267                 // convert both to curves:
1268                 node->code = NR_CURVETO;
1269                 node->n.other->code = NR_CURVETO;
1271                 sp_node_adjust_handles_auto(node);
1272             } else {
1273                 // pull the handle opposite to line segment, making it half-smooth
1274                 if (p_is_line && node->n.other) {
1275                     if (type != Inkscape::NodePath::NODE_SYMM) {
1276                         // pull n handle
1277                         node->n.other->code = NR_CURVETO;
1278                         double len =  Geom::L2(node->n.other->pos - node->pos) / 3;
1279                         node->n.pos = node->pos + (len / Geom::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1280                     }
1281                 } else if (n_is_line && node->p.other) {
1282                     if (type != Inkscape::NodePath::NODE_SYMM) {
1283                         // pull p handle
1284                         node->code = NR_CURVETO;
1285                         double len =  Geom::L2(node->p.other->pos - node->pos) / 3;
1286                         node->p.pos = node->pos + (len / Geom::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1287                     }
1288                 }
1289             }
1290         }
1291     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1292         // cusping a cusp: retract nodes
1293         node->p.pos = node->pos;
1294         node->n.pos = node->pos;
1295     }
1297     sp_nodepath_set_node_type (node, type);
1300 /**
1301  * Move node to point, and adjust its and neighbouring handles.
1302  */
1303 void sp_node_moveto(Inkscape::NodePath::Node *node, Geom::Point p)
1305     if (node->type == Inkscape::NodePath::NODE_AUTO) {
1306         node->pos = p;
1307         sp_node_adjust_handles_auto(node);
1308     } else {
1309         Geom::Point delta = p - node->pos;
1310         node->pos = p;
1312         node->p.pos += delta;
1313         node->n.pos += delta;
1314     }
1316     Inkscape::NodePath::Node *node_p = NULL;
1317     Inkscape::NodePath::Node *node_n = NULL;
1319     if (node->p.other) {
1320         if (sp_node_side_is_line(node, &node->p)) {
1321             sp_node_adjust_handle(node, 1);
1322             sp_node_adjust_handle(node->p.other, -1);
1323             node_p = node->p.other;
1324         }
1325         if (!node->p.other->selected && node->p.other->type == Inkscape::NodePath::NODE_AUTO) {
1326             sp_node_adjust_handles_auto(node->p.other);
1327             node_p = node->p.other;
1328         }
1329     }
1330     if (node->n.other) {
1331         if (sp_node_side_is_line(node, &node->n)) {
1332             sp_node_adjust_handle(node, -1);
1333             sp_node_adjust_handle(node->n.other, 1);
1334             node_n = node->n.other;
1335         }
1336         if (!node->n.other->selected && node->n.other->type == Inkscape::NodePath::NODE_AUTO) {
1337             sp_node_adjust_handles_auto(node->n.other);
1338             node_n = node->n.other;
1339         }
1340     }
1342     // this function is only called from batch movers that will update display at the end
1343     // themselves, so here we just move all the knots without emitting move signals, for speed
1344     sp_node_update_handles(node, false);
1345     if (node_n) {
1346         sp_node_update_handles(node_n, false);
1347     }
1348     if (node_p) {
1349         sp_node_update_handles(node_p, false);
1350     }
1353 /**
1354  * Call sp_node_moveto() for node selection and handle possible snapping.
1355  */
1356 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, Geom::Coord dx, Geom::Coord dy,
1357                                             bool const snap, bool constrained = false,
1358                                             Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point())
1360     Geom::Point delta(dx, dy);
1361     Geom::Point best_pt = delta;
1362     Inkscape::SnappedPoint best;
1364     if (snap) {
1365         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1366          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we
1367          * must provide that information. */
1369         // Build a list of the unselected nodes to which the snapper should snap
1370         std::vector<Inkscape::SnapCandidatePoint> unselected_nodes;
1371         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1372             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1373             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1374                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1375                 if (!node->selected) {
1376                     unselected_nodes.push_back(Inkscape::SnapCandidatePoint(node->pos, Inkscape::SNAPSOURCE_UNDEFINED, node->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPTARGET_NODE_SMOOTH : Inkscape::SNAPTARGET_NODE_CUSP));
1377                 }
1378             }
1379         }
1381         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1383         // When only the node closest to the mouse pointer is to be snapped
1384         // then we will not even try to snap to other points and discard those immediately
1385         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1386         bool closest_only = prefs->getBool("/options/snapclosestonly/value", false);
1388         Inkscape::NodePath::Node *closest_node = NULL;
1389         Geom::Coord closest_dist = NR_HUGE;
1391         if (closest_only) {
1392             for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1393                 Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1394                 Geom::Coord dist = Geom::L2(nodepath->drag_origin_mouse - n->origin);
1395                 if (dist < closest_dist) {
1396                     closest_node = n;
1397                     closest_dist = dist;
1398                 }
1399             }
1400         }
1402         // Iterate through all selected nodes
1403         m.setup(nodepath->desktop, false, nodepath->item, &unselected_nodes);
1404         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1405             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1406             if (!closest_only || n == closest_node) { //try to snap either all selected nodes or only the closest one
1407                 Inkscape::SnappedPoint s;
1408                 Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP);
1409                 if (constrained) {
1410                     Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1411                     dedicated_constraint.setPoint(n->pos);
1412                     s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, Inkscape::SnapCandidatePoint(n->pos + delta, source_type), dedicated_constraint);
1413                 } else {
1414                     s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, Inkscape::SnapCandidatePoint(n->pos + delta, source_type));
1415                 }
1417                 if (s.getSnapped()) {
1418                     s.setPointerDistance(Geom::L2(nodepath->drag_origin_mouse - n->origin));
1419                     if (!s.isOtherSnapBetter(best, true)) {
1420                         best = s;
1421                         best_pt = from_2geom(s.getPoint()) - n->pos;
1422                     }
1423                 }
1424             }
1425         }
1427         if (best.getSnapped()) {
1428             nodepath->desktop->snapindicator->set_new_snaptarget(best);
1429         } else {
1430             nodepath->desktop->snapindicator->remove_snaptarget();
1431         }
1432     }
1434     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1435         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1436         sp_node_moveto(n, n->pos + best_pt);
1437     }
1439     // do not update repr here so that node dragging is acceptably fast
1440     update_object(nodepath);
1443 /**
1444 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1445 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1446 near x = 0.
1447  */
1448 double
1449 sculpt_profile (double x, double alpha, guint profile)
1451     double result = 1;
1453     if (x >= 1) {
1454         result = 0;
1455     } else if (x <= 0) {
1456         result = 1;
1457     } else {
1458         switch (profile) {
1459             case SCULPT_PROFILE_LINEAR:
1460                 result = 1 - x;
1461                 break;
1462             case SCULPT_PROFILE_BELL:
1463                 result = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1464                 break;
1465             case SCULPT_PROFILE_ELLIPTIC:
1466                 result = sqrt(1 - x*x);
1467                 break;
1468             default:
1469                 g_assert_not_reached();
1470         }
1471     }
1473     return result;
1476 double
1477 bezier_length (Geom::Point a, Geom::Point ah, Geom::Point bh, Geom::Point b)
1479     // extremely primitive for now, don't have time to look for the real one
1480     double lower = Geom::L2(b - a);
1481     double upper = Geom::L2(ah - a) + Geom::L2(bh - ah) + Geom::L2(bh - b);
1482     return (lower + upper)/2;
1485 void
1486 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, Geom::Point delta, Geom::Point delta_n, Geom::Point delta_p)
1488     n->pos = n->origin + delta;
1489     n->n.pos = n->n.origin + delta_n;
1490     n->p.pos = n->p.origin + delta_p;
1491     sp_node_adjust_handles(n);
1492     sp_node_update_handles(n, false);
1495 /**
1496  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1497  * on how far they are from the dragged node n.
1498  */
1499 static void
1500 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, Geom::Point delta)
1502     g_assert (n);
1503     g_assert (nodepath);
1504     g_assert (n->subpath->nodepath == nodepath);
1506     double pressure = n->knot->pressure;
1507     if (pressure == 0)
1508         pressure = 0.5; // default
1509     pressure = CLAMP (pressure, 0.2, 0.8);
1511     // map pressure to alpha = 1/5 ... 5
1512     double alpha = 1 - 2 * fabs(pressure - 0.5);
1513     if (pressure > 0.5)
1514         alpha = 1/alpha;
1516     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1517     guint profile = prefs->getInt("/tools/nodes/sculpting_profile", SCULPT_PROFILE_BELL);
1519     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1520         // Only one subpath has selected nodes:
1521         // use linear mode, where the distance from n to node being dragged is calculated along the path
1523         double n_sel_range = 0, p_sel_range = 0;
1524         guint n_nodes = 0, p_nodes = 0;
1525         guint n_sel_nodes = 0, p_sel_nodes = 0;
1527         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1528         {
1529             double n_range = 0, p_range = 0;
1530             bool n_going = true, p_going = true;
1531             Inkscape::NodePath::Node *n_node = n;
1532             Inkscape::NodePath::Node *p_node = n;
1533             do {
1534                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1535                 if (n_node && n_going)
1536                     n_node = n_node->n.other;
1537                 if (n_node == NULL) {
1538                     n_going = false;
1539                 } else {
1540                     n_nodes ++;
1541                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1542                     if (n_node->selected) {
1543                         n_sel_nodes ++;
1544                         n_sel_range = n_range;
1545                     }
1546                     if (n_node == p_node) {
1547                         n_going = false;
1548                         p_going = false;
1549                     }
1550                 }
1551                 if (p_node && p_going)
1552                     p_node = p_node->p.other;
1553                 if (p_node == NULL) {
1554                     p_going = false;
1555                 } else {
1556                     p_nodes ++;
1557                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1558                     if (p_node->selected) {
1559                         p_sel_nodes ++;
1560                         p_sel_range = p_range;
1561                     }
1562                     if (p_node == n_node) {
1563                         n_going = false;
1564                         p_going = false;
1565                     }
1566                 }
1567             } while (n_going || p_going);
1568         }
1570         // Second pass: actually move nodes in this subpath
1571         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1572         {
1573             double n_range = 0, p_range = 0;
1574             bool n_going = true, p_going = true;
1575             Inkscape::NodePath::Node *n_node = n;
1576             Inkscape::NodePath::Node *p_node = n;
1577             do {
1578                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1579                 if (n_node && n_going)
1580                     n_node = n_node->n.other;
1581                 if (n_node == NULL) {
1582                     n_going = false;
1583                 } else {
1584                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1585                     if (n_node->selected) {
1586                         sp_nodepath_move_node_and_handles (n_node,
1587                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1588                                                            sculpt_profile ((n_range + Geom::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1589                                                            sculpt_profile ((n_range - Geom::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1590                     }
1591                     if (n_node == p_node) {
1592                         n_going = false;
1593                         p_going = false;
1594                     }
1595                 }
1596                 if (p_node && p_going)
1597                     p_node = p_node->p.other;
1598                 if (p_node == NULL) {
1599                     p_going = false;
1600                 } else {
1601                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1602                     if (p_node->selected) {
1603                         sp_nodepath_move_node_and_handles (p_node,
1604                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1605                                                            sculpt_profile ((p_range - Geom::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1606                                                            sculpt_profile ((p_range + Geom::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1607                     }
1608                     if (p_node == n_node) {
1609                         n_going = false;
1610                         p_going = false;
1611                     }
1612                 }
1613             } while (n_going || p_going);
1614         }
1616     } else {
1617         // Multiple subpaths have selected nodes:
1618         // use spatial mode, where the distance from n to node being dragged is measured directly as Geom::L2.
1619         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1620         // fix the pear-like shape when sculpting e.g. a ring
1622         // First pass: calculate range
1623         gdouble direct_range = 0;
1624         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1625             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1626             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1627                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1628                 if (node->selected) {
1629                     direct_range = MAX(direct_range, Geom::L2(node->origin - n->origin));
1630                 }
1631             }
1632         }
1634         // Second pass: actually move nodes
1635         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1636             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1637             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1638                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1639                 if (node->selected) {
1640                     if (direct_range > 1e-6) {
1641                         sp_nodepath_move_node_and_handles (node,
1642                                                        sculpt_profile (Geom::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1643                                                        sculpt_profile (Geom::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1644                                                        sculpt_profile (Geom::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1645                     } else {
1646                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1647                     }
1649                 }
1650             }
1651         }
1652     }
1654     // do not update repr here so that node dragging is acceptably fast
1655     update_object(nodepath);
1659 /**
1660  * Move node selection to point, adjust its and neighbouring handles,
1661  * handle possible snapping, and commit the change with possible undo.
1662  */
1663 void
1664 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1666     if (!nodepath) return;
1668     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1670     if (dx == 0) {
1671         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1672     } else if (dy == 0) {
1673         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1674     } else {
1675         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1676     }
1679 /**
1680  * Move node selection off screen and commit the change.
1681  */
1682 void
1683 sp_node_selected_move_screen(SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1685     // borrowed from sp_selection_move_screen in selection-chemistry.c
1686     // we find out the current zoom factor and divide deltas by it
1688     gdouble zoom = desktop->current_zoom();
1689     gdouble zdx = dx / zoom;
1690     gdouble zdy = dy / zoom;
1692     if (!nodepath) return;
1694     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1696     if (dx == 0) {
1697         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1698     } else if (dy == 0) {
1699         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1700     } else {
1701         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1702     }
1705 /**
1706  * Move selected nodes to the absolute position given
1707  */
1708 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis)
1710     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1711         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1712         Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]);
1713         sp_node_moveto(n, npos);
1714     }
1716     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1719 /**
1720  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return Geom::Nothing
1721  */
1722 boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1724     boost::optional<Geom::Coord> no_coord;
1725     g_return_val_if_fail(nodepath->selected, no_coord);
1727     // determine coordinate of first selected node
1728     GList *nsel = nodepath->selected;
1729     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1730     Geom::Coord coord = n->pos[axis];
1731     bool coincide = true;
1733     // compare it to the coordinates of all the other selected nodes
1734     for (GList *l = nsel->next; l != NULL; l = l->next) {
1735         n = (Inkscape::NodePath::Node *) l->data;
1736         if (n->pos[axis] != coord) {
1737             coincide = false;
1738         }
1739     }
1740     if (coincide) {
1741         return coord;
1742     } else {
1743         Geom::Rect bbox = sp_node_selected_bbox(nodepath);
1744         // currently we return the coordinate of the bounding box midpoint because I don't know how
1745         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1746         return bbox.midpoint()[axis];
1747     }
1750 /** If they don't yet exist, creates knot and line for the given side of the node */
1751 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1753     if (!side->knot) {
1754         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"));
1756         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1757         side->knot->setSize (7);
1758         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1759         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1760         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1761         sp_knot_update_ctrl(side->knot);
1763         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1764         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1765         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1766         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1767         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1768         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1769     }
1771     if (!side->line) {
1772         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1773                                         SP_TYPE_CTRLLINE, NULL);
1774     }
1777 /**
1778  * Ensure the given handle of the node is visible/invisible, update its screen position
1779  */
1780 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1782     g_assert(node != NULL);
1784    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1785     NRPathcode code = sp_node_path_code_from_side(node, side);
1787     show_handle = show_handle && (code == NR_CURVETO) && (Geom::L2(side->pos - node->pos) > 1e-6);
1789     if (show_handle) {
1790         if (!side->knot) { // No handle knot at all
1791             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1792             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1793             side->knot->pos = side->pos;
1794             if (side->knot->item)
1795                 SP_CTRL(side->knot->item)->moveto(side->pos);
1796             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1797             sp_knot_show(side->knot);
1798         } else {
1799             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1800                 if (fire_move_signals) {
1801                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1802                 } else {
1803                     sp_knot_moveto(side->knot, side->pos);
1804                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1805                 }
1806             }
1807             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1808                 sp_knot_show(side->knot);
1809             }
1810         }
1811         sp_canvas_item_show(side->line);
1812     } else {
1813         if (side->knot) {
1814             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1815                 sp_knot_hide(side->knot);
1816             }
1817         }
1818         if (side->line) {
1819             sp_canvas_item_hide(side->line);
1820         }
1821     }
1824 /**
1825  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1826  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1827  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1828  * updated; otherwise, just move the knots silently (used in batch moves).
1829  */
1830 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1832     g_assert(node != NULL);
1834     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1835         sp_knot_show(node->knot);
1836     }
1838     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1839         if (fire_move_signals)
1840             sp_knot_set_position(node->knot, node->pos, 0);
1841         else
1842             sp_knot_moveto(node->knot, node->pos);
1843     }
1845     gboolean show_handles = node->selected;
1846     if (node->p.other != NULL) {
1847         if (node->p.other->selected) show_handles = TRUE;
1848     }
1849     if (node->n.other != NULL) {
1850         if (node->n.other->selected) show_handles = TRUE;
1851     }
1853     if (node->subpath->nodepath->show_handles == false)
1854         show_handles = FALSE;
1856     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1857     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1860 /**
1861  * Call sp_node_update_handles() for all nodes on subpath.
1862  */
1863 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1865     g_assert(subpath != NULL);
1867     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1868         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1869     }
1872 /**
1873  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1874  */
1875 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1877     g_assert(nodepath != NULL);
1879     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1880         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1881     }
1884 void
1885 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1887     if (nodepath) {
1888         nodepath->show_handles = show;
1889         sp_nodepath_update_handles(nodepath);
1890     }
1893 /**
1894  * Adds all selected nodes in nodepath to list.
1895  */
1896 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1898     StlConv<Node *>::list(l, selected);
1899 /// \todo this adds a copying, rework when the selection becomes a stl list
1902 /**
1903  * Align selected nodes on the specified axis.
1904  */
1905 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1907     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1908         return;
1909     }
1911     if ( !nodepath->selected->next ) { // only one node selected
1912         return;
1913     }
1914    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1915     Geom::Point dest(pNode->pos);
1916     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1917         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1918         if (pNode) {
1919             dest[axis] = pNode->pos[axis];
1920             sp_node_moveto(pNode, dest);
1921         }
1922     }
1924     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1927 /// Helper struct.
1928 struct NodeSort
1930    Inkscape::NodePath::Node *_node;
1931     Geom::Coord _coord;
1932     /// \todo use vectorof pointers instead of calling copy ctor
1933     NodeSort(Inkscape::NodePath::Node *node, Geom::Dim2 axis) :
1934         _node(node), _coord(node->pos[axis])
1935     {}
1937 };
1939 static bool operator<(NodeSort const &a, NodeSort const &b)
1941     return (a._coord < b._coord);
1944 /**
1945  * Distribute selected nodes on the specified axis.
1946  */
1947 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1949     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1950         return;
1951     }
1953     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1954         return;
1955     }
1957    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1958     std::vector<NodeSort> sorted;
1959     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1960         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1961         if (pNode) {
1962             NodeSort n(pNode, axis);
1963             sorted.push_back(n);
1964             //dest[axis] = pNode->pos[axis];
1965             //sp_node_moveto(pNode, dest);
1966         }
1967     }
1968     std::sort(sorted.begin(), sorted.end());
1969     unsigned int len = sorted.size();
1970     //overall bboxes span
1971     float dist = (sorted.back()._coord -
1972                   sorted.front()._coord);
1973     //new distance between each bbox
1974     float step = (dist) / (len - 1);
1975     float pos = sorted.front()._coord;
1976     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1977           it < sorted.end();
1978           it ++ )
1979     {
1980         Geom::Point dest((*it)._node->pos);
1981         dest[axis] = pos;
1982         sp_node_moveto((*it)._node, dest);
1983         pos += step;
1984     }
1986     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1990 /**
1991  * Call sp_nodepath_line_add_node() for all selected segments.
1992  */
1993 void
1994 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1996     if (!nodepath) {
1997         return;
1998     }
2000     GList *nl = NULL;
2002     int n_added = 0;
2004     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2005        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
2006         g_assert(t->selected);
2007         if (t->p.other && t->p.other->selected) {
2008             nl = g_list_prepend(nl, t);
2009         }
2010     }
2012     while (nl) {
2013        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
2014        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
2015        sp_nodepath_node_select(n, TRUE, FALSE);
2016        n_added ++;
2017        nl = g_list_remove(nl, t);
2018     }
2020     /** \todo fixme: adjust ? */
2021     sp_nodepath_update_handles(nodepath);
2023     if (n_added > 1) {
2024         sp_nodepath_update_repr(nodepath, _("Add nodes"));
2025     } else if (n_added > 0) {
2026         sp_nodepath_update_repr(nodepath, _("Add node"));
2027     }
2029     sp_nodepath_update_statusbar(nodepath);
2032 /**
2033  * Select segment nearest to point
2034  */
2035 void
2036 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p, bool toggle)
2038     if (!nodepath) {
2039         return;
2040     }
2042     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2043     Geom::PathVector const &pathv = curve->get_pathvector();
2044     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2045     if (!pvpos) {
2046         g_print ("Possible error?\n");
2047         return;
2048     }
2050     // calculate index for nodepath's representation.
2051     unsigned int segment_index = floor(pvpos->t) + 1;
2052     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2053         segment_index += pathv[i].size() + 1;
2054         if (pathv[i].closed()) {
2055             segment_index += 1;
2056         }
2057     }
2059     curve->unref();
2061     //find segment to segment
2062     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2064     //fixme: this can return NULL, so check before proceeding.
2065     g_return_if_fail(e != NULL);
2067     gboolean force = FALSE;
2068     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
2069         force = TRUE;
2070     }
2071     sp_nodepath_node_select(e, (gboolean) toggle, force);
2072     if (e->p.other)
2073         sp_nodepath_node_select(e->p.other, TRUE, force);
2075     sp_nodepath_update_handles(nodepath);
2077     sp_nodepath_update_statusbar(nodepath);
2080 /**
2081  * Add a node nearest to point
2082  */
2083 void
2084 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p)
2086     if (!nodepath) {
2087         return;
2088     }
2090     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2091     Geom::PathVector const &pathv = curve->get_pathvector();
2092     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2093     if (!pvpos) {
2094         g_print ("Possible error?\n");
2095         return;
2096     }
2098     // calculate index for nodepath's representation.
2099     double int_part;
2100     double t = std::modf(pvpos->t, &int_part);
2101     unsigned int segment_index = (unsigned int)int_part + 1;
2102     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2103         segment_index += pathv[i].size() + 1;
2104         if (pathv[i].closed()) {
2105             segment_index += 1;
2106         }
2107     }
2109     curve->unref();
2111     //find segment to split
2112     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2113     if (!e) {
2114         return;
2115     }
2117     //don't know why but t seems to flip for lines
2118     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2119         t = 1.0 - t;
2120     }
2122     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2123     sp_nodepath_node_select(n, FALSE, TRUE);
2125     /* fixme: adjust ? */
2126     sp_nodepath_update_handles(nodepath);
2128     sp_nodepath_update_repr(nodepath, _("Add node"));
2130     sp_nodepath_update_statusbar(nodepath);
2133 /*
2134  * Adjusts a segment so that t moves by a certain delta for dragging
2135  * converts lines to curves
2136  *
2137  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2138  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2139  */
2140 void
2141 sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, Geom::Point delta)
2143     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node);
2145     //fixme: e and e->p can be NULL, so check for those before proceeding
2146     g_return_if_fail(e != NULL);
2147     g_return_if_fail(&e->p != NULL);
2149     if (e->type == Inkscape::NodePath::NODE_AUTO) {
2150         e->type = Inkscape::NodePath::NODE_SMOOTH;
2151         sp_nodepath_update_node_knot (e);
2152     }
2153     if (e->p.other->type == Inkscape::NodePath::NODE_AUTO) {
2154         e->p.other->type = Inkscape::NodePath::NODE_SMOOTH;
2155         sp_nodepath_update_node_knot (e->p.other);
2156     }
2158     /* feel good is an arbitrary parameter that distributes the delta between handles
2159      * if t of the drag point is less than 1/6 distance form the endpoint only
2160      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2161      */
2162     double feel_good;
2163     if (t <= 1.0 / 6.0)
2164         feel_good = 0;
2165     else if (t <= 0.5)
2166         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2167     else if (t <= 5.0 / 6.0)
2168         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2169     else
2170         feel_good = 1;
2172     //if we're dragging a line convert it to a curve
2173     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2174         sp_nodepath_set_line_type(e, NR_CURVETO);
2175     }
2177     Geom::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2178     Geom::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2179     e->p.other->n.pos += offsetcoord0;
2180     e->p.pos += offsetcoord1;
2182     // adjust handles of adjacent nodes where necessary
2183     sp_node_adjust_handle(e,1);
2184     sp_node_adjust_handle(e->p.other,-1);
2186     sp_nodepath_update_handles(e->subpath->nodepath);
2188     update_object(e->subpath->nodepath);
2190     sp_nodepath_update_statusbar(e->subpath->nodepath);
2194 /**
2195  * Call sp_nodepath_break() for all selected segments.
2196  */
2197 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2199     if (!nodepath) return;
2201     GList *tempin = g_list_copy(nodepath->selected);
2202     GList *temp = NULL;
2203     for (GList *l = tempin; l != NULL; l = l->next) {
2204        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2205        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2206         if (nn == NULL) continue; // no break, no new node
2207         temp = g_list_prepend(temp, nn);
2208     }
2209     g_list_free(tempin);
2211     if (temp) {
2212         sp_nodepath_deselect(nodepath);
2213     }
2214     for (GList *l = temp; l != NULL; l = l->next) {
2215         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2216     }
2218     sp_nodepath_update_handles(nodepath);
2220     sp_nodepath_update_repr(nodepath, _("Break path"));
2223 /**
2224  * Duplicate the selected node(s).
2225  */
2226 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2228     if (!nodepath) {
2229         return;
2230     }
2232     GList *temp = NULL;
2233     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2234        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2235        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2236         if (nn == NULL) continue; // could not duplicate
2237         temp = g_list_prepend(temp, nn);
2238     }
2240     if (temp) {
2241         sp_nodepath_deselect(nodepath);
2242     }
2243     for (GList *l = temp; l != NULL; l = l->next) {
2244         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2245     }
2247     sp_nodepath_update_handles(nodepath);
2249     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2252 /**
2253  *  Internal function to join two nodes by merging them into one.
2254  */
2255 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2257     /* a and b are endpoints */
2259     // if one of the two nodes is mouseovered, fix its position
2260     Geom::Point c;
2261     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2262         c = a->pos;
2263     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2264         c = b->pos;
2265     } else {
2266         // otherwise, move joined node to the midpoint
2267         c = (a->pos + b->pos) / 2;
2268     }
2270     if (a->subpath == b->subpath) {
2271        Inkscape::NodePath::SubPath *sp = a->subpath;
2272         sp_nodepath_subpath_close(sp);
2273         sp_node_moveto (sp->first, c);
2275         sp_nodepath_update_handles(sp->nodepath);
2276         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2277         return;
2278     }
2280     /* a and b are separate subpaths */
2281     Inkscape::NodePath::SubPath *sa = a->subpath;
2282     Inkscape::NodePath::SubPath *sb = b->subpath;
2283     Geom::Point p;
2284     Inkscape::NodePath::Node *n;
2285     NRPathcode code;
2286     if (a == sa->first) {
2287         // we will now reverse sa, so that a is its last node, not first, and drop that node
2288         p = sa->first->n.pos;
2289         code = (NRPathcode)sa->first->n.other->code;
2290         // create new subpath
2291        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2292        // create a first moveto node on it
2293         n = sa->last;
2294         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2295         n = n->p.other;
2296         if (n == sa->first) n = NULL;
2297         while (n) {
2298             // copy the rest of the nodes from sa to t, going backwards
2299             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2300             n = n->p.other;
2301             if (n == sa->first) n = NULL;
2302         }
2303         // replace sa with t
2304         sp_nodepath_subpath_destroy(sa);
2305         sa = t;
2306     } else if (a == sa->last) {
2307         // a is already last, just drop it
2308         p = sa->last->p.pos;
2309         code = (NRPathcode)sa->last->code;
2310         sp_nodepath_node_destroy(sa->last);
2311     } else {
2312         code = NR_END;
2313         g_assert_not_reached();
2314     }
2316     if (b == sb->first) {
2317         // copy all nodes from b to a, forward
2318         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2319         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2320             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2321         }
2322     } else if (b == sb->last) {
2323         // copy all nodes from b to a, backward
2324         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2325         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2326             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2327         }
2328     } else {
2329         g_assert_not_reached();
2330     }
2331     /* and now destroy sb */
2333     sp_nodepath_subpath_destroy(sb);
2335     sp_nodepath_update_handles(sa->nodepath);
2337     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2339     sp_nodepath_update_statusbar(nodepath);
2342 /**
2343  *  Internal function to join two nodes by adding a segment between them.
2344  */
2345 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2347     if (a->subpath == b->subpath) {
2348        Inkscape::NodePath::SubPath *sp = a->subpath;
2350         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2351         sp->closed = TRUE;
2353         sp->first->p.other = sp->last;
2354         sp->last->n.other  = sp->first;
2356         sp_node_handle_mirror_p_to_n(sp->last);
2357         sp_node_handle_mirror_n_to_p(sp->first);
2359         sp->first->code = sp->last->code;
2360         sp->first       = sp->last;
2362         sp_nodepath_update_handles(sp->nodepath);
2364         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2366         return;
2367     }
2369     /* a and b are separate subpaths */
2370     Inkscape::NodePath::SubPath *sa = a->subpath;
2371     Inkscape::NodePath::SubPath *sb = b->subpath;
2373     Inkscape::NodePath::Node *n;
2374     Geom::Point p;
2375     NRPathcode code;
2376     if (a == sa->first) {
2377         code = (NRPathcode) sa->first->n.other->code;
2378        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2379         n = sa->last;
2380         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2381         for (n = n->p.other; n != NULL; n = n->p.other) {
2382             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2383         }
2384         sp_nodepath_subpath_destroy(sa);
2385         sa = t;
2386     } else if (a == sa->last) {
2387         code = (NRPathcode)sa->last->code;
2388     } else {
2389         code = NR_END;
2390         g_assert_not_reached();
2391     }
2393     if (b == sb->first) {
2394         n = sb->first;
2395         sp_node_handle_mirror_p_to_n(sa->last);
2396         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2397         sp_node_handle_mirror_n_to_p(sa->last);
2398         for (n = n->n.other; n != NULL; n = n->n.other) {
2399             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2400         }
2401     } else if (b == sb->last) {
2402         n = sb->last;
2403         sp_node_handle_mirror_p_to_n(sa->last);
2404         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2405         sp_node_handle_mirror_n_to_p(sa->last);
2406         for (n = n->p.other; n != NULL; n = n->p.other) {
2407             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2408         }
2409     } else {
2410         g_assert_not_reached();
2411     }
2412     /* and now destroy sb */
2414     sp_nodepath_subpath_destroy(sb);
2416     sp_nodepath_update_handles(sa->nodepath);
2418     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2421 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2423 /**
2424  * Internal function to handle joining two nodes.
2425  */
2426 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2428     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2430     if (g_list_length(nodepath->selected) != 2) {
2431         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2432         return;
2433     }
2435     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2436     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2438     g_assert(a != b);
2439     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2440         // someone tried to join an orphan node (i.e. a single-node subpath).
2441         // this is not worth an error message, just fail silently.
2442         return;
2443     }
2445     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2446         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2447         return;
2448     }
2450     switch(mode) {
2451         case NODE_JOIN_ENDPOINTS:
2452             do_node_selected_join(nodepath, a, b);
2453             break;
2454         case NODE_JOIN_SEGMENT:
2455             do_node_selected_join_segment(nodepath, a, b);
2456             break;
2457     }
2460 /**
2461  *  Join two nodes by merging them into one.
2462  */
2463 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2465     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2468 /**
2469  *  Join two nodes by adding a segment between them.
2470  */
2471 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2473     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2476 /**
2477  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2478  */
2479 void sp_node_delete_preserve(GList *nodes_to_delete)
2481     GSList *nodepaths = NULL;
2483     while (nodes_to_delete) {
2484         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2485         Inkscape::NodePath::SubPath *sp = node->subpath;
2486         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2487         Inkscape::NodePath::Node *sample_cursor = NULL;
2488         Inkscape::NodePath::Node *sample_end = NULL;
2489         Inkscape::NodePath::Node *delete_cursor = node;
2490         bool just_delete = false;
2492         //find the start of this contiguous selection
2493         //move left to the first node that is not selected
2494         //or the start of the non-closed path
2495         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2496             delete_cursor = curr;
2497         }
2499         //just delete at the beginning of an open path
2500         if (!delete_cursor->p.other) {
2501             sample_cursor = delete_cursor;
2502             just_delete = true;
2503         } else {
2504             sample_cursor = delete_cursor->p.other;
2505         }
2507         //calculate points for each segment
2508         int rate = 5;
2509         float period = 1.0 / rate;
2510         std::vector<Geom::Point> data;
2511         if (!just_delete) {
2512             data.push_back(sample_cursor->pos);
2513             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2514                 //just delete at the end of an open path
2515                 if (!sp->closed && curr == sp->last) {
2516                     just_delete = true;
2517                     break;
2518                 }
2520                 //sample points on the contiguous selected segment
2521                 Geom::Point *bez;
2522                 bez = new Geom::Point [4];
2523                 bez[0] = curr->pos;
2524                 bez[1] = curr->n.pos;
2525                 bez[2] = curr->n.other->p.pos;
2526                 bez[3] = curr->n.other->pos;
2527                 for (int i=1; i<rate; i++) {
2528                     gdouble t = i * period;
2529                     Geom::Point p = bezier_pt(3, bez, t);
2530                     data.push_back(p);
2531                 }
2532                 data.push_back(curr->n.other->pos);
2534                 sample_end = curr->n.other;
2535                 //break if we've come full circle or hit the end of the selection
2536                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2537                     break;
2538                 }
2539             }
2540         }
2542         if (!just_delete) {
2543             //calculate the best fitting single segment and adjust the endpoints
2544             Geom::Point *adata;
2545             adata = new Geom::Point [data.size()];
2546             copy(data.begin(), data.end(), adata);
2548             Geom::Point *bez;
2549             bez = new Geom::Point [4];
2550             //would decreasing error create a better fitting approximation?
2551             gdouble error = 1.0;
2552             gint ret;
2553             ret = Geom::bezier_fit_cubic (bez, adata, data.size(), error);
2555             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2556             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2557             //the resulting nodes behave as expected.
2558             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2559                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2560             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2561                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2563             //adjust endpoints
2564             sample_cursor->n.pos = bez[1];
2565             sample_end->p.pos = bez[2];
2566         }
2568         //destroy this contiguous selection
2569         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2570             Inkscape::NodePath::Node *temp = delete_cursor;
2571             if (delete_cursor->n.other == delete_cursor) {
2572                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2573                 delete_cursor = NULL;
2574             } else {
2575                 delete_cursor = delete_cursor->n.other;
2576             }
2577             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2578             sp_nodepath_node_destroy(temp);
2579         }
2581         sp_nodepath_update_handles(nodepath);
2583         if (!g_slist_find(nodepaths, nodepath))
2584             nodepaths = g_slist_prepend (nodepaths, nodepath);
2585     }
2587     for (GSList *i = nodepaths; i; i = i->next) {
2588         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2589         // different nodepaths will give us one undo event per nodepath
2590         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2592         // if the entire nodepath is removed, delete the selected object.
2593         if (nodepath->subpaths == NULL ||
2594             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2595             //at least 2
2596             sp_nodepath_get_node_count(nodepath) < 2) {
2597             SPDocument *document = sp_desktop_document (nodepath->desktop);
2598             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2599             //delete this nodepath's object, not the entire selection! (though at this time, this
2600             //does not matter)
2601             sp_selection_delete(nodepath->desktop);
2602             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2603                               _("Delete nodes"));
2604         } else {
2605             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2606             sp_nodepath_update_statusbar(nodepath);
2607         }
2608     }
2610     g_slist_free (nodepaths);
2613 /**
2614  * Delete one or more selected nodes.
2615  */
2616 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2618     if (!nodepath) return;
2619     if (!nodepath->selected) return;
2621     /** \todo fixme: do it the right way */
2622     while (nodepath->selected) {
2623        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2624         sp_nodepath_node_destroy(node);
2625     }
2628     //clean up the nodepath (such as for trivial subpaths)
2629     sp_nodepath_cleanup(nodepath);
2631     sp_nodepath_update_handles(nodepath);
2633     // if the entire nodepath is removed, delete the selected object.
2634     if (nodepath->subpaths == NULL ||
2635         sp_nodepath_get_node_count(nodepath) < 2) {
2636         SPDocument *document = sp_desktop_document (nodepath->desktop);
2637         sp_selection_delete(nodepath->desktop);
2638         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2639                           _("Delete nodes"));
2640         return;
2641     }
2643     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2645     sp_nodepath_update_statusbar(nodepath);
2648 /**
2649  * Delete one or more segments between two selected nodes.
2650  * This is the code for 'split'.
2651  */
2652 void
2653 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2655    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2656    Inkscape::NodePath::Node *curr, *next;     //Iterators
2658     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2660     if (g_list_length(nodepath->selected) != 2) {
2661         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2662                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2663         return;
2664     }
2666     //Selected nodes, not inclusive
2667    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2668    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2670     if ( ( a==b)                       ||  //same node
2671          (a->subpath  != b->subpath )  ||  //not the same path
2672          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2673          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2674     {
2675         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2676                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2677         return;
2678     }
2680     //###########################################
2681     //# BEGIN EDITS
2682     //###########################################
2683     //##################################
2684     //# CLOSED PATH
2685     //##################################
2686     if (a->subpath->closed) {
2689         gboolean reversed = FALSE;
2691         //Since we can go in a circle, we need to find the shorter distance.
2692         //  a->b or b->a
2693         start = end = NULL;
2694         int distance    = 0;
2695         int minDistance = 0;
2696         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2697             if (curr==b) {
2698                 //printf("a to b:%d\n", distance);
2699                 start = a;//go from a to b
2700                 end   = b;
2701                 minDistance = distance;
2702                 //printf("A to B :\n");
2703                 break;
2704             }
2705             distance++;
2706         }
2708         //try again, the other direction
2709         distance = 0;
2710         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2711             if (curr==a) {
2712                 //printf("b to a:%d\n", distance);
2713                 if (distance < minDistance) {
2714                     start    = b;  //we go from b to a
2715                     end      = a;
2716                     reversed = TRUE;
2717                     //printf("B to A\n");
2718                 }
2719                 break;
2720             }
2721             distance++;
2722         }
2725         //Copy everything from 'end' to 'start' to a new subpath
2726        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2727         for (curr=end ; curr ; curr=curr->n.other) {
2728             NRPathcode code = (NRPathcode) curr->code;
2729             if (curr == end)
2730                 code = NR_MOVETO;
2731             sp_nodepath_node_new(t, NULL,
2732                                  (Inkscape::NodePath::NodeType)curr->type, code,
2733                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2734             if (curr == start)
2735                 break;
2736         }
2737         sp_nodepath_subpath_destroy(a->subpath);
2740     }
2744     //##################################
2745     //# OPEN PATH
2746     //##################################
2747     else {
2749         //We need to get the direction of the list between A and B
2750         //Can we walk from a to b?
2751         start = end = NULL;
2752         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2753             if (curr==b) {
2754                 start = a;  //did it!  we go from a to b
2755                 end   = b;
2756                 //printf("A to B\n");
2757                 break;
2758             }
2759         }
2760         if (!start) {//didn't work?  let's try the other direction
2761             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2762                 if (curr==a) {
2763                     start = b;  //did it!  we go from b to a
2764                     end   = a;
2765                     //printf("B to A\n");
2766                     break;
2767                 }
2768             }
2769         }
2770         if (!start) {
2771             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2772                                                      _("Cannot find path between nodes."));
2773             return;
2774         }
2778         //Copy everything after 'end' to a new subpath
2779        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2780         for (curr=end ; curr ; curr=curr->n.other) {
2781             NRPathcode code = (NRPathcode) curr->code;
2782             if (curr == end)
2783                 code = NR_MOVETO;
2784             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2785                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2786         }
2788         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2789         for (curr = start->n.other ; curr  ; curr=next) {
2790             next = curr->n.other;
2791             sp_nodepath_node_destroy(curr);
2792         }
2794     }
2795     //###########################################
2796     //# END EDITS
2797     //###########################################
2799     //clean up the nodepath (such as for trivial subpaths)
2800     sp_nodepath_cleanup(nodepath);
2802     sp_nodepath_update_handles(nodepath);
2804     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2806     sp_nodepath_update_statusbar(nodepath);
2809 /**
2810  * Call sp_nodepath_set_line() for all selected segments.
2811  */
2812 void
2813 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2815     if (nodepath == NULL) return;
2817     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2818        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2819         g_assert(n->selected);
2820         if (n->p.other && n->p.other->selected) {
2821             sp_nodepath_set_line_type(n, code);
2822         }
2823     }
2825     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2828 /**
2829  * Call sp_nodepath_convert_node_type() for all selected nodes.
2830  */
2831 void
2832 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2834     if (nodepath == NULL) return;
2836     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2838     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2839         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2840     }
2842     sp_nodepath_update_repr(nodepath, _("Change node type"));
2845 /**
2846  * Change select status of node, update its own and neighbour handles.
2847  */
2848 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2850     node->selected = selected;
2852     if (selected) {
2853         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 11 : 9);
2854         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2855         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2856         sp_knot_update_ctrl(node->knot);
2857     } else {
2858         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 9 : 7);
2859         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2860         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2861         sp_knot_update_ctrl(node->knot);
2862     }
2864     sp_node_update_handles(node);
2865     if (node->n.other) sp_node_update_handles(node->n.other);
2866     if (node->p.other) sp_node_update_handles(node->p.other);
2869 /**
2870 \brief Select a node
2871 \param node     The node to select
2872 \param incremental   If true, add to selection, otherwise deselect others
2873 \param override   If true, always select this node, otherwise toggle selected status
2874 */
2875 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2877     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2879     if (incremental) {
2880         if (override) {
2881             if (!g_list_find(nodepath->selected, node)) {
2882                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2883             }
2884             sp_node_set_selected(node, TRUE);
2885         } else { // toggle
2886             if (node->selected) {
2887                 g_assert(g_list_find(nodepath->selected, node));
2888                 nodepath->selected = g_list_remove(nodepath->selected, node);
2889             } else {
2890                 g_assert(!g_list_find(nodepath->selected, node));
2891                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2892             }
2893             sp_node_set_selected(node, !node->selected);
2894         }
2895     } else {
2896         sp_nodepath_deselect(nodepath);
2897         nodepath->selected = g_list_prepend(nodepath->selected, node);
2898         sp_node_set_selected(node, TRUE);
2899     }
2901     sp_nodepath_update_statusbar(nodepath);
2905 /**
2906 \brief Deselect all nodes in the nodepath
2907 */
2908 void
2909 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2911     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2913     while (nodepath->selected) {
2914         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2915         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2916     }
2917     sp_nodepath_update_statusbar(nodepath);
2920 /**
2921 \brief Select or invert selection of all nodes in the nodepath
2922 */
2923 void
2924 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2926     if (!nodepath) return;
2928     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2929        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2930         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2931            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2932            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2933         }
2934     }
2937 /**
2938  * If nothing selected, does the same as sp_nodepath_select_all();
2939  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2940  * (i.e., similar to "select all in layer", with the "selected" subpaths
2941  * being treated as "layers" in the path).
2942  */
2943 void
2944 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2946     if (!nodepath) return;
2948     if (g_list_length (nodepath->selected) == 0) {
2949         sp_nodepath_select_all (nodepath, invert);
2950         return;
2951     }
2953     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2954     GSList *subpaths = NULL;
2956     for (GList *l = copy; l != NULL; l = l->next) {
2957         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2958         Inkscape::NodePath::SubPath *subpath = n->subpath;
2959         if (!g_slist_find (subpaths, subpath))
2960             subpaths = g_slist_prepend (subpaths, subpath);
2961     }
2963     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2964         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2965         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2966             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2967             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2968         }
2969     }
2971     g_slist_free (subpaths);
2972     g_list_free (copy);
2975 /**
2976  * \brief Select the node after the last selected; if none is selected,
2977  * select the first within path.
2978  */
2979 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2981     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2983    Inkscape::NodePath::Node *last = NULL;
2984     if (nodepath->selected) {
2985         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2986            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2987             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2988             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2989                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2990                 if (node->selected) {
2991                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2992                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2993                             if (spl->next) { // there's a next subpath
2994                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2995                                 last = subpath_next->first;
2996                             } else if (spl->prev) { // there's a previous subpath
2997                                 last = NULL; // to be set later to the first node of first subpath
2998                             } else {
2999                                 last = node->n.other;
3000                             }
3001                         } else {
3002                             last = node->n.other;
3003                         }
3004                     } else {
3005                         if (node->n.other) {
3006                             last = node->n.other;
3007                         } else {
3008                             if (spl->next) { // there's a next subpath
3009                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
3010                                 last = subpath_next->first;
3011                             } else if (spl->prev) { // there's a previous subpath
3012                                 last = NULL; // to be set later to the first node of first subpath
3013                             } else {
3014                                 last = (Inkscape::NodePath::Node *) subpath->first;
3015                             }
3016                         }
3017                     }
3018                 }
3019             }
3020         }
3021         sp_nodepath_deselect(nodepath);
3022     }
3024     if (last) { // there's at least one more node after selected
3025         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3026     } else { // no more nodes, select the first one in first subpath
3027        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
3028         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
3029     }
3032 /**
3033  * \brief Select the node before the first selected; if none is selected,
3034  * select the last within path
3035  */
3036 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
3038     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
3040    Inkscape::NodePath::Node *last = NULL;
3041     if (nodepath->selected) {
3042         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
3043            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3044             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
3045                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3046                 if (node->selected) {
3047                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
3048                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
3049                             if (spl->prev) { // there's a prev subpath
3050                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3051                                 last = subpath_prev->last;
3052                             } else if (spl->next) { // there's a next subpath
3053                                 last = NULL; // to be set later to the last node of last subpath
3054                             } else {
3055                                 last = node->p.other;
3056                             }
3057                         } else {
3058                             last = node->p.other;
3059                         }
3060                     } else {
3061                         if (node->p.other) {
3062                             last = node->p.other;
3063                         } else {
3064                             if (spl->prev) { // there's a prev subpath
3065                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3066                                 last = subpath_prev->last;
3067                             } else if (spl->next) { // there's a next subpath
3068                                 last = NULL; // to be set later to the last node of last subpath
3069                             } else {
3070                                 last = (Inkscape::NodePath::Node *) subpath->last;
3071                             }
3072                         }
3073                     }
3074                 }
3075             }
3076         }
3077         sp_nodepath_deselect(nodepath);
3078     }
3080     if (last) { // there's at least one more node before selected
3081         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3082     } else { // no more nodes, select the last one in last subpath
3083         GList *spl = g_list_last(nodepath->subpaths);
3084        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3085         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
3086     }
3089 /**
3090  * \brief Select all nodes that are within the rectangle.
3091  */
3092 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, Geom::Rect const &b, gboolean incremental)
3094     if (!incremental) {
3095         sp_nodepath_deselect(nodepath);
3096     }
3098     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3099        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3100         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3101            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3103             if (b.contains(node->pos)) {
3104                 sp_nodepath_node_select(node, TRUE, TRUE);
3105             }
3106         }
3107     }
3111 void
3112 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3114     g_assert (n);
3115     g_assert (nodepath);
3116     g_assert (n->subpath->nodepath == nodepath);
3118     if (g_list_length (nodepath->selected) == 0) {
3119         if (grow > 0) {
3120             sp_nodepath_node_select(n, TRUE, TRUE);
3121         }
3122         return;
3123     }
3125     if (g_list_length (nodepath->selected) == 1) {
3126         if (grow < 0) {
3127             sp_nodepath_deselect (nodepath);
3128             return;
3129         }
3130     }
3132         double n_sel_range = 0, p_sel_range = 0;
3133             Inkscape::NodePath::Node *farthest_n_node = n;
3134             Inkscape::NodePath::Node *farthest_p_node = n;
3136         // Calculate ranges
3137         {
3138             double n_range = 0, p_range = 0;
3139             bool n_going = true, p_going = true;
3140             Inkscape::NodePath::Node *n_node = n;
3141             Inkscape::NodePath::Node *p_node = n;
3142             do {
3143                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3144                 if (n_node && n_going)
3145                     n_node = n_node->n.other;
3146                 if (n_node == NULL) {
3147                     n_going = false;
3148                 } else {
3149                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3150                     if (n_node->selected) {
3151                         n_sel_range = n_range;
3152                         farthest_n_node = n_node;
3153                     }
3154                     if (n_node == p_node) {
3155                         n_going = false;
3156                         p_going = false;
3157                     }
3158                 }
3159                 if (p_node && p_going)
3160                     p_node = p_node->p.other;
3161                 if (p_node == NULL) {
3162                     p_going = false;
3163                 } else {
3164                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3165                     if (p_node->selected) {
3166                         p_sel_range = p_range;
3167                         farthest_p_node = p_node;
3168                     }
3169                     if (p_node == n_node) {
3170                         n_going = false;
3171                         p_going = false;
3172                     }
3173                 }
3174             } while (n_going || p_going);
3175         }
3177     if (grow > 0) {
3178         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3179                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3180         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3181                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3182         }
3183     } else {
3184         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3185                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3186         } else if (farthest_p_node && farthest_p_node->selected) {
3187                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3188         }
3189     }
3192 void
3193 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3195     g_assert (n);
3196     g_assert (nodepath);
3197     g_assert (n->subpath->nodepath == nodepath);
3199     if (g_list_length (nodepath->selected) == 0) {
3200         if (grow > 0) {
3201             sp_nodepath_node_select(n, TRUE, TRUE);
3202         }
3203         return;
3204     }
3206     if (g_list_length (nodepath->selected) == 1) {
3207         if (grow < 0) {
3208             sp_nodepath_deselect (nodepath);
3209             return;
3210         }
3211     }
3213     Inkscape::NodePath::Node *farthest_selected = NULL;
3214     double farthest_dist = 0;
3216     Inkscape::NodePath::Node *closest_unselected = NULL;
3217     double closest_dist = NR_HUGE;
3219     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3220        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3221         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3222            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3223            if (node == n)
3224                continue;
3225            if (node->selected) {
3226                if (Geom::L2(node->pos - n->pos) > farthest_dist) {
3227                    farthest_dist = Geom::L2(node->pos - n->pos);
3228                    farthest_selected = node;
3229                }
3230            } else {
3231                if (Geom::L2(node->pos - n->pos) < closest_dist) {
3232                    closest_dist = Geom::L2(node->pos - n->pos);
3233                    closest_unselected = node;
3234                }
3235            }
3236         }
3237     }
3239     if (grow > 0) {
3240         if (closest_unselected) {
3241             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3242         }
3243     } else {
3244         if (farthest_selected) {
3245             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3246         }
3247     }
3251 /**
3252 \brief  Saves all nodes' and handles' current positions in their origin members
3253 */
3254 void
3255 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3257     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3258        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3259         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3260            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3261            n->origin = n->pos;
3262            n->p.origin = n->p.pos;
3263            n->n.origin = n->n.pos;
3264         }
3265     }
3268 /**
3269 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3270 */
3271 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3273     GList *r = NULL;
3274     if (nodepath->selected) {
3275         guint i = 0;
3276         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3277             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3278             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3279                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3280                 i++;
3281                 if (node->selected) {
3282                     r = g_list_append(r, GINT_TO_POINTER(i));
3283                 }
3284             }
3285         }
3286     }
3287     return r;
3290 /**
3291 \brief  Restores selection by selecting nodes whose positions are in the list
3292 */
3293 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3295     sp_nodepath_deselect(nodepath);
3297     guint i = 0;
3298     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3299        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3300         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3301            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3302             i++;
3303             if (g_list_find(r, GINT_TO_POINTER(i))) {
3304                 sp_nodepath_node_select(node, TRUE, TRUE);
3305             }
3306         }
3307     }
3311 /**
3312 \brief Adjusts handle according to node type and line code.
3313 */
3314 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3316     g_assert(node);
3318     // nothing to do for auto nodes (sp_node_adjust_handles() does the job)
3319     if (node->type == Inkscape::NodePath::NODE_AUTO)
3320         return;
3322    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3323    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3325    // nothing to do if we are an end node
3326     if (me->other == NULL) return;
3327     if (other->other == NULL) return;
3329     // nothing to do if we are a cusp node
3330     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3332     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3333     NRPathcode mecode;
3334     if (which_adjust == 1) {
3335         mecode = (NRPathcode)me->other->code;
3336     } else {
3337         mecode = (NRPathcode)node->code;
3338     }
3339     if (mecode == NR_LINETO) return;
3341     if (sp_node_side_is_line(node, other)) {
3342         // other is a line, and we are either smooth or symm
3343        Inkscape::NodePath::Node *othernode = other->other;
3344         double len = Geom::L2(me->pos - node->pos);
3345         Geom::Point delta = node->pos - othernode->pos;
3346         double linelen = Geom::L2(delta);
3347         if (linelen < 1e-18)
3348             return;
3349         me->pos = node->pos + (len / linelen)*delta;
3350         return;
3351     }
3353     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3354         // symmetrize
3355         me->pos = 2 * node->pos - other->pos;
3356         return;
3357     } else {
3358         // smoothify
3359         double len = Geom::L2(me->pos - node->pos);
3360         Geom::Point delta = other->pos - node->pos;
3361         double otherlen = Geom::L2(delta);
3362         if (otherlen < 1e-18) return;
3363         me->pos = node->pos - (len / otherlen) * delta;
3364     }
3367 /**
3368  \brief Adjusts both handles according to node type and line code
3369  */
3370 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3372     g_assert(node);
3374     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3376     /* we are either smooth or symm */
3378     if (node->p.other == NULL) return;
3379     if (node->n.other == NULL) return;
3381     if (node->type == Inkscape::NodePath::NODE_AUTO) {
3382         sp_node_adjust_handles_auto(node);
3383         return;
3384     }
3386     if (sp_node_side_is_line(node, &node->p)) {
3387         sp_node_adjust_handle(node, 1);
3388         return;
3389     }
3391     if (sp_node_side_is_line(node, &node->n)) {
3392         sp_node_adjust_handle(node, -1);
3393         return;
3394     }
3396     /* both are curves */
3397     Geom::Point const delta( node->n.pos - node->p.pos );
3399     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3400         node->p.pos = node->pos - delta / 2;
3401         node->n.pos = node->pos + delta / 2;
3402         return;
3403     }
3405     /* We are smooth */
3406     double plen = Geom::L2(node->p.pos - node->pos);
3407     if (plen < 1e-18) return;
3408     double nlen = Geom::L2(node->n.pos - node->pos);
3409     if (nlen < 1e-18) return;
3410     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3411     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3414 static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node)
3416     if (node->p.other == NULL || node->n.other == NULL) {
3417         node->p.pos = node->pos;
3418         node->n.pos = node->pos;
3419         return;
3420     }
3422     Geom::Point leg_prev = to_2geom(node->p.other->pos - node->pos);
3423     Geom::Point leg_next = to_2geom(node->n.other->pos - node->pos);
3425     double norm_leg_prev = Geom::L2(leg_prev);
3426     double norm_leg_next = Geom::L2(leg_next);
3428     Geom::Point delta;
3429     if (norm_leg_next > 0.0) {
3430         delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
3431         delta.normalize();
3432     }
3434     node->p.pos = node->pos - norm_leg_prev / 3 * delta;
3435     node->n.pos = node->pos + norm_leg_next / 3 * delta;
3438 /**
3439  * Node event callback.
3440  */
3441 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3443     gboolean ret = FALSE;
3444     switch (event->type) {
3445         case GDK_ENTER_NOTIFY:
3446             Inkscape::NodePath::Path::active_node = n;
3447             break;
3448         case GDK_LEAVE_NOTIFY:
3449             Inkscape::NodePath::Path::active_node = NULL;
3450             break;
3451         case GDK_SCROLL:
3452             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3453                 switch (event->scroll.direction) {
3454                     case GDK_SCROLL_UP:
3455                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3456                         break;
3457                     case GDK_SCROLL_DOWN:
3458                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3459                         break;
3460                     default:
3461                         break;
3462                 }
3463                 ret = TRUE;
3464             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3465                 switch (event->scroll.direction) {
3466                     case GDK_SCROLL_UP:
3467                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3468                         break;
3469                     case GDK_SCROLL_DOWN:
3470                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3471                         break;
3472                     default:
3473                         break;
3474                 }
3475                 ret = TRUE;
3476             }
3477             break;
3478         case GDK_KEY_PRESS:
3479             switch (get_group0_keyval (&event->key)) {
3480                 case GDK_space:
3481                     if (event->key.state & GDK_BUTTON1_MASK) {
3482                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3483                         stamp_repr(nodepath);
3484                         ret = TRUE;
3485                     }
3486                     break;
3487                 case GDK_Page_Up:
3488                     if (event->key.state & GDK_CONTROL_MASK) {
3489                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3490                     } else {
3491                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3492                     }
3493                     break;
3494                 case GDK_Page_Down:
3495                     if (event->key.state & GDK_CONTROL_MASK) {
3496                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3497                     } else {
3498                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3499                     }
3500                     break;
3501                 default:
3502                     break;
3503             }
3504             break;
3505         default:
3506             break;
3507     }
3509     return ret;
3512 /**
3513  * Handle keypress on node; directly called.
3514  */
3515 gboolean node_key(GdkEvent *event)
3517     Inkscape::NodePath::Path *np;
3519     // there is no way to verify nodes so set active_node to nil when deleting!!
3520     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3522     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3523         gint ret = FALSE;
3524         switch (get_group0_keyval (&event->key)) {
3525             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3526             case GDK_BackSpace:
3527                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3528                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3529                 sp_nodepath_update_repr(np, _("Delete node"));
3530                 Inkscape::NodePath::Path::active_node = NULL;
3531                 ret = TRUE;
3532                 break;
3533             case GDK_c:
3534                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3535                 ret = TRUE;
3536                 break;
3537             case GDK_s:
3538                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3539                 ret = TRUE;
3540                 break;
3541             case GDK_a:
3542                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_AUTO);
3543                 ret = TRUE;
3544                 break;
3545             case GDK_y:
3546                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3547                 ret = TRUE;
3548                 break;
3549             case GDK_b:
3550                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3551                 ret = TRUE;
3552                 break;
3553         }
3554         return ret;
3555     }
3556     return FALSE;
3559 /**
3560  * Mouseclick on node callback.
3561  */
3562 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3564    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3566     if (state & GDK_CONTROL_MASK) {
3567         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3569         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3570             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3571                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3572             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3573                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3574             } else if (n->type == Inkscape::NodePath::NODE_SYMM) {
3575                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_AUTO);
3576             } else {
3577                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3578             }
3579             sp_nodepath_update_repr(nodepath, _("Change node type"));
3580             sp_nodepath_update_statusbar(nodepath);
3582         } else { //ctrl+alt+click: delete node
3583             GList *node_to_delete = NULL;
3584             node_to_delete = g_list_append(node_to_delete, n);
3585             sp_node_delete_preserve(node_to_delete);
3586         }
3588     } else {
3589         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3590     }
3593 /**
3594  * Mouse grabbed node callback.
3595  */
3596 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3598    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3600     if (!n->selected) {
3601         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3602     }
3604     n->is_dragging = true;
3605     // Reconstruct and store the location of the mouse pointer at the time when we started dragging (needed for snapping)
3606     n->subpath->nodepath->drag_origin_mouse = knot->grabbed_rel_pos + knot->drag_origin;
3608     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3610     sp_nodepath_remember_origins (n->subpath->nodepath);
3613 /**
3614  * Mouse ungrabbed node callback.
3615  */
3616 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3618    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3620    n->dragging_out = NULL;
3621    n->is_dragging = false;
3622    n->subpath->nodepath->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
3623    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3625    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3628 /**
3629  * The point on a line, given by its angle, closest to the given point.
3630  * \param p  A point.
3631  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3632  * \param closest  Pointer to the point struct where the result is stored.
3633  * \todo FIXME: use dot product perhaps?
3634  */
3635 static void point_line_closest(Geom::Point *p, double a, Geom::Point *closest)
3637     if (a == HUGE_VAL) { // vertical
3638         *closest = Geom::Point(0, (*p)[Geom::Y]);
3639     } else {
3640         (*closest)[Geom::X] = ( a * (*p)[Geom::Y] + (*p)[Geom::X]) / (a*a + 1);
3641         (*closest)[Geom::Y] = a * (*closest)[Geom::X];
3642     }
3645 /**
3646  * Distance from the point to a line given by its angle.
3647  * \param p  A point.
3648  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3649  */
3650 static double point_line_distance(Geom::Point *p, double a)
3652     Geom::Point c;
3653     point_line_closest(p, a, &c);
3654     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]));
3657 /**
3658  * Callback for node "request" signal.
3659  * \todo fixme: This goes to "moved" event? (lauris)
3660  */
3661 static gboolean
3662 node_request(SPKnot */*knot*/, Geom::Point const &p, guint state, gpointer data)
3664     double yn, xn, yp, xp;
3665     double an, ap, na, pa;
3666     double d_an, d_ap, d_na, d_pa;
3667     gboolean collinear = FALSE;
3668     Geom::Point c;
3669     Geom::Point pr;
3671     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3673     n->subpath->nodepath->desktop->snapindicator->remove_snaptarget();
3675     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3676     if ( (!n->subpath->nodepath->straight_path) &&
3677          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3678            || n->dragging_out ) )
3679     {
3680        Geom::Point mouse = p;
3682        if (!n->dragging_out) {
3683            // This is the first drag-out event; find out which handle to drag out
3684            double appr_n = (n->n.other ? Geom::L2(n->n.other->pos - n->pos) - Geom::L2(n->n.other->pos - p) : -HUGE_VAL);
3685            double appr_p = (n->p.other ? Geom::L2(n->p.other->pos - n->pos) - Geom::L2(n->p.other->pos - p) : -HUGE_VAL);
3687            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3688                return FALSE;
3690            Inkscape::NodePath::NodeSide *opposite;
3691            if (appr_p > appr_n) { // closer to p
3692                n->dragging_out = &n->p;
3693                opposite = &n->n;
3694                n->code = NR_CURVETO;
3695            } else if (appr_p < appr_n) { // closer to n
3696                n->dragging_out = &n->n;
3697                opposite = &n->p;
3698                n->n.other->code = NR_CURVETO;
3699            } else { // p and n nodes are the same
3700                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3701                    n->dragging_out = &n->p;
3702                    opposite = &n->n;
3703                    n->code = NR_CURVETO;
3704                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3705                    n->dragging_out = &n->n;
3706                    opposite = &n->p;
3707                    n->n.other->code = NR_CURVETO;
3708                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3709                    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);
3710                    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);
3711                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3712                        n->dragging_out = &n->n;
3713                        opposite = &n->p;
3714                        n->n.other->code = NR_CURVETO;
3715                    } else { // closer to other's n handle
3716                        n->dragging_out = &n->p;
3717                        opposite = &n->n;
3718                        n->code = NR_CURVETO;
3719                    }
3720                }
3721            }
3723            // if there's another handle, make sure the one we drag out starts parallel to it
3724            if (opposite->pos != n->pos) {
3725                mouse = n->pos - Geom::L2(mouse - n->pos) * Geom::unit_vector(opposite->pos - n->pos);
3726            }
3728            // knots might not be created yet!
3729            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3730            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3731        }
3733        // pass this on to the handle-moved callback
3734        node_handle_moved(n->dragging_out->knot, mouse, state, (gpointer) n);
3735        sp_node_update_handles(n);
3736        return TRUE;
3737    }
3739     if (state & GDK_CONTROL_MASK) { // constrained motion
3741         // calculate relative distances of handles
3742         // n handle:
3743         yn = n->n.pos[Geom::Y] - n->pos[Geom::Y];
3744         xn = n->n.pos[Geom::X] - n->pos[Geom::X];
3745         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3746         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3747             if (n->n.other) { // if there is the next point
3748                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3749                     yn = n->n.other->origin[Geom::Y] - n->origin[Geom::Y]; // use origin because otherwise the direction will change as you drag
3750                     xn = n->n.other->origin[Geom::X] - n->origin[Geom::X];
3751             }
3752         }
3753         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3754         if (yn < 0) { xn = -xn; yn = -yn; }
3756         // p handle:
3757         yp = n->p.pos[Geom::Y] - n->pos[Geom::Y];
3758         xp = n->p.pos[Geom::X] - n->pos[Geom::X];
3759         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3760         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3761             if (n->p.other) {
3762                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3763                     yp = n->p.other->origin[Geom::Y] - n->origin[Geom::Y];
3764                     xp = n->p.other->origin[Geom::X] - n->origin[Geom::X];
3765             }
3766         }
3767         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3768         if (yp < 0) { xp = -xp; yp = -yp; }
3770         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3771             // sliding on handles, only if at least one of the handles is non-vertical
3772             // (otherwise it's the same as ctrl+drag anyway)
3774             // calculate angles of the handles
3775             if (xn == 0) {
3776                 if (yn == 0) { // no handle, consider it the continuation of the other one
3777                     an = 0;
3778                     collinear = TRUE;
3779                 }
3780                 else an = 0; // vertical; set the angle to horizontal
3781             } else an = yn/xn;
3783             if (xp == 0) {
3784                 if (yp == 0) { // no handle, consider it the continuation of the other one
3785                     ap = an;
3786                 }
3787                 else ap = 0; // vertical; set the angle to horizontal
3788             } else  ap = yp/xp;
3790             if (collinear) an = ap;
3792             // angles of the perpendiculars; HUGE_VAL means vertical
3793             if (an == 0) na = HUGE_VAL; else na = -1/an;
3794             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3796             // mouse point relative to the node's original pos
3797             pr = p - n->origin;
3799             // distances to the four lines (two handles and two perpendiculars)
3800             d_an = point_line_distance(&pr, an);
3801             d_na = point_line_distance(&pr, na);
3802             d_ap = point_line_distance(&pr, ap);
3803             d_pa = point_line_distance(&pr, pa);
3805             // find out which line is the closest, save its closest point in c
3806             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3807                 point_line_closest(&pr, an, &c);
3808             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3809                 point_line_closest(&pr, ap, &c);
3810             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3811                 point_line_closest(&pr, na, &c);
3812             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3813                 point_line_closest(&pr, pa, &c);
3814             }
3816             // move the node to the closest point
3817             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3818                                             n->origin[Geom::X] + c[Geom::X] - n->pos[Geom::X],
3819                                             n->origin[Geom::Y] + c[Geom::Y] - n->pos[Geom::Y],
3820                                             true);
3822         } else {  // constraining to hor/vert
3824             if (fabs(p[Geom::X] - n->origin[Geom::X]) > fabs(p[Geom::Y] - n->origin[Geom::Y])) { // snap to hor
3825                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3826                                                 p[Geom::X] - n->pos[Geom::X],
3827                                                 n->origin[Geom::Y] - n->pos[Geom::Y],
3828                                                 true,
3829                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::X]));
3830             } else { // snap to vert
3831                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3832                                                 n->origin[Geom::X] - n->pos[Geom::X],
3833                                                 p[Geom::Y] - n->pos[Geom::Y],
3834                                                 true,
3835                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::Y]));
3836             }
3837         }
3838     } else { // move freely
3839         if (n->is_dragging) {
3840             if (state & GDK_MOD1_MASK) { // sculpt
3841                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, p - n->origin);
3842             } else {
3843                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3844                                             p[Geom::X] - n->pos[Geom::X],
3845                                             p[Geom::Y] - n->pos[Geom::Y],
3846                                             (state & GDK_SHIFT_MASK) == 0);
3847             }
3848         }
3849     }
3851     n->subpath->nodepath->desktop->scroll_to_point(p);
3853     return TRUE;
3856 /**
3857  * Node handle clicked callback.
3858  */
3859 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3861    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3863     if (state & GDK_CONTROL_MASK) { // "delete" handle
3864         if (n->p.knot == knot) {
3865             n->p.pos = n->pos;
3866         } else if (n->n.knot == knot) {
3867             n->n.pos = n->pos;
3868         }
3869         sp_node_update_handles(n);
3870         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3871         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3872         sp_nodepath_update_statusbar(nodepath);
3874     } else { // just select or add to selection, depending in Shift
3875         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3876     }
3879 /**
3880  * Node handle grabbed callback.
3881  */
3882 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3884    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3886     // convert auto -> smooth when dragging handle
3887    if (n->type == Inkscape::NodePath::NODE_AUTO) {
3888         n->type = Inkscape::NodePath::NODE_SMOOTH;
3889         sp_nodepath_update_node_knot (n);
3890    }
3892     if (!n->selected) {
3893         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3894     }
3896     // remember the origin point of the handle
3897     if (n->p.knot == knot) {
3898         n->p.origin_radial = n->p.pos - n->pos;
3899     } else if (n->n.knot == knot) {
3900         n->n.origin_radial = n->n.pos - n->pos;
3901     } else {
3902         g_assert_not_reached();
3903     }
3905     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3908 /**
3909  * Node handle ungrabbed callback.
3910  */
3911 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3913    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3915     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3916     if (n->p.knot == knot) {
3917         n->p.origin_radial.a = 0;
3918         sp_knot_set_position(knot, n->p.pos, state);
3919     } else if (n->n.knot == knot) {
3920         n->n.origin_radial.a = 0;
3921         sp_knot_set_position(knot, n->n.pos, state);
3922     } else {
3923         g_assert_not_reached();
3924     }
3926     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3929 /**
3930  * Node handle "request" signal callback.
3931  */
3932 static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, gpointer data)
3934     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3936     Inkscape::NodePath::NodeSide *me, *opposite;
3937     gint which;
3938     if (n->p.knot == knot) {
3939         me = &n->p;
3940         opposite = &n->n;
3941         which = -1;
3942     } else if (n->n.knot == knot) {
3943         me = &n->n;
3944         opposite = &n->p;
3945         which = 1;
3946     } else {
3947         me = opposite = NULL;
3948         which = 0;
3949         g_assert_not_reached();
3950     }
3952     SPDesktop *desktop = n->subpath->nodepath->desktop;
3953     SnapManager &m = desktop->namedview->snap_manager;
3954     m.setup(desktop, true, n->subpath->nodepath->item);
3955     Inkscape::SnappedPoint s;
3957     if ((state & GDK_SHIFT_MASK) != 0) {
3958         // We will not try to snap when the shift-key is pressed
3959         // so remove the old snap indicator and don't wait for it to time-out
3960         desktop->snapindicator->remove_snaptarget();
3961     }
3963     Inkscape::NodePath::Node *othernode = opposite->other;
3964     Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP);
3965     if (othernode) {
3966         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3967             /* We are smooth node adjacent with line */
3968             Geom::Point const delta = p - n->pos;
3969             Geom::Coord const len = Geom::L2(delta);
3970             Inkscape::NodePath::Node *othernode = opposite->other;
3971             Geom::Point const ndelta = n->pos - othernode->pos;
3972             Geom::Coord const linelen = Geom::L2(ndelta);
3973             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3974                 Geom::Coord const scal = dot(delta, ndelta) / linelen;
3975                 p = n->pos + (scal / linelen) * ndelta;
3976             }
3977             if ((state & GDK_SHIFT_MASK) == 0) {
3978                 s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, Inkscape::SnapCandidatePoint(p, source_type), Inkscape::Snapper::ConstraintLine(p, ndelta));
3979             }
3980         } else {
3981             if ((state & GDK_SHIFT_MASK) == 0) {
3982                 s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, Inkscape::SnapCandidatePoint(p, source_type));
3983             }
3984         }
3985     } else {
3986         if ((state & GDK_SHIFT_MASK) == 0) {
3987             s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, Inkscape::SnapCandidatePoint(p, source_type));
3988         }
3989     }
3991     s.getPoint(p);
3993     sp_node_adjust_handle(n, -which);
3995     return FALSE;
3998 /**
3999  * Node handle moved callback.
4000  */
4001 static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data)
4003    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
4004    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4006    Inkscape::NodePath::NodeSide *me;
4007    Inkscape::NodePath::NodeSide *other;
4008     if (n->p.knot == knot) {
4009         me = &n->p;
4010         other = &n->n;
4011     } else if (n->n.knot == knot) {
4012         me = &n->n;
4013         other = &n->p;
4014     } else {
4015         me = NULL;
4016         other = NULL;
4017         g_assert_not_reached();
4018     }
4020     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
4021     Radial rme(me->pos - n->pos);
4022     Radial rother(other->pos - n->pos);
4023     Radial rnew(p - n->pos);
4025     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
4026         int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
4027         /* 0 interpreted as "no snapping". */
4029         // 1. Snap to the closest PI/snaps angle, starting from zero.
4030         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
4032         // 2. Snap to the original angle, its opposite and perpendiculars
4033         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
4034             /* The closest PI/2 angle, starting from original angle */
4035             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
4037             // Snap to the closest.
4038             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
4039                        ? a_snapped
4040                        : a_ortho );
4041         }
4043         // 3. Snap to the angle of the opposite line, if any
4044         Inkscape::NodePath::Node *othernode = other->other;
4045         if (othernode) {
4046             Geom::Point other_to_snap(0,0);
4047             if (sp_node_side_is_line(n, other)) {
4048                 other_to_snap = othernode->pos - n->pos;
4049             } else {
4050                 other_to_snap = other->pos - n->pos;
4051             }
4052             if (Geom::L2(other_to_snap) > 1e-3) {
4053                 Radial rother_to_snap(other_to_snap);
4054                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
4055                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
4057                 // Snap to the closest.
4058                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
4059                        ? a_snapped
4060                        : a_oppo );
4061             }
4062         }
4064         rnew.a = a_snapped;
4065     }
4067     if (state & GDK_MOD1_MASK) {
4068         // lock handle length
4069         rnew.r = me->origin_radial.r;
4070     }
4072     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (!n->dragging_out && (state & GDK_SHIFT_MASK)))
4073         && (rme.a != HUGE_VAL) && (rnew.a != HUGE_VAL) && ((fabs(rme.a - rnew.a) > 0.001) || (n->type ==Inkscape::NodePath::NODE_SYMM))) {
4074         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
4075         rother.a += rnew.a - rme.a;
4076         other->pos = Geom::Point(rother) + n->pos;
4077         if (other->knot) {
4078             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
4079             sp_knot_moveto(other->knot, other->pos);
4080         }
4081     }
4083     me->pos = Geom::Point(rnew) + n->pos;
4084     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
4086     // move knot, but without emitting the signal:
4087     // we cannot emit a "moved" signal because we're now processing it
4088     sp_knot_moveto(me->knot, me->pos);
4090     update_object(n->subpath->nodepath);
4092     /* status text */
4093     SPDesktop *desktop = n->subpath->nodepath->desktop;
4094     if (!desktop) return;
4095     SPEventContext *ec = desktop->event_context;
4096     if (!ec) return;
4098     Inkscape::MessageContext *mc = get_message_context(ec);
4100     if (!mc) return;
4102     double degrees = 180 / M_PI * rnew.a;
4103     if (degrees > 180) degrees -= 360;
4104     if (degrees < -180) degrees += 360;
4105     if (prefs->getBool("/options/compassangledisplay/value"))
4106         degrees = angle_to_compass (degrees);
4108     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
4110     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
4111          _("<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);
4113     g_string_free(length, TRUE);
4116 /**
4117  * Node handle event callback.
4118  */
4119 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
4121     gboolean ret = FALSE;
4122     switch (event->type) {
4123         case GDK_KEY_PRESS:
4124             switch (get_group0_keyval (&event->key)) {
4125                 case GDK_space:
4126                     if (event->key.state & GDK_BUTTON1_MASK) {
4127                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
4128                         stamp_repr(nodepath);
4129                         ret = TRUE;
4130                     }
4131                     break;
4132                 default:
4133                     break;
4134             }
4135             break;
4136         case GDK_ENTER_NOTIFY:
4137             // we use an experimentally determined threshold that seems to work fine
4138             if (Geom::L2(n->pos - knot->pos) < 0.75)
4139                 Inkscape::NodePath::Path::active_node = n;
4140             break;
4141         case GDK_LEAVE_NOTIFY:
4142             // we use an experimentally determined threshold that seems to work fine
4143             if (Geom::L2(n->pos - knot->pos) < 0.75)
4144                 Inkscape::NodePath::Path::active_node = NULL;
4145             break;
4146         default:
4147             break;
4148     }
4150     return ret;
4153 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
4154                                  Radial &rme, Radial &rother, gboolean const both)
4156     rme.a += angle;
4157     if ( both
4158          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4159          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4160     {
4161         rother.a += angle;
4162     }
4165 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
4166                                         Radial &rme, Radial &rother, gboolean const both)
4168     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
4170     gdouble r;
4171     if ( both
4172          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4173          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4174     {
4175         r = MAX(rme.r, rother.r);
4176     } else {
4177         r = rme.r;
4178     }
4180     gdouble const weird_angle = atan2(norm_angle, r);
4181 /* Bulia says norm_angle is just the visible distance that the
4182  * object's end must travel on the screen.  Left as 'angle' for want of
4183  * a better name.*/
4185     rme.a += weird_angle;
4186     if ( both
4187          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4188          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4189     {
4190         rother.a += weird_angle;
4191     }
4194 /**
4195  * Rotate one node.
4196  */
4197 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4199     Inkscape::NodePath::NodeSide *me, *other;
4200     bool both = false;
4202     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4203     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4205     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4206         me = &(n->p);
4207         other = &(n->n);
4208     } else if (!n->p.other) {
4209         me = &(n->n);
4210         other = &(n->p);
4211     } else {
4212         if (which > 0) { // right 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 if (which < 0){ // left handle
4221             if (xn <= xp) {
4222                 me = &(n->n);
4223                 other = &(n->p);
4224             } else {
4225                 me = &(n->p);
4226                 other = &(n->n);
4227             }
4228         } else { // both handles
4229             me = &(n->n);
4230             other = &(n->p);
4231             both = true;
4232         }
4233     }
4235     Radial rme(me->pos - n->pos);
4236     Radial rother(other->pos - n->pos);
4238     if (screen) {
4239         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4240     } else {
4241         node_rotate_one_internal (*n, angle, rme, rother, both);
4242     }
4244     me->pos = n->pos + Geom::Point(rme);
4246     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4247         other->pos =  n->pos + Geom::Point(rother);
4248     }
4250     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4251     // so here we just move all the knots without emitting move signals, for speed
4252     sp_node_update_handles(n, false);
4255 /**
4256  * Rotate selected nodes.
4257  */
4258 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4260     if (!nodepath || !nodepath->selected) return;
4262     if (g_list_length(nodepath->selected) == 1) {
4263        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4264         node_rotate_one (n, angle, which, screen);
4265     } else {
4266        // rotate as an object:
4268         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4269         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4270         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4271             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4272             box.expandTo (n->pos); // contain all selected nodes
4273         }
4275         gdouble rot;
4276         if (screen) {
4277             gdouble const zoom = nodepath->desktop->current_zoom();
4278             gdouble const zmove = angle / zoom;
4279             gdouble const r = Geom::L2(box.max() - box.midpoint());
4280             rot = atan2(zmove, r);
4281         } else {
4282             rot = angle;
4283         }
4285         Geom::Point rot_center;
4286         if (Inkscape::NodePath::Path::active_node == NULL)
4287             rot_center = box.midpoint();
4288         else
4289             rot_center = Inkscape::NodePath::Path::active_node->pos;
4291         Geom::Matrix t =
4292             Geom::Matrix (Geom::Translate(-rot_center)) *
4293             Geom::Matrix (Geom::Rotate(rot)) *
4294             Geom::Matrix (Geom::Translate(rot_center));
4296         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4297             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4298             n->pos *= t;
4299             n->n.pos *= t;
4300             n->p.pos *= t;
4301             sp_node_update_handles(n, false);
4302         }
4303     }
4305     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4308 /**
4309  * Scale one node.
4310  */
4311 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4313     bool both = false;
4314     Inkscape::NodePath::NodeSide *me, *other;
4316     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4317     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4319     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4320         me = &(n->p);
4321         other = &(n->n);
4322         n->code = NR_CURVETO;
4323     } else if (!n->p.other) {
4324         me = &(n->n);
4325         other = &(n->p);
4326         if (n->n.other)
4327             n->n.other->code = NR_CURVETO;
4328     } else {
4329         if (which > 0) { // right handle
4330             if (xn > xp) {
4331                 me = &(n->n);
4332                 other = &(n->p);
4333                 if (n->n.other)
4334                     n->n.other->code = NR_CURVETO;
4335             } else {
4336                 me = &(n->p);
4337                 other = &(n->n);
4338                 n->code = NR_CURVETO;
4339             }
4340         } else if (which < 0){ // left handle
4341             if (xn <= xp) {
4342                 me = &(n->n);
4343                 other = &(n->p);
4344                 if (n->n.other)
4345                     n->n.other->code = NR_CURVETO;
4346             } else {
4347                 me = &(n->p);
4348                 other = &(n->n);
4349                 n->code = NR_CURVETO;
4350             }
4351         } else { // both handles
4352             me = &(n->n);
4353             other = &(n->p);
4354             both = true;
4355             n->code = NR_CURVETO;
4356             if (n->n.other)
4357                 n->n.other->code = NR_CURVETO;
4358         }
4359     }
4361     Radial rme(me->pos - n->pos);
4362     Radial rother(other->pos - n->pos);
4364     rme.r += grow;
4365     if (rme.r < 0) rme.r = 0;
4366     if (rme.a == HUGE_VAL) {
4367         if (me->other) { // if direction is unknown, initialize it towards the next node
4368             Radial rme_next(me->other->pos - n->pos);
4369             rme.a = rme_next.a;
4370         } else { // if there's no next, initialize to 0
4371             rme.a = 0;
4372         }
4373     }
4374     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4375         rother.r += grow;
4376         if (rother.r < 0) rother.r = 0;
4377         if (rother.a == HUGE_VAL) {
4378             rother.a = rme.a + M_PI;
4379         }
4380     }
4382     me->pos = n->pos + Geom::Point(rme);
4384     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4385         other->pos = n->pos + Geom::Point(rother);
4386     }
4388     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4389     // so here we just move all the knots without emitting move signals, for speed
4390     sp_node_update_handles(n, false);
4393 /**
4394  * Scale selected nodes.
4395  */
4396 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4398     if (!nodepath || !nodepath->selected) return;
4400     if (g_list_length(nodepath->selected) == 1) {
4401         // scale handles of the single selected node
4402         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4403         node_scale_one (n, grow, which);
4404     } else {
4405         // scale nodes as an "object":
4407         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4408         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4409         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4410             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4411             box.expandTo (n->pos); // contain all selected nodes
4412         }
4414         if ( Geom::are_near(box.maxExtent(), 0) ) {
4415             SPEventContext *ec = nodepath->desktop->event_context;
4416             if (!ec) return;
4417             Inkscape::MessageContext *mc = get_message_context(ec);
4418             if (!mc) return;
4419             mc->setF(Inkscape::WARNING_MESSAGE,
4420                              _("Cannot scale nodes when all are at the same location."));
4421             return;
4422         }
4423         double scale = (box.maxExtent() + grow)/box.maxExtent();
4426         Geom::Point scale_center;
4427         if (Inkscape::NodePath::Path::active_node == NULL)
4428             scale_center = box.midpoint();
4429         else
4430             scale_center = Inkscape::NodePath::Path::active_node->pos;
4432         Geom::Matrix t =
4433             Geom::Translate(-scale_center) *
4434             Geom::Scale(scale, scale) *
4435             Geom::Translate(scale_center);
4437         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4438             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4439             n->pos *= t;
4440             n->n.pos *= t;
4441             n->p.pos *= t;
4442             sp_node_update_handles(n, false);
4443         }
4444     }
4446     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4449 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4451     if (!nodepath) return;
4452     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4455 /**
4456  * Flip selected nodes horizontally/vertically.
4457  */
4458 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis, boost::optional<Geom::Point> center)
4460     if (!nodepath || !nodepath->selected) return;
4462     if (g_list_length(nodepath->selected) == 1 && !center) {
4463         // flip handles of the single selected node
4464         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4465         double temp = n->p.pos[axis];
4466         n->p.pos[axis] = n->n.pos[axis];
4467         n->n.pos[axis] = temp;
4468         sp_node_update_handles(n, false);
4469     } else {
4470         // scale nodes as an "object":
4472         Geom::Rect box = sp_node_selected_bbox (nodepath);
4473         if (!center) {
4474             center = box.midpoint();
4475         }
4476         Geom::Matrix t =
4477             Geom::Matrix (Geom::Translate(- *center)) *
4478             Geom::Matrix ((axis == Geom::X)? Geom::Scale(-1, 1) : Geom::Scale(1, -1)) *
4479             Geom::Matrix (Geom::Translate(*center));
4481         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4482             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4483             n->pos *= t;
4484             n->n.pos *= t;
4485             n->p.pos *= t;
4486             sp_node_update_handles(n, false);
4487         }
4488     }
4490     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4493 Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4495     g_assert (nodepath->selected);
4497     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4498     Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4499     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4500         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4501         box.expandTo (n->pos); // contain all selected nodes
4502     }
4503     return box;
4506 //-----------------------------------------------
4507 /**
4508  * Return new subpath under given nodepath.
4509  */
4510 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4512     g_assert(nodepath);
4513     g_assert(nodepath->desktop);
4515    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4517     s->nodepath = nodepath;
4518     s->closed = FALSE;
4519     s->nodes = NULL;
4520     s->first = NULL;
4521     s->last = NULL;
4523     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4524     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4525     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4527     return s;
4530 /**
4531  * Destroy nodes in subpath, then subpath itself.
4532  */
4533 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4535     g_assert(subpath);
4536     g_assert(subpath->nodepath);
4537     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4539     while (subpath->nodes) {
4540         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4541     }
4543     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4545     g_free(subpath);
4548 /**
4549  * Link head to tail in subpath.
4550  */
4551 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4553     g_assert(!sp->closed);
4554     g_assert(sp->last != sp->first);
4555     g_assert(sp->first->code == NR_MOVETO);
4557     sp->closed = TRUE;
4559     //Link the head to the tail
4560     sp->first->p.other = sp->last;
4561     sp->last->n.other  = sp->first;
4562     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4563     sp->first          = sp->last;
4565     //Remove the extra end node
4566     sp_nodepath_node_destroy(sp->last->n.other);
4569 /**
4570  * Open closed (loopy) subpath at node.
4571  */
4572 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4574     g_assert(sp->closed);
4575     g_assert(n->subpath == sp);
4576     g_assert(sp->first == sp->last);
4578     /* We create new startpoint, current node will become last one */
4580    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4581                                                 &n->pos, &n->pos, &n->n.pos);
4584     sp->closed        = FALSE;
4586     //Unlink to make a head and tail
4587     sp->first         = new_path;
4588     sp->last          = n;
4589     n->n.other        = NULL;
4590     new_path->p.other = NULL;
4593 /**
4594  * Return new node in subpath with given properties.
4595  * \param pos Position of node.
4596  * \param ppos Handle position in previous direction
4597  * \param npos Handle position in previous direction
4598  */
4599 Inkscape::NodePath::Node *
4600 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)
4602     g_assert(sp);
4603     g_assert(sp->nodepath);
4604     g_assert(sp->nodepath->desktop);
4606     if (nodechunk == NULL)
4607         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4609     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4611     n->subpath  = sp;
4613     if (type != Inkscape::NodePath::NODE_NONE) {
4614         // use the type from sodipodi:nodetypes
4615         n->type = type;
4616     } else {
4617         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4618             // points are (almost) collinear
4619             if (Geom::L2(*pos - *ppos) < 1e-6 || Geom::L2(*pos - *npos) < 1e-6) {
4620                 // endnode, or a node with a retracted handle
4621                 n->type = Inkscape::NodePath::NODE_CUSP;
4622             } else {
4623                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4624             }
4625         } else {
4626             n->type = Inkscape::NodePath::NODE_CUSP;
4627         }
4628     }
4630     n->code     = code;
4631     n->selected = FALSE;
4632     n->pos      = *pos;
4633     n->p.pos    = *ppos;
4634     n->n.pos    = *npos;
4636     n->dragging_out = NULL;
4638     Inkscape::NodePath::Node *prev;
4639     if (next) {
4640         //g_assert(g_list_find(sp->nodes, next));
4641         prev = next->p.other;
4642     } else {
4643         prev = sp->last;
4644     }
4646     if (prev)
4647         prev->n.other = n;
4648     else
4649         sp->first = n;
4651     if (next)
4652         next->p.other = n;
4653     else
4654         sp->last = n;
4656     n->p.other = prev;
4657     n->n.other = next;
4659     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"));
4660     sp_knot_set_position(n->knot, *pos, 0);
4662     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4663     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4664     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4666     sp_nodepath_update_node_knot(n);
4668     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4669     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4670     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4671     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4672     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4673     sp_knot_show(n->knot);
4675     // We only create handle knots and lines on demand
4676     n->p.knot = NULL;
4677     n->p.line = NULL;
4678     n->n.knot = NULL;
4679     n->n.line = NULL;
4681     sp->nodes = g_list_prepend(sp->nodes, n);
4683     return n;
4686 /**
4687  * Destroy node and its knots, link neighbors in subpath.
4688  */
4689 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4691     g_assert(node);
4692     g_assert(node->subpath);
4693     g_assert(SP_IS_KNOT(node->knot));
4695    Inkscape::NodePath::SubPath *sp = node->subpath;
4697     if (node->selected) { // first, deselect
4698         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4699         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4700     }
4702     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4704     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4705     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4706     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4707     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4708     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4709     g_object_unref(G_OBJECT(node->knot));
4711     if (node->p.knot) {
4712         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4713         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4714         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4715         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4716         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4717         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4718         g_object_unref(G_OBJECT(node->p.knot));
4719         node->p.knot = NULL;
4720     }
4722     if (node->n.knot) {
4723         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4724         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4725         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4726         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4727         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4728         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4729         g_object_unref(G_OBJECT(node->n.knot));
4730         node->n.knot = NULL;
4731     }
4733     if (node->p.line)
4734         gtk_object_destroy(GTK_OBJECT(node->p.line));
4735     if (node->n.line)
4736         gtk_object_destroy(GTK_OBJECT(node->n.line));
4738     if (sp->nodes) { // there are others nodes on the subpath
4739         if (sp->closed) {
4740             if (sp->first == node) {
4741                 g_assert(sp->last == node);
4742                 sp->first = node->n.other;
4743                 sp->last = sp->first;
4744             }
4745             node->p.other->n.other = node->n.other;
4746             node->n.other->p.other = node->p.other;
4747         } else {
4748             if (sp->first == node) {
4749                 sp->first = node->n.other;
4750                 sp->first->code = NR_MOVETO;
4751             }
4752             if (sp->last == node) sp->last = node->p.other;
4753             if (node->p.other) node->p.other->n.other = node->n.other;
4754             if (node->n.other) node->n.other->p.other = node->p.other;
4755         }
4756     } else { // this was the last node on subpath
4757         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4758     }
4760     g_mem_chunk_free(nodechunk, node);
4763 /**
4764  * Returns one of the node's two sides.
4765  * \param which Indicates which side.
4766  * \return Pointer to previous node side if which==-1, next if which==1.
4767  */
4768 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4770     g_assert(node);
4771     Inkscape::NodePath::NodeSide * result = 0;
4772     switch (which) {
4773         case -1:
4774             result = &node->p;
4775             break;
4776         case 1:
4777             result = &node->n;
4778             break;
4779         default:
4780             g_assert_not_reached();
4781     }
4783     return result;
4786 /**
4787  * Return the other side of the node, given one of its sides.
4788  */
4789 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4791     g_assert(node);
4792     Inkscape::NodePath::NodeSide *result = 0;
4794     if (me == &node->p) {
4795         result = &node->n;
4796     } else if (me == &node->n) {
4797         result = &node->p;
4798     } else {
4799         g_assert_not_reached();
4800     }
4802     return result;
4805 /**
4806  * Return NRPathcode on the given side of the node.
4807  */
4808 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4810     g_assert(node);
4812     NRPathcode result = NR_END;
4813     if (me == &node->p) {
4814         if (node->p.other) {
4815             result = (NRPathcode)node->code;
4816         } else {
4817             result = NR_MOVETO;
4818         }
4819     } else if (me == &node->n) {
4820         if (node->n.other) {
4821             result = (NRPathcode)node->n.other->code;
4822         } else {
4823             result = NR_MOVETO;
4824         }
4825     } else {
4826         g_assert_not_reached();
4827     }
4829     return result;
4832 /**
4833  * Return node with the given index
4834  */
4835 Inkscape::NodePath::Node *
4836 sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index)
4838     Inkscape::NodePath::Node *e = NULL;
4840     if (!nodepath) {
4841         return e;
4842     }
4844     //find segment
4845     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4847         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4848         int n = g_list_length(sp->nodes);
4849         if (sp->closed) {
4850             n++;
4851         }
4853         //if the piece belongs to this subpath grab it
4854         //otherwise move onto the next subpath
4855         if (index < n) {
4856             e = sp->first;
4857             for (int i = 0; i < index; ++i) {
4858                 e = e->n.other;
4859             }
4860             break;
4861         } else {
4862             if (sp->closed) {
4863                 index -= (n+1);
4864             } else {
4865                 index -= n;
4866             }
4867         }
4868     }
4870     return e;
4873 /**
4874  * Returns plain text meaning of node type.
4875  */
4876 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4878     unsigned retracted = 0;
4879     bool endnode = false;
4881     for (int which = -1; which <= 1; which += 2) {
4882         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4883         if (side->other && Geom::L2(side->pos - node->pos) < 1e-6)
4884             retracted ++;
4885         if (!side->other)
4886             endnode = true;
4887     }
4889     if (retracted == 0) {
4890         if (endnode) {
4891                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4892                 return _("end node");
4893         } else {
4894             switch (node->type) {
4895                 case Inkscape::NodePath::NODE_CUSP:
4896                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4897                     return _("cusp");
4898                 case Inkscape::NodePath::NODE_SMOOTH:
4899                     // TRANSLATORS: "smooth" is an adjective here
4900                     return _("smooth");
4901                 case Inkscape::NodePath::NODE_AUTO:
4902                     return _("auto");
4903                 case Inkscape::NodePath::NODE_SYMM:
4904                     return _("symmetric");
4905             }
4906         }
4907     } else if (retracted == 1) {
4908         if (endnode) {
4909             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4910             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4911         } else {
4912             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4913         }
4914     } else {
4915         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4916     }
4918     return NULL;
4921 /**
4922  * Handles content of statusbar as long as node tool is active.
4923  */
4924 void
4925 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4927     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");
4928     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4930     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4931     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4932     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4933     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4935     SPDesktop *desktop = NULL;
4936     if (nodepath) {
4937         desktop = nodepath->desktop;
4938     } else {
4939         desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above
4940     }
4942     SPEventContext *ec = desktop->event_context;
4943     if (!ec) return;
4945     Inkscape::MessageContext *mc = get_message_context(ec);
4946     if (!mc) return;
4948     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4950     if (selected_nodes == 0) {
4951         Inkscape::Selection *sel = desktop->selection;
4952         if (!sel || sel->isEmpty()) {
4953             mc->setF(Inkscape::NORMAL_MESSAGE,
4954                      _("Select a single object to edit its nodes or handles."));
4955         } else {
4956             if (nodepath) {
4957             mc->setF(Inkscape::NORMAL_MESSAGE,
4958                      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.",
4959                               "<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.",
4960                               total_nodes),
4961                      total_nodes);
4962             } else {
4963                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4964                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4965                 } else {
4966                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4967                 }
4968             }
4969         }
4970     } else if (nodepath && selected_nodes == 1) {
4971         mc->setF(Inkscape::NORMAL_MESSAGE,
4972                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4973                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4974                           total_nodes),
4975                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4976     } else {
4977         if (selected_subpaths > 1) {
4978             mc->setF(Inkscape::NORMAL_MESSAGE,
4979                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4980                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4981                               total_nodes),
4982                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4983         } else {
4984             mc->setF(Inkscape::NORMAL_MESSAGE,
4985                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4986                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4987                               total_nodes),
4988                      selected_nodes, total_nodes, when_selected);
4989         }
4990     }
4993 /*
4994  * returns a *copy* of the curve of that object.
4995  */
4996 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4997     if (!object)
4998         return NULL;
5000     SPCurve *curve = NULL;
5001     if (SP_IS_PATH(object)) {
5002         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
5003         curve = curve_new->copy();
5004     } else if ( IS_LIVEPATHEFFECT(object) && key) {
5005         const gchar *svgd = object->repr->attribute(key);
5006         if (svgd) {
5007             Geom::PathVector pv = sp_svg_read_pathv(svgd);
5008             SPCurve *curve_new = new SPCurve(pv);
5009             if (curve_new) {
5010                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
5011             }
5012         }
5013     }
5015     return curve;
5018 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
5019     if (!np || !np->object || !curve)
5020         return;
5022     if (SP_IS_PATH(np->object)) {
5023         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
5024             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
5025         } else {
5026             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
5027         }
5028     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
5029         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(np->object)->get_lpe();
5030         if (lpe) {
5031             Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( lpe->getParameter(np->repr_key) );
5032             if (pathparam) {
5033                 pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
5034                 np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
5035             }
5036         }
5037     }
5040 /*
5041 SPCanvasItem *
5042 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
5043     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
5045 */
5048 /// \todo this code to generate a helper canvasitem from an spcurve should be moved to different file
5049 SPCanvasItem *
5050 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const Geom::Matrix & i2d, guint32 color = 0xff0000ff) {
5051     SPCurve *flash_curve = curve->copy();
5052     flash_curve->transform(i2d);
5053     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
5054     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
5055     // unless we also flash the nodes...
5056     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
5057     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
5058     sp_canvas_item_show(canvasitem);
5059     flash_curve->unref();
5060     return canvasitem;
5063 SPCanvasItem *
5064 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item) {
5065     if (!item || !desktop) {
5066         return NULL;
5067     }
5069     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5070     guint32 color = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
5072     Geom::Matrix i2d = sp_item_i2d_affine(item);
5074     SPCurve *curve = NULL;
5075     if (SP_IS_PATH(item)) {
5076         curve = sp_path_get_curve_for_edit(SP_PATH(item));
5077     } else if ( SP_IS_SHAPE(item) && SP_SHAPE(item)->curve ) {
5078         curve = sp_shape_get_curve (SP_SHAPE(item));
5079     } else if ( SP_IS_TEXT(item) ) {
5080         // do not display helperpath for text - we cannot do anything with it in Node tool anyway
5081         // curve = SP_TEXT(item)->getNormalizedBpath();
5082         return NULL;
5083     } else {
5084         g_warning ("-----> sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item): TODO: generate the helper path for this item type!\n");
5085         return NULL;
5086     }
5088     SPCanvasItem * helperpath = sp_nodepath_generate_helperpath(desktop, curve, i2d, color);
5090     curve->unref();
5092     return helperpath;
5096 // TODO: Merge this with sp_nodepath_make_helper_item()!
5097 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
5098     np->show_helperpath = show;
5100     if (show) {
5101         SPCurve *helper_curve = np->curve->copy();
5102         helper_curve->transform(np->i2d);
5103         if (!np->helper_path) {
5104             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
5106             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
5107             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);
5108             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
5109             sp_canvas_item_move_to_z(np->helper_path, 0);
5110             sp_canvas_item_show(np->helper_path);
5111         } else {
5112             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
5113         }
5114         helper_curve->unref();
5115     } else {
5116         if (np->helper_path) {
5117             GtkObject *temp = np->helper_path;
5118             np->helper_path = NULL;
5119             gtk_object_destroy(temp);
5120         }
5121     }
5124 /* sp_nodepath_make_straight_path:
5125  *   Prevents user from curving the path by dragging a segment or activating handles etc.
5126  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
5127  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
5128  */
5129 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
5130     np->straight_path = true;
5131     np->show_handles = false;
5132     g_message("add code to make the path straight.");
5133     // do sp_nodepath_convert_node_type on all nodes?
5134     // coding tip: search for this text : "Make selected segments lines"
5137 /*
5138   Local Variables:
5139   mode:c++
5140   c-file-style:"stroustrup"
5141   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5142   indent-tabs-mode:nil
5143   fill-column:99
5144   End:
5145 */
5146 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :