1
#define __SP_NODEPATH_C__
4
* Path handler in node edit mode
7
* Lauris Kaplinski <lauris@kaplinski.com>
8
* bulia byak <buliabyak@users.sf.net>
10
* Portions of this code are in public domain; node sculpting functions written by bulia byak are under GNU GPL
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"
33
#include "sp-namedview.h"
35
#include "desktop-handles.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"
45
#include "preferences.h"
46
#include "sp-metrics.h"
50
#include "libnr/nr-matrix-ops.h"
53
#include <2geom/bezier-utils.h>
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; }
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);
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);
172
sp_canvas_item_show(helper_path);
174
helper_curve->unref();
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");
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();
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();
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;
210
gtk_object_destroy(temp);
213
np->helper_path_vec.clear();
216
/** updates canvas items from the effect's helper paths */
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");
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();
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();
248
* \brief Creates new nodepath from item
250
* If repr_key_in is not NULL, object *has* to be a LivePathEffectObject !
252
* \todo create proper constructor for nodepath::path, this method returns null a constructor cannot so this cannot be simply converted to constructor.
254
Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
257
g_assert(IS_LIVEPATHEFFECT(object));
260
Inkscape::XML::Node *repr = object->repr;
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.
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);
279
SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
285
if (curve->get_segment_count() < 1) {
287
return NULL; // prevent crash for one-node paths
290
//Create new nodepath
291
Inkscape::NodePath::Path *np = new Inkscape::NodePath::Path();
297
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
300
np->desktop = desktop;
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;
318
np->straight_path = false;
319
if (IS_LIVEPATHEFFECT(object) && item) {
322
np->item = SP_ITEM(object);
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();
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();
341
g_error("sp_nodepath_new: lpeobject without real lpe passed as argument!");
344
Inkscape::LivePathEffect::Parameter *lpeparam = lpe->getParameter(repr_key_in);
346
lpeparam->param_setup_nodepath(np);
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));
355
lpe->setup_nodepath(np);
358
np->repr_key = g_strdup("d");
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;
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);
385
if (np->show_helperpath) {
386
np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true, np->helperpath_rgba);
389
sp_nodepath_create_helperpaths(np);
395
* Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
397
Inkscape::NodePath::Path::~Path() {
398
while (this->subpaths) {
399
sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) this->subpaths->data);
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);
414
this->curve->unref();
418
if (this->repr_key) {
419
g_free(this->repr_key);
420
this->repr_key = NULL;
422
if (this->repr_nodetypes_key) {
423
g_free(this->repr_nodetypes_key);
424
this->repr_nodetypes_key = NULL;
427
sp_nodepath_destroy_helperpaths(this);
429
this->desktop = NULL;
433
* Return the node count of a given NodeSubPath.
435
static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
440
nodeCount = g_list_length(subpath->nodes);
447
* Return the node count of a given NodePath.
449
static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *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);
462
* Return the subpath count of a given NodePath.
464
static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
468
nodeCount = g_list_length(np->subpaths);
474
* Return the selected node count of a given NodePath.
476
static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
480
nodeCount = g_list_length(np->selected);
486
* Return the number of subpaths where nodes are selected in a given NodePath.
488
static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
491
if (np && np->selected) {
492
if (!np->selected->next) {
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) {
511
* Clean up a nodepath after editing.
513
* Currently we are deleting trivial subpaths.
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);
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);
533
g_list_free(badSubPaths);
537
* Create new nodepaths from pathvector, make it subpaths of np.
538
* \param t The node type array.
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) {
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) )
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;
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;
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);
586
sp_nodepath_subpath_close(sp);
592
* Convert from sodipodi:nodetypes to new style type array.
595
Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
597
Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
602
for (guint i = 0; types[i] && ( i < length ); i++) {
603
while ((types[i] > '\0') && (types[i] <= ' ')) i++;
604
if (types[i] != '\0') {
607
typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
610
typestr[pos++] =Inkscape::NodePath::NODE_AUTO;
613
typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
616
typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
619
typestr[pos++] =Inkscape::NodePath::NODE_NONE;
626
while (pos < length) {
627
typestr[pos++] = Inkscape::NodePath::NODE_NONE;
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.
637
static void update_object(Inkscape::NodePath::Path *np)
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();
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();
662
* Update XML path node with data from path object.
664
static void update_repr_internal(Inkscape::NodePath::Path *np)
668
Inkscape::XML::Node *repr = np->object->repr;
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
679
repr->setAttribute(np->repr_key, svgpath);
682
if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
684
repr->setAttribute(np->repr_nodetypes_key, 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();
697
// TODO: do we need this call here? after all, update_object() should have been called just before
698
//sp_nodepath_update_helperpaths(np);
702
* Update XML path node with data from path object, commit changes forever.
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,
717
* Update XML path node with data from path object, commit changes with undo.
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,
727
* Make duplicate of path, replace corresponding XML node in tree, commit.
729
static void stamp_repr(Inkscape::NodePath::Path *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();
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,
757
Inkscape::GC::release(new_repr);
764
* Create curve from path.
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;
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");
781
curve->lineto(end_pt);
784
curve->curveto(n->p.other->n.pos * np->d2i,
789
g_assert_not_reached();
807
* Convert path type string to sodipodi:nodetypes style.
809
static gchar *create_typestr(Inkscape::NodePath::Path *np)
811
gchar *typestr = g_new(gchar, 32);
815
for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
816
Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
819
typestr = g_renew(gchar, typestr, len + 32);
823
typestr[pos++] = 'c';
825
Inkscape::NodePath::Node *n;
826
n = sp->first->n.other;
831
case Inkscape::NodePath::NODE_CUSP:
834
case Inkscape::NodePath::NODE_SMOOTH:
837
case Inkscape::NodePath::NODE_AUTO:
840
case Inkscape::NodePath::NODE_SYMM:
844
g_assert_not_reached();
850
typestr = g_renew(gchar, typestr, len + 32);
854
typestr[pos++] = code;
865
typestr = g_renew(gchar, typestr, len + 1);
869
typestr[pos++] = '\0';
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
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;
887
g_warning ("Nodepath should only be present in Node tool or Geometric tool.");
894
\brief Fills node and handle positions for three nodes, splitting line
895
marked by end at distance t.
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;
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);
911
new_path->type =Inkscape::NodePath::NODE_SMOOTH;
912
new_path->code = NR_CURVETO;
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;
935
* Adds new node on direct line between two nodes, activates handles of all
938
static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
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,
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);
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
966
static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *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;
977
sp_nodepath_subpath_open(sp, node);
979
} else if ( (node == sp->first) || (node == sp->last ) ){
980
// no break for end nodes
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;
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);
1012
* Duplicate node and connect to neighbours.
1014
static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *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
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
1032
return newnode; // otherwise select the newly created node
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));
1047
* Change line type at node, with side effects on neighbours.
1049
static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
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;
1060
if (code == NR_LINETO) {
1061
if (start->code == NR_LINETO) {
1062
sp_nodepath_set_node_type(start, Inkscape::NodePath::NODE_CUSP);
1065
if (end->n.other->code == NR_LINETO) {
1066
sp_nodepath_set_node_type(end, Inkscape::NodePath::NODE_CUSP);
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);
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);
1089
sp_node_update_handles(start);
1090
sp_node_update_handles(end);
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);
1106
node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1107
node->knot->setSize (node->selected? 9 : 7);
1108
sp_knot_update_ctrl(node->knot);
1114
* Change node type, and its handles accordingly.
1116
static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
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;
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);
1137
sp_node_adjust_handles(node);
1140
sp_node_update_handles(node);
1142
sp_nodepath_update_statusbar(node->subpath->nodepath);
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;
1154
NRPathcode const code = sp_node_path_code_from_side(node, side);
1155
if (code == NR_LINETO)
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;
1166
(Geom::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1167
Geom::L2(node->pos - side->pos) < 1e-6);
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.
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;
1186
if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1189
Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
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) {
1196
// already half-smooth; pull opposite handle too making it fully smooth
1198
// do nothing, adjust_handles will line the handle up, producing a half-smooth node
1201
// pull opposite handle in line with the existing one
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) {
1209
// pull the handle opposite to line segment, making node half-smooth
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;
1231
// do nothing, adjust_handles will line the handle up, producing a half-smooth node
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;
1241
// do nothing, adjust_handles will line the handle up, producing a half-smooth node
1243
} else if (p_has_handle && node->n.other) {
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) {
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);
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))
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);
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) {
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);
1281
} else if (n_is_line && node->p.other) {
1282
if (type != Inkscape::NodePath::NODE_SYMM) {
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);
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;
1297
sp_nodepath_set_node_type (node, type);
1301
* Move node to point, and adjust its and neighbouring handles.
1303
void sp_node_moveto(Inkscape::NodePath::Node *node, Geom::Point p)
1305
if (node->type == Inkscape::NodePath::NODE_AUTO) {
1307
sp_node_adjust_handles_auto(node);
1309
Geom::Point delta = p - node->pos;
1312
node->p.pos += delta;
1313
node->n.pos += delta;
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;
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;
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;
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;
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);
1346
sp_node_update_handles(node_n, false);
1349
sp_node_update_handles(node_p, false);
1354
* Call sp_node_moveto() for node selection and handle possible snapping.
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;
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<std::pair<Geom::Point, int> > 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(std::make_pair(to_2geom(node->pos), node->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPTARGET_NODE_SMOOTH : Inkscape::SNAPTARGET_NODE_CUSP));
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;
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) {
1397
closest_dist = dist;
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);
1410
Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1411
dedicated_constraint.setPoint(n->pos);
1412
s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type, dedicated_constraint, false);
1414
s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type);
1417
if (s.getSnapped()) {
1418
s.setPointerDistance(Geom::L2(nodepath->drag_origin_mouse - n->origin));
1419
if (!s.isOtherSnapBetter(best, true)) {
1421
best_pt = from_2geom(s.getPoint()) - n->pos;
1427
if (best.getSnapped()) {
1428
nodepath->desktop->snapindicator->set_new_snaptarget(best);
1430
nodepath->desktop->snapindicator->remove_snaptarget();
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);
1439
// do not update repr here so that node dragging is acceptably fast
1440
update_object(nodepath);
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
1449
sculpt_profile (double x, double alpha, guint profile)
1455
} else if (x <= 0) {
1459
case SCULPT_PROFILE_LINEAR:
1462
case SCULPT_PROFILE_BELL:
1463
result = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1465
case SCULPT_PROFILE_ELLIPTIC:
1466
result = sqrt(1 - x*x);
1469
g_assert_not_reached();
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;
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);
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.
1500
sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, Geom::Point delta)
1503
g_assert (nodepath);
1504
g_assert (n->subpath->nodepath == nodepath);
1506
double pressure = n->knot->pressure;
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);
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)
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;
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) {
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) {
1544
n_sel_range = n_range;
1546
if (n_node == p_node) {
1551
if (p_node && p_going)
1552
p_node = p_node->p.other;
1553
if (p_node == NULL) {
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) {
1560
p_sel_range = p_range;
1562
if (p_node == n_node) {
1567
} while (n_going || p_going);
1570
// Second pass: actually move nodes in this subpath
1571
sp_nodepath_move_node_and_handles (n, delta, delta, delta);
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;
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) {
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);
1591
if (n_node == p_node) {
1596
if (p_node && p_going)
1597
p_node = p_node->p.other;
1598
if (p_node == NULL) {
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);
1608
if (p_node == n_node) {
1613
} while (n_going || p_going);
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));
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);
1646
sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1654
// do not update repr here so that node dragging is acceptably fast
1655
update_object(nodepath);
1660
* Move node selection to point, adjust its and neighbouring handles,
1661
* handle possible snapping, and commit the change with possible undo.
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);
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"));
1675
sp_nodepath_update_repr(nodepath, _("Move nodes"));
1680
* Move node selection off screen and commit the change.
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);
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"));
1701
sp_nodepath_update_repr(nodepath, _("Move nodes"));
1706
* Move selected nodes to the absolute position given
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);
1716
sp_nodepath_update_repr(nodepath, _("Move nodes"));
1720
* If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return Geom::Nothing
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) {
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];
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)
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);
1772
side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1773
SP_TYPE_CTRLLINE, NULL);
1778
* Ensure the given handle of the node is visible/invisible, update its screen position
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);
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);
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
1803
sp_knot_moveto(side->knot, side->pos);
1804
sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1807
if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1808
sp_knot_show(side->knot);
1811
sp_canvas_item_show(side->line);
1814
if (SP_KNOT_IS_VISIBLE(side->knot)) {
1815
sp_knot_hide(side->knot);
1819
sp_canvas_item_hide(side->line);
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).
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);
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);
1842
sp_knot_moveto(node->knot, node->pos);
1845
gboolean show_handles = node->selected;
1846
if (node->p.other != NULL) {
1847
if (node->p.other->selected) show_handles = TRUE;
1849
if (node->n.other != NULL) {
1850
if (node->n.other->selected) show_handles = TRUE;
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);
1861
* Call sp_node_update_handles() for all nodes on subpath.
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);
1873
* Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
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);
1885
sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1888
nodepath->show_handles = show;
1889
sp_nodepath_update_handles(nodepath);
1894
* Adds all selected nodes in nodepath to list.
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
1903
* Align selected nodes on the specified axis.
1905
void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1907
if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1911
if ( !nodepath->selected->next ) { // only one node selected
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);
1919
dest[axis] = pNode->pos[axis];
1920
sp_node_moveto(pNode, dest);
1924
sp_nodepath_update_repr(nodepath, _("Align nodes"));
1930
Inkscape::NodePath::Node *_node;
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])
1939
static bool operator<(NodeSort const &a, NodeSort const &b)
1941
return (a._coord < b._coord);
1945
* Distribute selected nodes on the specified axis.
1947
void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1949
if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1953
if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
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);
1962
NodeSort n(pNode, axis);
1963
sorted.push_back(n);
1964
//dest[axis] = pNode->pos[axis];
1965
//sp_node_moveto(pNode, dest);
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());
1980
Geom::Point dest((*it)._node->pos);
1982
sp_node_moveto((*it)._node, dest);
1986
sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1991
* Call sp_nodepath_line_add_node() for all selected segments.
1994
sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
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);
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);
2017
nl = g_list_remove(nl, t);
2020
/** \todo fixme: adjust ? */
2021
sp_nodepath_update_handles(nodepath);
2024
sp_nodepath_update_repr(nodepath, _("Add nodes"));
2025
} else if (n_added > 0) {
2026
sp_nodepath_update_repr(nodepath, _("Add node"));
2029
sp_nodepath_update_statusbar(nodepath);
2033
* Select segment nearest to point
2036
sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p, bool toggle)
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);
2046
g_print ("Possible error?\n");
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()) {
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))) {
2071
sp_nodepath_node_select(e, (gboolean) toggle, force);
2073
sp_nodepath_node_select(e->p.other, TRUE, force);
2075
sp_nodepath_update_handles(nodepath);
2077
sp_nodepath_update_statusbar(nodepath);
2081
* Add a node nearest to point
2084
sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p)
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);
2094
g_print ("Possible error?\n");
2098
// calculate index for nodepath's representation.
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()) {
2111
//find segment to split
2112
Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
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) {
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);
2134
* Adjusts a segment so that t moves by a certain delta for dragging
2135
* converts lines to curves
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()
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);
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);
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
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;
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);
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);
2195
* Call sp_nodepath_break() for all selected segments.
2197
void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2199
if (!nodepath) return;
2201
GList *tempin = g_list_copy(nodepath->selected);
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);
2209
g_list_free(tempin);
2212
sp_nodepath_deselect(nodepath);
2214
for (GList *l = temp; l != NULL; l = l->next) {
2215
sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2218
sp_nodepath_update_handles(nodepath);
2220
sp_nodepath_update_repr(nodepath, _("Break path"));
2224
* Duplicate the selected node(s).
2226
void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
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);
2241
sp_nodepath_deselect(nodepath);
2243
for (GList *l = temp; l != NULL; l = l->next) {
2244
sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2247
sp_nodepath_update_handles(nodepath);
2249
sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2253
* Internal function to join two nodes by merging them into one.
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
2261
if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2263
} else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2266
// otherwise, move joined node to the midpoint
2267
c = (a->pos + b->pos) / 2;
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"));
2280
/* a and b are separate subpaths */
2281
Inkscape::NodePath::SubPath *sa = a->subpath;
2282
Inkscape::NodePath::SubPath *sb = b->subpath;
2284
Inkscape::NodePath::Node *n;
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
2294
sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2296
if (n == sa->first) n = NULL;
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);
2301
if (n == sa->first) n = NULL;
2303
// replace sa with t
2304
sp_nodepath_subpath_destroy(sa);
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);
2313
g_assert_not_reached();
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);
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);
2329
g_assert_not_reached();
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);
2343
* Internal function to join two nodes by adding a segment between them.
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*/
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"));
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;
2376
if (a == sa->first) {
2377
code = (NRPathcode) sa->first->n.other->code;
2378
Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
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);
2384
sp_nodepath_subpath_destroy(sa);
2386
} else if (a == sa->last) {
2387
code = (NRPathcode)sa->last->code;
2390
g_assert_not_reached();
2393
if (b == 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);
2401
} else if (b == 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);
2410
g_assert_not_reached();
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 };
2424
* Internal function to handle joining two nodes.
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."));
2435
Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2436
Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
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.
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."));
2451
case NODE_JOIN_ENDPOINTS:
2452
do_node_selected_join(nodepath, a, b);
2454
case NODE_JOIN_SEGMENT:
2455
do_node_selected_join_segment(nodepath, a, b);
2461
* Join two nodes by merging them into one.
2463
void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2465
node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2469
* Join two nodes by adding a segment between them.
2471
void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2473
node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2477
* Delete one or more selected nodes and preserve the shape of the path as much as possible.
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;
2499
//just delete at the beginning of an open path
2500
if (!delete_cursor->p.other) {
2501
sample_cursor = delete_cursor;
2504
sample_cursor = delete_cursor->p.other;
2507
//calculate points for each segment
2509
float period = 1.0 / rate;
2510
std::vector<Geom::Point> data;
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) {
2520
//sample points on the contiguous selected segment
2522
bez = new Geom::Point [4];
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);
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) {
2543
//calculate the best fitting single segment and adjust the endpoints
2545
adata = new Geom::Point [data.size()];
2546
copy(data.begin(), data.end(), adata);
2549
bez = new Geom::Point [4];
2550
//would decreasing error create a better fitting approximation?
2551
gdouble error = 1.0;
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);
2564
sample_cursor->n.pos = bez[1];
2565
sample_end->p.pos = bez[2];
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;
2575
delete_cursor = delete_cursor->n.other;
2577
nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2578
sp_nodepath_node_destroy(temp);
2581
sp_nodepath_update_handles(nodepath);
2583
if (!g_slist_find(nodepaths, nodepath))
2584
nodepaths = g_slist_prepend (nodepaths, nodepath);
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
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
2601
sp_selection_delete(nodepath->desktop);
2602
sp_document_done (document, SP_VERB_CONTEXT_NODE,
2605
sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2606
sp_nodepath_update_statusbar(nodepath);
2610
g_slist_free (nodepaths);
2614
* Delete one or more selected nodes.
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);
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,
2643
sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2645
sp_nodepath_update_statusbar(nodepath);
2649
* Delete one or more segments between two selected nodes.
2650
* This is the code for 'split'.
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."));
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
2675
nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2676
_("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2680
//###########################################
2682
//###########################################
2683
//##################################
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.
2695
int minDistance = 0;
2696
for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2698
//printf("a to b:%d\n", distance);
2699
start = a;//go from a to b
2701
minDistance = distance;
2702
//printf("A to B :\n");
2708
//try again, the other direction
2710
for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2712
//printf("b to a:%d\n", distance);
2713
if (distance < minDistance) {
2714
start = b; //we go from b to a
2717
//printf("B to A\n");
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;
2731
sp_nodepath_node_new(t, NULL,
2732
(Inkscape::NodePath::NodeType)curr->type, code,
2733
&curr->p.pos, &curr->pos, &curr->n.pos);
2737
sp_nodepath_subpath_destroy(a->subpath);
2744
//##################################
2746
//##################################
2749
//We need to get the direction of the list between A and B
2750
//Can we walk from a to b?
2752
for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2754
start = a; //did it! we go from a to b
2756
//printf("A to B\n");
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) {
2763
start = b; //did it! we go from b to a
2765
//printf("B to A\n");
2771
nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2772
_("Cannot find path between nodes."));
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;
2784
sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2785
&curr->p.pos, &curr->pos, &curr->n.pos);
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);
2795
//###########################################
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);
2810
* Call sp_nodepath_set_line() for all selected segments.
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);
2825
sp_nodepath_update_repr(nodepath, _("Change segment type"));
2829
* Call sp_nodepath_convert_node_type() for all selected nodes.
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);
2842
sp_nodepath_update_repr(nodepath, _("Change node type"));
2846
* Change select status of node, update its own and neighbour handles.
2848
static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2850
node->selected = 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);
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);
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);
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
2875
static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2877
Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2881
if (!g_list_find(nodepath->selected, node)) {
2882
nodepath->selected = g_list_prepend(nodepath->selected, node);
2884
sp_node_set_selected(node, TRUE);
2886
if (node->selected) {
2887
g_assert(g_list_find(nodepath->selected, node));
2888
nodepath->selected = g_list_remove(nodepath->selected, node);
2890
g_assert(!g_list_find(nodepath->selected, node));
2891
nodepath->selected = g_list_prepend(nodepath->selected, node);
2893
sp_node_set_selected(node, !node->selected);
2896
sp_nodepath_deselect(nodepath);
2897
nodepath->selected = g_list_prepend(nodepath->selected, node);
2898
sp_node_set_selected(node, TRUE);
2901
sp_nodepath_update_statusbar(nodepath);
2906
\brief Deselect all nodes in the nodepath
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);
2917
sp_nodepath_update_statusbar(nodepath);
2921
\brief Select or invert selection of all nodes in the nodepath
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);
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).
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);
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);
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);
2971
g_slist_free (subpaths);
2976
* \brief Select the node after the last selected; if none is selected,
2977
* select the first within path.
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
2999
last = node->n.other;
3002
last = node->n.other;
3005
if (node->n.other) {
3006
last = node->n.other;
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
3014
last = (Inkscape::NodePath::Node *) subpath->first;
3021
sp_nodepath_deselect(nodepath);
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);
3033
* \brief Select the node before the first selected; if none is selected,
3034
* select the last within path
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
3055
last = node->p.other;
3058
last = node->p.other;
3061
if (node->p.other) {
3062
last = node->p.other;
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
3070
last = (Inkscape::NodePath::Node *) subpath->last;
3077
sp_nodepath_deselect(nodepath);
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);
3090
* \brief Select all nodes that are within the rectangle.
3092
void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, Geom::Rect const &b, gboolean incremental)
3095
sp_nodepath_deselect(nodepath);
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);
3112
nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3115
g_assert (nodepath);
3116
g_assert (n->subpath->nodepath == nodepath);
3118
if (g_list_length (nodepath->selected) == 0) {
3120
sp_nodepath_node_select(n, TRUE, TRUE);
3125
if (g_list_length (nodepath->selected) == 1) {
3127
sp_nodepath_deselect (nodepath);
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;
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;
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) {
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;
3154
if (n_node == p_node) {
3159
if (p_node && p_going)
3160
p_node = p_node->p.other;
3161
if (p_node == NULL) {
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;
3169
if (p_node == n_node) {
3174
} while (n_going || p_going);
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);
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);
3193
nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3196
g_assert (nodepath);
3197
g_assert (n->subpath->nodepath == nodepath);
3199
if (g_list_length (nodepath->selected) == 0) {
3201
sp_nodepath_node_select(n, TRUE, TRUE);
3206
if (g_list_length (nodepath->selected) == 1) {
3208
sp_nodepath_deselect (nodepath);
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;
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;
3231
if (Geom::L2(node->pos - n->pos) < closest_dist) {
3232
closest_dist = Geom::L2(node->pos - n->pos);
3233
closest_unselected = node;
3240
if (closest_unselected) {
3241
sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3244
if (farthest_selected) {
3245
sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3252
\brief Saves all nodes' and handles' current positions in their origin members
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;
3262
n->p.origin = n->p.pos;
3263
n->n.origin = n->n.pos;
3269
\brief Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3271
GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3274
if (nodepath->selected) {
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;
3281
if (node->selected) {
3282
r = g_list_append(r, GINT_TO_POINTER(i));
3291
\brief Restores selection by selecting nodes whose positions are in the list
3293
void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3295
sp_nodepath_deselect(nodepath);
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;
3303
if (g_list_find(r, GINT_TO_POINTER(i))) {
3304
sp_nodepath_node_select(node, TRUE, TRUE);
3312
\brief Adjusts handle according to node type and line code.
3314
static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3318
// nothing to do for auto nodes (sp_node_adjust_handles() does the job)
3319
if (node->type == Inkscape::NodePath::NODE_AUTO)
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)
3334
if (which_adjust == 1) {
3335
mecode = (NRPathcode)me->other->code;
3337
mecode = (NRPathcode)node->code;
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)
3349
me->pos = node->pos + (len / linelen)*delta;
3353
if (node->type == Inkscape::NodePath::NODE_SYMM) {
3355
me->pos = 2 * node->pos - other->pos;
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;
3368
\brief Adjusts both handles according to node type and line code
3370
static void sp_node_adjust_handles(Inkscape::NodePath::Node *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);
3386
if (sp_node_side_is_line(node, &node->p)) {
3387
sp_node_adjust_handle(node, 1);
3391
if (sp_node_side_is_line(node, &node->n)) {
3392
sp_node_adjust_handle(node, -1);
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;
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;
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);
3429
if (norm_leg_next > 0.0) {
3430
delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
3434
node->p.pos = node->pos - norm_leg_prev / 3 * delta;
3435
node->n.pos = node->pos + norm_leg_next / 3 * delta;
3439
* Node event callback.
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;
3448
case GDK_LEAVE_NOTIFY:
3449
Inkscape::NodePath::Path::active_node = NULL;
3452
if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3453
switch (event->scroll.direction) {
3455
nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3457
case GDK_SCROLL_DOWN:
3458
nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3464
} else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3465
switch (event->scroll.direction) {
3467
nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3469
case GDK_SCROLL_DOWN:
3470
nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3479
switch (get_group0_keyval (&event->key)) {
3481
if (event->key.state & GDK_BUTTON1_MASK) {
3482
Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3483
stamp_repr(nodepath);
3488
if (event->key.state & GDK_CONTROL_MASK) {
3489
nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3491
nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3495
if (event->key.state & GDK_CONTROL_MASK) {
3496
nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3498
nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3513
* Handle keypress on node; directly called.
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))) {
3524
switch (get_group0_keyval (&event->key)) {
3525
/// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
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;
3534
sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3538
sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3542
sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_AUTO);
3546
sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3550
sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3560
* Mouseclick on node callback.
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);
3577
sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
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);
3589
sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3594
* Mouse grabbed node callback.
3596
static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3598
Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3601
sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
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);
3614
* Mouse ungrabbed node callback.
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"));
3629
* The point on a line, given by its angle, closest to the given 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?
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]);
3640
(*closest)[Geom::X] = ( a * (*p)[Geom::Y] + (*p)[Geom::X]) / (a*a + 1);
3641
(*closest)[Geom::Y] = a * (*closest)[Geom::X];
3646
* Distance from the point to a line given by its angle.
3648
* \param a Angle of the line; it is assumed to go through coordinate origin.
3650
static double point_line_distance(Geom::Point *p, double a)
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]));
3658
* Callback for node "request" signal.
3659
* \todo fixme: This goes to "moved" event? (lauris)
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;
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 ) )
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?
3690
Inkscape::NodePath::NodeSide *opposite;
3691
if (appr_p > appr_n) { // closer to p
3692
n->dragging_out = &n->p;
3694
n->code = NR_CURVETO;
3695
} else if (appr_p < appr_n) { // closer to n
3696
n->dragging_out = &n->n;
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;
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;
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;
3714
n->n.other->code = NR_CURVETO;
3715
} else { // closer to other's n handle
3716
n->dragging_out = &n->p;
3718
n->code = NR_CURVETO;
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);
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);
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);
3739
if (state & GDK_CONTROL_MASK) { // constrained motion
3741
// calculate relative distances of handles
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];
3753
if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3754
if (yn < 0) { xn = -xn; yn = -yn; }
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) {
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];
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
3776
if (yn == 0) { // no handle, consider it the continuation of the other one
3780
else an = 0; // vertical; set the angle to horizontal
3784
if (yp == 0) { // no handle, consider it the continuation of the other one
3787
else ap = 0; // vertical; set the angle to horizontal
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
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);
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],
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],
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],
3835
true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::Y]));
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);
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);
3851
n->subpath->nodepath->desktop->scroll_to_point(p);
3857
* Node handle clicked callback.
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) {
3866
} else if (n->n.knot == knot) {
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);
3880
* Node handle grabbed callback.
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);
3893
sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
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;
3902
g_assert_not_reached();
3905
sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3909
* Node handle ungrabbed callback.
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);
3923
g_assert_not_reached();
3926
sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3930
* Node handle "request" signal callback.
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;
3938
if (n->p.knot == knot) {
3942
} else if (n->n.knot == knot) {
3947
me = opposite = NULL;
3949
g_assert_not_reached();
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();
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);
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;
3977
if ((state & GDK_SHIFT_MASK) == 0) {
3978
s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type, Inkscape::Snapper::ConstraintLine(p, ndelta), false);
3981
if ((state & GDK_SHIFT_MASK) == 0) {
3982
s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type);
3986
if ((state & GDK_SHIFT_MASK) == 0) {
3987
s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type);
3993
sp_node_adjust_handle(n, -which);
3999
* Node handle moved callback.
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) {
4011
} else if (n->n.knot == knot) {
4017
g_assert_not_reached();
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)
4043
// 3. Snap to the angle of the opposite line, if any
4044
Inkscape::NodePath::Node *othernode = other->other;
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;
4050
other_to_snap = other->pos - n->pos;
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)
4067
if (state & GDK_MOD1_MASK) {
4068
// lock handle length
4069
rnew.r = me->origin_radial.r;
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;
4078
sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
4079
sp_knot_moveto(other->knot, other->pos);
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);
4093
SPDesktop *desktop = n->subpath->nodepath->desktop;
4094
if (!desktop) return;
4095
SPEventContext *ec = desktop->event_context;
4098
Inkscape::MessageContext *mc = get_message_context(ec);
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°, 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);
4117
* Node handle event callback.
4119
static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
4121
gboolean ret = FALSE;
4122
switch (event->type) {
4124
switch (get_group0_keyval (&event->key)) {
4126
if (event->key.state & GDK_BUTTON1_MASK) {
4127
Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
4128
stamp_repr(nodepath);
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;
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;
4153
static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
4154
Radial &rme, Radial &rother, gboolean const both)
4158
|| ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4159
|| ( n.type == Inkscape::NodePath::NODE_SYMM ) )
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();
4172
|| ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4173
|| ( n.type == Inkscape::NodePath::NODE_SYMM ) )
4175
r = MAX(rme.r, rother.r);
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
4185
rme.a += weird_angle;
4187
|| ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4188
|| ( n.type == Inkscape::NodePath::NODE_SYMM ) )
4190
rother.a += weird_angle;
4197
static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4199
Inkscape::NodePath::NodeSide *me, *other;
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"
4208
} else if (!n->p.other) {
4212
if (which > 0) { // right handle
4220
} else if (which < 0){ // left handle
4228
} else { // both handles
4235
Radial rme(me->pos - n->pos);
4236
Radial rother(other->pos - n->pos);
4239
node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4241
node_rotate_one_internal (*n, angle, rme, rother, both);
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);
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);
4256
* Rotate selected nodes.
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);
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
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);
4285
Geom::Point rot_center;
4286
if (Inkscape::NodePath::Path::active_node == NULL)
4287
rot_center = box.midpoint();
4289
rot_center = Inkscape::NodePath::Path::active_node->pos;
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;
4301
sp_node_update_handles(n, false);
4305
sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4311
static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
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"
4322
n->code = NR_CURVETO;
4323
} else if (!n->p.other) {
4327
n->n.other->code = NR_CURVETO;
4329
if (which > 0) { // right handle
4334
n->n.other->code = NR_CURVETO;
4338
n->code = NR_CURVETO;
4340
} else if (which < 0){ // left handle
4345
n->n.other->code = NR_CURVETO;
4349
n->code = NR_CURVETO;
4351
} else { // both handles
4355
n->code = NR_CURVETO;
4357
n->n.other->code = NR_CURVETO;
4361
Radial rme(me->pos - n->pos);
4362
Radial rother(other->pos - n->pos);
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);
4370
} else { // if there's no next, initialize to 0
4374
if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4376
if (rother.r < 0) rother.r = 0;
4377
if (rother.a == HUGE_VAL) {
4378
rother.a = rme.a + M_PI;
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);
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);
4394
* Scale selected nodes.
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);
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
4414
if ( Geom::are_near(box.maxExtent(), 0) ) {
4415
SPEventContext *ec = nodepath->desktop->event_context;
4417
Inkscape::MessageContext *mc = get_message_context(ec);
4419
mc->setF(Inkscape::WARNING_MESSAGE,
4420
_("Cannot scale nodes when all are at the same location."));
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();
4430
scale_center = Inkscape::NodePath::Path::active_node->pos;
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;
4442
sp_node_update_handles(n, false);
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);
4456
* Flip selected nodes horizontally/vertically.
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);
4470
// scale nodes as an "object":
4472
Geom::Rect box = sp_node_selected_bbox (nodepath);
4474
center = box.midpoint();
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;
4486
sp_node_update_handles(n, false);
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
4506
//-----------------------------------------------
4508
* Return new subpath under given nodepath.
4510
static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4513
g_assert(nodepath->desktop);
4515
Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4517
s->nodepath = nodepath;
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);
4531
* Destroy nodes in subpath, then subpath itself.
4533
static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *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);
4543
subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4549
* Link head to tail in subpath.
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);
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);
4570
* Open closed (loopy) subpath at node.
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);
4586
//Unlink to make a head and tail
4587
sp->first = new_path;
4590
new_path->p.other = NULL;
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
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)
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);
4613
if (type != Inkscape::NodePath::NODE_NONE) {
4614
// use the type from sodipodi:nodetypes
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;
4623
n->type = Inkscape::NodePath::NODE_SMOOTH;
4626
n->type = Inkscape::NodePath::NODE_CUSP;
4631
n->selected = FALSE;
4636
n->dragging_out = NULL;
4638
Inkscape::NodePath::Node *prev;
4640
//g_assert(g_list_find(sp->nodes, next));
4641
prev = next->p.other;
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
4681
sp->nodes = g_list_prepend(sp->nodes, n);
4687
* Destroy node and its knots, link neighbors in subpath.
4689
static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *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);
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));
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;
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;
4734
gtk_object_destroy(GTK_OBJECT(node->p.line));
4736
gtk_object_destroy(GTK_OBJECT(node->n.line));
4738
if (sp->nodes) { // there are others nodes on the subpath
4740
if (sp->first == node) {
4741
g_assert(sp->last == node);
4742
sp->first = node->n.other;
4743
sp->last = sp->first;
4745
node->p.other->n.other = node->n.other;
4746
node->n.other->p.other = node->p.other;
4748
if (sp->first == node) {
4749
sp->first = node->n.other;
4750
sp->first->code = NR_MOVETO;
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;
4756
} else { // this was the last node on subpath
4757
sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4760
g_mem_chunk_free(nodechunk, node);
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.
4768
static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4771
Inkscape::NodePath::NodeSide * result = 0;
4780
g_assert_not_reached();
4787
* Return the other side of the node, given one of its sides.
4789
static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4792
Inkscape::NodePath::NodeSide *result = 0;
4794
if (me == &node->p) {
4796
} else if (me == &node->n) {
4799
g_assert_not_reached();
4806
* Return NRPathcode on the given side of the node.
4808
static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4812
NRPathcode result = NR_END;
4813
if (me == &node->p) {
4814
if (node->p.other) {
4815
result = (NRPathcode)node->code;
4819
} else if (me == &node->n) {
4820
if (node->n.other) {
4821
result = (NRPathcode)node->n.other->code;
4826
g_assert_not_reached();
4833
* Return node with the given index
4835
Inkscape::NodePath::Node *
4836
sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index)
4838
Inkscape::NodePath::Node *e = NULL;
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);
4853
//if the piece belongs to this subpath grab it
4854
//otherwise move onto the next subpath
4857
for (int i = 0; i < index; ++i) {
4874
* Returns plain text meaning of node type.
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)
4889
if (retracted == 0) {
4891
// TRANSLATORS: "end" is an adjective here (NOT a verb)
4892
return _("end node");
4894
switch (node->type) {
4895
case Inkscape::NodePath::NODE_CUSP:
4896
// TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4898
case Inkscape::NodePath::NODE_SMOOTH:
4899
// TRANSLATORS: "smooth" is an adjective here
4901
case Inkscape::NodePath::NODE_AUTO:
4903
case Inkscape::NodePath::NODE_SYMM:
4904
return _("symmetric");
4907
} else if (retracted == 1) {
4909
// TRANSLATORS: "end" is an adjective here (NOT a verb)
4910
return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4912
return _("one handle retracted (drag with <b>Shift</b> to extend)");
4915
return _("both handles retracted (drag with <b>Shift</b> to extend)");
4922
* Handles content of statusbar as long as node tool is active.
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>< ></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;
4937
desktop = nodepath->desktop;
4939
desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above
4942
SPEventContext *ec = desktop->event_context;
4945
Inkscape::MessageContext *mc = get_message_context(ec);
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."));
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.",
4963
if (g_slist_length((GSList *)sel->itemList()) == 1) {
4964
mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4966
mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
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.",
4975
selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
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.",
4982
selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
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.",
4988
selected_nodes, total_nodes, when_selected);
4994
* returns a *copy* of the curve of that object.
4996
SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
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);
5007
Geom::PathVector pv = sp_svg_read_pathv(svgd);
5008
SPCurve *curve_new = new SPCurve(pv);
5010
curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
5018
void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
5019
if (!np || !np->object || !curve)
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);
5026
sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
5028
} else if ( IS_LIVEPATHEFFECT(np->object) ) {
5029
Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(np->object)->get_lpe();
5031
Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( lpe->getParameter(np->repr_key) );
5033
pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
5034
np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
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));
5048
/// \todo this code to generate a helper canvasitem from an spcurve should be moved to different file
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();
5064
sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item) {
5065
if (!item || !desktop) {
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();
5084
g_warning ("-----> sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item): TODO: generate the helper path for this item type!\n");
5088
SPCanvasItem * helperpath = sp_nodepath_generate_helperpath(desktop, curve, i2d, color);
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;
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);
5112
sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
5114
helper_curve->unref();
5116
if (np->helper_path) {
5117
GtkObject *temp = np->helper_path;
5118
np->helper_path = NULL;
5119
gtk_object_destroy(temp);
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
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"
5140
c-file-style:"stroustrup"
5141
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5142
indent-tabs-mode:nil
5146
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :