1 /*
2 * Base class for shapes, including <path> element
3 *
4 * Author:
5 * Lauris Kaplinski <lauris@kaplinski.com>
6 *
7 * Copyright (C) 1999-2002 Lauris Kaplinski
8 * Copyright (C) 2000-2001 Ximian, Inc.
9 * Copyright (C) 2004 John Cliff
10 * Copyright (C) 2007-2008 Johan Engelen
11 *
12 * Released under GNU GPL, read the file 'COPYING' for more information
13 */
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
19 #include <libnr/nr-matrix-fns.h>
20 #include <libnr/nr-matrix-ops.h>
21 #include <libnr/nr-matrix-translate-ops.h>
22 #include <libnr/nr-scale-matrix-ops.h>
23 #include <2geom/rect.h>
24 #include <2geom/transforms.h>
25 #include <2geom/pathvector.h>
26 #include <2geom/path-intersection.h>
27 #include <2geom/exception.h>
28 #include "helper/geom.h"
29 #include "helper/geom-nodetype.h"
31 #include <sigc++/functors/ptr_fun.h>
32 #include <sigc++/adaptors/bind.h>
34 #include "macros.h"
35 #include "display/nr-arena-shape.h"
36 #include "display/curve.h"
37 #include "print.h"
38 #include "document.h"
39 #include "style.h"
40 #include "marker.h"
41 #include "sp-path.h"
42 #include "preferences.h"
43 #include "attributes.h"
45 #include "live_effects/lpeobject.h"
46 #include "uri.h"
47 #include "extract-uri.h"
48 #include "uri-references.h"
49 #include "bad-uri-exception.h"
50 #include "xml/repr.h"
52 #include "util/mathfns.h" // for triangle_area()
54 #include "splivarot.h" // for bounding box calculation
56 #define noSHAPE_VERBOSE
58 static void sp_shape_class_init (SPShapeClass *klass);
59 static void sp_shape_init (SPShape *shape);
60 static void sp_shape_finalize (GObject *object);
62 static void sp_shape_build (SPObject * object, SPDocument * document, Inkscape::XML::Node * repr);
63 static void sp_shape_release (SPObject *object);
65 static void sp_shape_set(SPObject *object, unsigned key, gchar const *value);
66 static void sp_shape_update (SPObject *object, SPCtx *ctx, unsigned int flags);
67 static void sp_shape_modified (SPObject *object, unsigned int flags);
68 static Inkscape::XML::Node *sp_shape_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
70 static void sp_shape_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags);
71 void sp_shape_print (SPItem * item, SPPrintContext * ctx);
72 static NRArenaItem *sp_shape_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags);
73 static void sp_shape_hide (SPItem *item, unsigned int key);
74 static void sp_shape_snappoints (SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs);
76 static void sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai);
78 static SPLPEItemClass *parent_class;
80 /**
81 * Registers the SPShape class with Gdk and returns its type number.
82 */
83 GType
84 sp_shape_get_type (void)
85 {
86 static GType type = 0;
87 if (!type) {
88 GTypeInfo info = {
89 sizeof (SPShapeClass),
90 NULL, NULL,
91 (GClassInitFunc) sp_shape_class_init,
92 NULL, NULL,
93 sizeof (SPShape),
94 16,
95 (GInstanceInitFunc) sp_shape_init,
96 NULL, /* value_table */
97 };
98 type = g_type_register_static (SP_TYPE_LPE_ITEM, "SPShape", &info, (GTypeFlags)0);
99 }
100 return type;
101 }
103 /**
104 * Initializes a SPShapeClass object. Establishes the function pointers to the class'
105 * member routines in the class vtable, and sets pointers to parent classes.
106 */
107 static void
108 sp_shape_class_init (SPShapeClass *klass)
109 {
110 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
111 SPObjectClass *sp_object_class = SP_OBJECT_CLASS(klass);
112 SPItemClass * item_class = SP_ITEM_CLASS(klass);
113 SPLPEItemClass * lpe_item_class = SP_LPE_ITEM_CLASS(klass);
115 parent_class = (SPLPEItemClass *)g_type_class_peek_parent (klass);
117 gobject_class->finalize = sp_shape_finalize;
119 sp_object_class->build = sp_shape_build;
120 sp_object_class->release = sp_shape_release;
121 sp_object_class->set = sp_shape_set;
122 sp_object_class->update = sp_shape_update;
123 sp_object_class->modified = sp_shape_modified;
124 sp_object_class->write = sp_shape_write;
126 item_class->bbox = sp_shape_bbox;
127 item_class->print = sp_shape_print;
128 item_class->show = sp_shape_show;
129 item_class->hide = sp_shape_hide;
130 item_class->snappoints = sp_shape_snappoints;
131 lpe_item_class->update_patheffect = NULL;
133 klass->set_shape = NULL;
134 }
136 /**
137 * Initializes an SPShape object.
138 */
139 static void
140 sp_shape_init (SPShape *shape)
141 {
142 for ( int i = 0 ; i < SP_MARKER_LOC_QTY ; i++ ) {
143 new (&shape->release_connect[i]) sigc::connection();
144 new (&shape->modified_connect[i]) sigc::connection();
145 shape->marker[i] = NULL;
146 }
147 shape->curve = NULL;
148 }
150 static void
151 sp_shape_finalize (GObject *object)
152 {
153 SPShape *shape=(SPShape *)object;
155 for ( int i = 0 ; i < SP_MARKER_LOC_QTY ; i++ ) {
156 shape->release_connect[i].disconnect();
157 shape->release_connect[i].~connection();
158 shape->modified_connect[i].disconnect();
159 shape->modified_connect[i].~connection();
160 }
162 if (((GObjectClass *) (parent_class))->finalize) {
163 (* ((GObjectClass *) (parent_class))->finalize)(object);
164 }
165 }
167 /**
168 * Virtual build callback for SPMarker.
169 *
170 * This is to be invoked immediately after creation of an SPShape.
171 *
172 * \see sp_object_build()
173 */
174 static void
175 sp_shape_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
176 {
177 if (((SPObjectClass *) (parent_class))->build) {
178 (*((SPObjectClass *) (parent_class))->build) (object, document, repr);
179 }
181 for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
182 sp_shape_set_marker (object, i, object->style->marker[i].value);
183 }
184 }
186 /**
187 * Removes, releases and unrefs all children of object
188 *
189 * This is the inverse of sp_shape_build(). It must be invoked as soon
190 * as the shape is removed from the tree, even if it is still referenced
191 * by other objects. This routine also disconnects/unrefs markers and
192 * curves attached to it.
193 *
194 * \see sp_object_release()
195 */
196 static void
197 sp_shape_release (SPObject *object)
198 {
199 SPItem *item;
200 SPShape *shape;
201 SPItemView *v;
202 int i;
204 item = (SPItem *) object;
205 shape = (SPShape *) object;
207 for (i = 0; i < SP_MARKER_LOC_QTY; i++) {
208 if (shape->marker[i]) {
209 for (v = item->display; v != NULL; v = v->next) {
210 sp_marker_hide ((SPMarker *) shape->marker[i], NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
211 }
212 shape->release_connect[i].disconnect();
213 shape->modified_connect[i].disconnect();
214 shape->marker[i] = sp_object_hunref (shape->marker[i], object);
215 }
216 }
217 if (shape->curve) {
218 shape->curve = shape->curve->unref();
219 }
221 if (((SPObjectClass *) parent_class)->release) {
222 ((SPObjectClass *) parent_class)->release (object);
223 }
224 }
228 static void
229 sp_shape_set(SPObject *object, unsigned int key, gchar const *value)
230 {
231 if (((SPObjectClass *) parent_class)->set) {
232 ((SPObjectClass *) parent_class)->set(object, key, value);
233 }
234 }
236 static Inkscape::XML::Node *
237 sp_shape_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags)
238 {
239 if (((SPObjectClass *)(parent_class))->write) {
240 ((SPObjectClass *)(parent_class))->write(object, doc, repr, flags);
241 }
243 return repr;
244 }
246 /**
247 * Updates the shape when its attributes have changed. Also establishes
248 * marker objects to match the style settings.
249 */
250 static void
251 sp_shape_update (SPObject *object, SPCtx *ctx, unsigned int flags)
252 {
253 SPItem *item = (SPItem *) object;
254 SPShape *shape = (SPShape *) object;
256 if (((SPObjectClass *) (parent_class))->update) {
257 (* ((SPObjectClass *) (parent_class))->update) (object, ctx, flags);
258 }
260 /* This stanza checks that an object's marker style agrees with
261 * the marker objects it has allocated. sp_shape_set_marker ensures
262 * that the appropriate marker objects are present (or absent) to
263 * match the style.
264 */
265 for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
266 sp_shape_set_marker (object, i, object->style->marker[i].value);
267 }
269 if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
270 SPStyle *style;
271 style = SP_OBJECT_STYLE (object);
272 if (style->stroke_width.unit == SP_CSS_UNIT_PERCENT) {
273 SPItemCtx *ictx = (SPItemCtx *) ctx;
274 double const aw = 1.0 / NR::expansion(ictx->i2vp);
275 style->stroke_width.computed = style->stroke_width.value * aw;
276 for (SPItemView *v = ((SPItem *) (shape))->display; v != NULL; v = v->next) {
277 nr_arena_shape_set_style ((NRArenaShape *) v->arenaitem, style);
278 }
279 }
280 }
282 if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) {
283 /* This is suboptimal, because changing parent style schedules recalculation */
284 /* But on the other hand - how can we know that parent does not tie style and transform */
285 Geom::OptRect paintbox = SP_ITEM(object)->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
286 for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
287 NRArenaShape * const s = NR_ARENA_SHAPE(v->arenaitem);
288 if (flags & SP_OBJECT_MODIFIED_FLAG) {
289 nr_arena_shape_set_path(s, shape->curve, (flags & SP_OBJECT_USER_MODIFIED_FLAG_B));
290 }
291 if (paintbox) {
292 s->setPaintBox(*paintbox);
293 }
294 }
295 }
297 if (sp_shape_has_markers (shape)) {
298 /* Dimension marker views */
299 for (SPItemView *v = item->display; v != NULL; v = v->next) {
300 if (!v->arenaitem->key) {
301 NR_ARENA_ITEM_SET_KEY (v->arenaitem, sp_item_display_key_new (SP_MARKER_LOC_QTY));
302 }
303 for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
304 if (shape->marker[i]) {
305 sp_marker_show_dimension ((SPMarker *) shape->marker[i],
306 NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i,
307 sp_shape_number_of_markers (shape, i));
308 }
309 }
310 }
312 /* Update marker views */
313 for (SPItemView *v = item->display; v != NULL; v = v->next) {
314 sp_shape_update_marker_view (shape, v->arenaitem);
315 }
316 }
317 }
319 /**
320 * Calculate the transform required to get a marker's path object in the
321 * right place for particular path segment on a shape.
322 *
323 * \see sp_shape_marker_update_marker_view.
324 *
325 * From SVG spec:
326 * The axes of the temporary new user coordinate system are aligned according to the orient attribute on the 'marker'
327 * element and the slope of the curve at the given vertex. (Note: if there is a discontinuity at a vertex, the slope
328 * is the average of the slopes of the two segments of the curve that join at the given vertex. If a slope cannot be
329 * determined, the slope is assumed to be zero.)
330 *
331 * Reference: http://www.w3.org/TR/SVG11/painting.html#MarkerElement, the `orient' attribute.
332 * Reference for behaviour of zero-length segments:
333 * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
334 */
335 Geom::Matrix
336 sp_shape_marker_get_transform(Geom::Curve const & c1, Geom::Curve const & c2)
337 {
338 Geom::Point p = c1.pointAt(1);
339 Geom::Curve * c1_reverse = c1.reverse();
340 Geom::Point tang1 = - c1_reverse->unitTangentAt(0);
341 delete c1_reverse;
342 Geom::Point tang2 = c2.unitTangentAt(0);
344 double const angle1 = Geom::atan2(tang1);
345 double const angle2 = Geom::atan2(tang2);
347 double ret_angle;
348 ret_angle = .5 * (angle1 + angle2);
350 if ( fabs( angle2 - angle1 ) > M_PI ) {
351 /* ret_angle is in the middle of the larger of the two sectors between angle1 and
352 * angle2, so flip it by 180degrees to force it to the middle of the smaller sector.
353 *
354 * (Imagine a circle with rays drawn at angle1 and angle2 from the centre of the
355 * circle. Those two rays divide the circle into two sectors.)
356 */
357 ret_angle += M_PI;
358 }
360 return Geom::Rotate(ret_angle) * Geom::Translate(p);
361 }
362 Geom::Matrix
363 sp_shape_marker_get_transform_at_start(Geom::Curve const & c)
364 {
365 Geom::Point p = c.pointAt(0);
366 Geom::Matrix ret = Geom::Translate(p);
368 if ( !c.isDegenerate() ) {
369 Geom::Point tang = c.unitTangentAt(0);
370 double const angle = Geom::atan2(tang);
371 ret = Geom::Rotate(angle) * Geom::Translate(p);
372 } else {
373 /* FIXME: the svg spec says to search for a better alternative than zero angle directionality:
374 * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes */
375 }
377 return ret;
378 }
379 Geom::Matrix
380 sp_shape_marker_get_transform_at_end(Geom::Curve const & c)
381 {
382 Geom::Point p = c.pointAt(1);
383 Geom::Matrix ret = Geom::Translate(p);
385 if ( !c.isDegenerate() ) {
386 Geom::Curve * c_reverse = c.reverse();
387 Geom::Point tang = - c_reverse->unitTangentAt(0);
388 delete c_reverse;
389 double const angle = Geom::atan2(tang);
390 ret = Geom::Rotate(angle) * Geom::Translate(p);
391 } else {
392 /* FIXME: the svg spec says to search for a better alternative than zero angle directionality:
393 * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes */
394 }
396 return ret;
397 }
399 /**
400 * Updates the instances (views) of a given marker in a shape.
401 * Marker views have to be scaled already. The transformation
402 * is retrieved and then shown by calling sp_marker_show_instance.
403 *
404 * @todo figure out what to do when both 'marker' and for instance 'marker-end' are set.
405 */
406 static void
407 sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai)
408 {
409 SPStyle *style = ((SPObject *) shape)->style;
411 // position arguments to sp_marker_show_instance, basically counts the amount of markers.
412 int counter[4] = {0};
414 if (!shape->curve) return;
415 Geom::PathVector const & pathv = shape->curve->get_pathvector();
416 if (pathv.empty()) return;
418 // the first vertex should get a start marker, the last an end marker, and all the others a mid marker
419 // see bug 456148
421 // START marker
422 {
423 Geom::Matrix const m (sp_shape_marker_get_transform_at_start(pathv.begin()->front()));
424 for (int i = 0; i < 2; i++) { // SP_MARKER_LOC and SP_MARKER_LOC_START
425 if ( shape->marker[i] ) {
426 sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
427 NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
428 style->stroke_width.computed);
429 counter[i]++;
430 }
431 }
432 }
434 // MID marker
435 if (shape->marker[SP_MARKER_LOC_MID] || shape->marker[SP_MARKER_LOC]) {
436 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
437 // START position
438 if ( path_it != pathv.begin()
439 && ! ((path_it == (pathv.end()-1)) && (path_it->size_default() == 0)) ) // if this is the last path and it is a moveto-only, don't draw mid marker there
440 {
441 Geom::Matrix const m (sp_shape_marker_get_transform_at_start(path_it->front()));
442 for (int i = 0; i < 3; i += 2) { // SP_MARKER_LOC and SP_MARKER_LOC_MID
443 if ( shape->marker[i] ) {
444 sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
445 NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
446 style->stroke_width.computed);
447 counter[i]++;
448 }
449 }
450 }
451 // MID position
452 if ( path_it->size_default() > 1) {
453 Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve
454 Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); // outgoing curve
455 while (curve_it2 != path_it->end_default())
456 {
457 /* Put marker between curve_it1 and curve_it2.
458 * Loop to end_default (so including closing segment), because when a path is closed,
459 * there should be a midpoint marker between last segment and closing straight line segment
460 */
461 Geom::Matrix const m (sp_shape_marker_get_transform(*curve_it1, *curve_it2));
462 for (int i = 0; i < 3; i += 2) { // SP_MARKER_LOC and SP_MARKER_LOC_MID
463 if (shape->marker[i]) {
464 sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
465 NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
466 style->stroke_width.computed);
467 counter[i]++;
468 }
469 }
471 ++curve_it1;
472 ++curve_it2;
473 }
474 }
475 // END position
476 if ( path_it != (pathv.end()-1) && !path_it->empty()) {
477 Geom::Curve const &lastcurve = path_it->back_default();
478 Geom::Matrix const m = sp_shape_marker_get_transform_at_end(lastcurve);
479 for (int i = 0; i < 3; i += 2) { // SP_MARKER_LOC and SP_MARKER_LOC_MID
480 if (shape->marker[i]) {
481 sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
482 NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
483 style->stroke_width.computed);
484 counter[i]++;
485 }
486 }
487 }
488 }
489 }
491 // END marker
492 if ( shape->marker[SP_MARKER_LOC_END] || shape->marker[SP_MARKER_LOC] ) {
493 /* Get reference to last curve in the path.
494 * For moveto-only path, this returns the "closing line segment". */
495 Geom::Path const &path_last = pathv.back();
496 unsigned int index = path_last.size_default();
497 if (index > 0) {
498 index--;
499 }
500 Geom::Curve const &lastcurve = path_last[index];
501 Geom::Matrix const m = sp_shape_marker_get_transform_at_end(lastcurve);
503 for (int i = 0; i < 4; i += 3) { // SP_MARKER_LOC and SP_MARKER_LOC_END
504 if (shape->marker[i]) {
505 sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
506 NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
507 style->stroke_width.computed);
508 counter[i]++;
509 }
510 }
511 }
512 }
514 /**
515 * Sets modified flag for all sub-item views.
516 */
517 static void
518 sp_shape_modified (SPObject *object, unsigned int flags)
519 {
520 SPShape *shape = SP_SHAPE (object);
522 if (((SPObjectClass *) (parent_class))->modified) {
523 (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
524 }
526 if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
527 for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
528 nr_arena_shape_set_style (NR_ARENA_SHAPE (v->arenaitem), object->style);
529 }
530 }
531 }
533 /**
534 * Calculates the bounding box for item, storing it into bbox.
535 * This also includes the bounding boxes of any markers included in the shape.
536 */
537 static void sp_shape_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags)
538 {
539 SPShape const *shape = SP_SHAPE (item);
540 if (shape->curve) {
541 Geom::OptRect geombbox = bounds_exact_transformed(shape->curve->get_pathvector(), transform);
542 if (geombbox) {
543 NRRect cbbox;
544 cbbox.x0 = (*geombbox)[0][0];
545 cbbox.y0 = (*geombbox)[1][0];
546 cbbox.x1 = (*geombbox)[0][1];
547 cbbox.y1 = (*geombbox)[1][1];
549 switch ((SPItem::BBoxType) flags) {
550 case SPItem::GEOMETRIC_BBOX: {
551 // do nothing
552 break;
553 }
554 case SPItem::RENDERING_BBOX: {
555 // convert the stroke to a path and calculate that path's geometric bbox
556 SPStyle* style=SP_OBJECT_STYLE (item);
557 if (!style->stroke.isNone()) {
558 Geom::PathVector *pathv = item_outline(item);
559 if (pathv) {
560 Geom::OptRect geomstrokebbox = bounds_exact_transformed(*pathv, transform);
561 if (geomstrokebbox) {
562 NRRect strokebbox;
563 strokebbox.x0 = (*geomstrokebbox)[0][0];
564 strokebbox.y0 = (*geomstrokebbox)[1][0];
565 strokebbox.x1 = (*geomstrokebbox)[0][1];
566 strokebbox.y1 = (*geomstrokebbox)[1][1];
567 nr_rect_d_union (&cbbox, &cbbox, &strokebbox);
568 }
569 delete pathv;
570 }
571 }
572 break;
573 }
574 default:
575 case SPItem::APPROXIMATE_BBOX: {
576 SPStyle* style=SP_OBJECT_STYLE (item);
577 if (!style->stroke.isNone()) {
578 double const scale = transform.descrim();
579 if ( fabs(style->stroke_width.computed * scale) > 0.01 ) { // sinon c'est 0=oon veut pas de bord
580 double const width = MAX(0.125, style->stroke_width.computed * scale);
581 if ( fabs(cbbox.x1-cbbox.x0) > -0.00001 && fabs(cbbox.y1-cbbox.y0) > -0.00001 ) {
582 cbbox.x0-=0.5*width;
583 cbbox.x1+=0.5*width;
584 cbbox.y0-=0.5*width;
585 cbbox.y1+=0.5*width;
586 }
587 }
588 }
590 // Union with bboxes of the markers, if any
591 if (sp_shape_has_markers (shape) && !shape->curve->get_pathvector().empty()) {
592 /** \todo make code prettier! */
593 Geom::PathVector const & pathv = shape->curve->get_pathvector();
594 // START marker
595 for (unsigned i = 0; i < 2; i++) { // SP_MARKER_LOC and SP_MARKER_LOC_START
596 if ( shape->marker[i] ) {
597 SPMarker* marker = SP_MARKER (shape->marker[i]);
598 SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
600 if (marker_item) {
601 Geom::Matrix tr(sp_shape_marker_get_transform_at_start(pathv.begin()->front()));
602 if (!marker->orient_auto) {
603 Geom::Point transl = tr.translation();
604 tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
605 }
606 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
607 tr = Geom::Scale(style->stroke_width.computed) * tr;
608 }
610 // total marker transform
611 tr = marker_item->transform * marker->c2p * tr * transform;
613 // get bbox of the marker with that transform
614 NRRect marker_bbox;
615 sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true);
616 // union it with the shape bbox
617 nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
618 }
619 }
620 }
621 // MID marker
622 for (unsigned i = 0; i < 3; i += 2) { // SP_MARKER_LOC and SP_MARKER_LOC_MID
623 SPMarker* marker = SP_MARKER (shape->marker[i]);
624 if ( !shape->marker[i] ) continue;
625 SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
626 if ( !marker_item ) continue;
628 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
629 // START position
630 if ( path_it != pathv.begin()
631 && ! ((path_it == (pathv.end()-1)) && (path_it->size_default() == 0)) ) // if this is the last path and it is a moveto-only, there is no mid marker there
632 {
633 Geom::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front()));
634 if (!marker->orient_auto) {
635 Geom::Point transl = tr.translation();
636 tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
637 }
638 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
639 tr = Geom::Scale(style->stroke_width.computed) * tr;
640 }
641 tr = marker_item->transform * marker->c2p * tr * transform;
642 NRRect marker_bbox;
643 sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true);
644 nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
645 }
646 // MID position
647 if ( path_it->size_default() > 1) {
648 Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve
649 Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); // outgoing curve
650 while (curve_it2 != path_it->end_default())
651 {
652 /* Put marker between curve_it1 and curve_it2.
653 * Loop to end_default (so including closing segment), because when a path is closed,
654 * there should be a midpoint marker between last segment and closing straight line segment */
656 SPMarker* marker = SP_MARKER (shape->marker[i]);
657 SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
659 if (marker_item) {
660 Geom::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
661 if (!marker->orient_auto) {
662 Geom::Point transl = tr.translation();
663 tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
664 }
665 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
666 tr = Geom::Scale(style->stroke_width.computed) * tr;
667 }
668 tr = marker_item->transform * marker->c2p * tr * transform;
669 NRRect marker_bbox;
670 sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true);
671 nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
672 }
674 ++curve_it1;
675 ++curve_it2;
676 }
677 }
678 // END position
679 if ( path_it != (pathv.end()-1) && !path_it->empty()) {
680 Geom::Curve const &lastcurve = path_it->back_default();
681 Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
682 if (!marker->orient_auto) {
683 Geom::Point transl = tr.translation();
684 tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
685 }
686 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
687 tr = Geom::Scale(style->stroke_width.computed) * tr;
688 }
689 tr = marker_item->transform * marker->c2p * tr * transform;
690 NRRect marker_bbox;
691 sp_item_invoke_bbox (marker_item, &marker_bbox, tr, true);
692 nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
693 }
694 }
695 }
696 // END marker
697 for (unsigned i = 0; i < 4; i += 3) { // SP_MARKER_LOC and SP_MARKER_LOC_END
698 if ( shape->marker[i] ) {
699 SPMarker* marker = SP_MARKER (shape->marker[i]);
700 SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
702 if (marker_item) {
703 /* Get reference to last curve in the path.
704 * For moveto-only path, this returns the "closing line segment". */
705 Geom::Path const &path_last = pathv.back();
706 unsigned int index = path_last.size_default();
707 if (index > 0) {
708 index--;
709 }
710 Geom::Curve const &lastcurve = path_last[index];
712 Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
713 if (!marker->orient_auto) {
714 Geom::Point transl = tr.translation();
715 tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
716 }
717 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
718 tr = Geom::Scale(style->stroke_width.computed) * tr;
719 }
721 // total marker transform
722 tr = marker_item->transform * marker->c2p * tr * transform;
724 // get bbox of the marker with that transform
725 NRRect marker_bbox;
726 sp_item_invoke_bbox (marker_item, &marker_bbox, tr, true);
727 // union it with the shape bbox
728 nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
729 }
730 }
731 }
732 }
733 break;
734 } // end case approximate bbox type
735 } // end switch bboxtype
737 // copy our bbox to the variable we're given
738 *bbox = cbbox;
739 }
740 }
741 }
743 static void
744 sp_shape_print_invoke_marker_printing(SPObject* obj, Geom::Matrix tr, SPStyle* style, SPPrintContext *ctx) {
745 SPMarker *marker = SP_MARKER(obj);
746 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
747 tr = Geom::Scale(style->stroke_width.computed) * tr;
748 }
750 SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
751 tr = marker_item->transform * marker->c2p * tr;
753 Geom::Matrix old_tr = marker_item->transform;
754 marker_item->transform = tr;
755 sp_item_invoke_print (marker_item, ctx);
756 marker_item->transform = old_tr;
757 }
758 /**
759 * Prepares shape for printing. Handles printing of comments for printing
760 * debugging, sizes the item to fit into the document width/height,
761 * applies print fill/stroke, sets transforms for markers, and adds
762 * comment labels.
763 */
764 void
765 sp_shape_print (SPItem *item, SPPrintContext *ctx)
766 {
767 NRRect pbox, dbox, bbox;
769 SPShape *shape = SP_SHAPE(item);
771 if (!shape->curve) return;
773 Geom::PathVector const & pathv = shape->curve->get_pathvector();
774 if (pathv.empty()) return;
776 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
777 gint add_comments = prefs->getBool("/printing/debug/add-label-comments");
778 if (add_comments) {
779 gchar * comment = g_strdup_printf("begin '%s'",
780 SP_OBJECT(item)->defaultLabel());
781 sp_print_comment(ctx, comment);
782 g_free(comment);
783 }
785 /* fixme: Think (Lauris) */
786 sp_item_invoke_bbox(item, &pbox, Geom::identity(), TRUE);
787 dbox.x0 = 0.0;
788 dbox.y0 = 0.0;
789 dbox.x1 = sp_document_width (SP_OBJECT_DOCUMENT (item));
790 dbox.y1 = sp_document_height (SP_OBJECT_DOCUMENT (item));
791 sp_item_bbox_desktop (item, &bbox);
792 Geom::Matrix const i2d(sp_item_i2d_affine(item));
794 SPStyle* style = SP_OBJECT_STYLE (item);
796 if (!style->fill.isNone()) {
797 sp_print_fill (ctx, pathv, &i2d, style, &pbox, &dbox, &bbox);
798 }
800 if (!style->stroke.isNone()) {
801 sp_print_stroke (ctx, pathv, &i2d, style, &pbox, &dbox, &bbox);
802 }
804 /** \todo make code prettier */
805 // START marker
806 for (int i = 0; i < 2; i++) { // SP_MARKER_LOC and SP_MARKER_LOC_START
807 if ( shape->marker[i] ) {
808 Geom::Matrix tr(sp_shape_marker_get_transform_at_start(pathv.begin()->front()));
809 sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
810 }
811 }
812 // MID marker
813 for (int i = 0; i < 3; i += 2) { // SP_MARKER_LOC and SP_MARKER_LOC_MID
814 if (shape->marker[i]) {
815 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
816 // START position
817 if ( path_it != pathv.begin()
818 && ! ((path_it == (pathv.end()-1)) && (path_it->size_default() == 0)) ) // if this is the last path and it is a moveto-only, there is no mid marker there
819 {
820 Geom::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front()));
821 sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
822 }
823 // MID position
824 if ( path_it->size_default() > 1) {
825 Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve
826 Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); // outgoing curve
827 while (curve_it2 != path_it->end_default())
828 {
829 /* Put marker between curve_it1 and curve_it2.
830 * Loop to end_default (so including closing segment), because when a path is closed,
831 * there should be a midpoint marker between last segment and closing straight line segment */
832 Geom::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
834 sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
836 ++curve_it1;
837 ++curve_it2;
838 }
839 }
840 if ( path_it != (pathv.end()-1) && !path_it->empty()) {
841 Geom::Curve const &lastcurve = path_it->back_default();
842 Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
843 sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
844 }
845 }
846 }
847 }
848 // END marker
849 if ( shape->marker[SP_MARKER_LOC_END] || shape->marker[SP_MARKER_LOC]) {
850 /* Get reference to last curve in the path.
851 * For moveto-only path, this returns the "closing line segment". */
852 Geom::Path const &path_last = pathv.back();
853 unsigned int index = path_last.size_default();
854 if (index > 0) {
855 index--;
856 }
857 Geom::Curve const &lastcurve = path_last[index];
859 Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
861 for (int i = 0; i < 4; i += 3) { // SP_MARKER_LOC and SP_MARKER_LOC_END
862 if (shape->marker[i]) {
863 sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
864 }
865 }
866 }
868 if (add_comments) {
869 gchar * comment = g_strdup_printf("end '%s'",
870 SP_OBJECT(item)->defaultLabel());
871 sp_print_comment(ctx, comment);
872 g_free(comment);
873 }
874 }
876 /**
877 * Sets style, path, and paintbox. Updates marker views, including dimensions.
878 */
879 static NRArenaItem *
880 sp_shape_show (SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int /*flags*/)
881 {
882 SPObject *object = SP_OBJECT(item);
883 SPShape *shape = SP_SHAPE(item);
885 NRArenaItem *arenaitem = NRArenaShape::create(arena);
886 NRArenaShape * const s = NR_ARENA_SHAPE(arenaitem);
887 nr_arena_shape_set_style(s, object->style);
888 nr_arena_shape_set_path(s, shape->curve, false);
889 Geom::OptRect paintbox = item->getBounds(Geom::identity());
890 if (paintbox) {
891 s->setPaintBox(*paintbox);
892 }
894 /* This stanza checks that an object's marker style agrees with
895 * the marker objects it has allocated. sp_shape_set_marker ensures
896 * that the appropriate marker objects are present (or absent) to
897 * match the style.
898 */
899 for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
900 sp_shape_set_marker (object, i, object->style->marker[i].value);
901 }
903 if (sp_shape_has_markers (shape)) {
905 /* provide key and dimension the marker views */
906 if (!arenaitem->key) {
907 NR_ARENA_ITEM_SET_KEY (arenaitem, sp_item_display_key_new (SP_MARKER_LOC_QTY));
908 }
910 for (int i = 0; i < SP_MARKER_LOC_QTY; i++) {
911 if (shape->marker[i]) {
912 sp_marker_show_dimension ((SPMarker *) shape->marker[i],
913 NR_ARENA_ITEM_GET_KEY (arenaitem) + i,
914 sp_shape_number_of_markers (shape, i));
915 }
916 }
918 /* Update marker views */
919 sp_shape_update_marker_view (shape, arenaitem);
920 }
922 return arenaitem;
923 }
925 /**
926 * Hides/removes marker views from the shape.
927 */
928 static void
929 sp_shape_hide (SPItem *item, unsigned int key)
930 {
931 SPShape *shape;
932 SPItemView *v;
933 int i;
935 shape = (SPShape *) item;
937 for (i=0; i<SP_MARKER_LOC_QTY; i++) {
938 if (shape->marker[i]) {
939 for (v = item->display; v != NULL; v = v->next) {
940 if (key == v->key) {
941 sp_marker_hide ((SPMarker *) shape->marker[i],
942 NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
943 }
944 }
945 }
946 }
948 if (((SPItemClass *) parent_class)->hide) {
949 ((SPItemClass *) parent_class)->hide (item, key);
950 }
951 }
953 /**
954 * \param shape Shape.
955 * \return TRUE if the shape has any markers, or FALSE if not.
956 */
957 int
958 sp_shape_has_markers (SPShape const *shape)
959 {
960 /* Note, we're ignoring 'marker' settings, which technically should apply for
961 all three settings. This should be fixed later such that if 'marker' is
962 specified, then all three should appear. */
964 return (
965 shape->curve &&
966 (shape->marker[SP_MARKER_LOC] ||
967 shape->marker[SP_MARKER_LOC_START] ||
968 shape->marker[SP_MARKER_LOC_MID] ||
969 shape->marker[SP_MARKER_LOC_END])
970 );
971 }
974 /**
975 * \param shape Shape.
976 * \param type Marker type (e.g. SP_MARKER_LOC_START)
977 * \return Number of markers that the shape has of this type.
978 */
979 int
980 sp_shape_number_of_markers (SPShape *shape, int type)
981 {
982 Geom::PathVector const & pathv = shape->curve->get_pathvector();
983 if (pathv.size() == 0) {
984 return 0;
985 }
987 switch(type) {
988 case SP_MARKER_LOC:
989 {
990 if ( shape->marker[SP_MARKER_LOC] ) {
991 guint n = 0;
992 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
993 n += path_it->size_default() + 1;
994 }
995 return n;
996 } else {
997 return 0;
998 }
999 }
1000 case SP_MARKER_LOC_START:
1001 // there is only a start marker on the first path of a pathvector
1002 return shape->marker[SP_MARKER_LOC_START] ? 1 : 0;
1004 case SP_MARKER_LOC_MID:
1005 {
1006 if ( shape->marker[SP_MARKER_LOC_MID] ) {
1007 guint n = 0;
1008 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
1009 n += path_it->size_default() + 1;
1010 }
1011 return n - 2; // minus the start and end marker.
1012 } else {
1013 return 0;
1014 }
1015 }
1017 case SP_MARKER_LOC_END:
1018 {
1019 // there is only an end marker on the last path of a pathvector
1020 return shape->marker[SP_MARKER_LOC_END] ? 1 : 0;
1021 }
1023 default:
1024 return 0;
1025 }
1026 }
1028 /**
1029 * Checks if the given marker is used in the shape, and if so, it
1030 * releases it by calling sp_marker_hide. Also detaches signals
1031 * and unrefs the marker from the shape.
1032 */
1033 static void
1034 sp_shape_marker_release (SPObject *marker, SPShape *shape)
1035 {
1036 SPItem *item;
1037 int i;
1039 item = (SPItem *) shape;
1041 for (i = 0; i < SP_MARKER_LOC_QTY; i++) {
1042 if (marker == shape->marker[i]) {
1043 SPItemView *v;
1044 /* Hide marker */
1045 for (v = item->display; v != NULL; v = v->next) {
1046 sp_marker_hide ((SPMarker *) (shape->marker[i]), NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
1047 /* fixme: Do we need explicit remove here? (Lauris) */
1048 /* nr_arena_item_set_mask (v->arenaitem, NULL); */
1049 }
1050 /* Detach marker */
1051 shape->release_connect[i].disconnect();
1052 shape->modified_connect[i].disconnect();
1053 shape->marker[i] = sp_object_hunref (shape->marker[i], item);
1054 }
1055 }
1056 }
1058 /**
1059 * No-op. Exists for handling 'modified' messages
1060 */
1061 static void
1062 sp_shape_marker_modified (SPObject */*marker*/, guint /*flags*/, SPItem */*item*/)
1063 {
1064 /* I think mask does update automagically */
1065 /* g_warning ("Item %s mask %s modified", SP_OBJECT_ID (item), SP_OBJECT_ID (mask)); */
1066 }
1068 /**
1069 * Adds a new marker to shape object at the location indicated by key. value
1070 * must be a valid URI reference resolvable from the shape object (i.e., present
1071 * in the document <defs>). If the shape object already has a marker
1072 * registered at the given position, it is removed first. Then the
1073 * new marker is hrefed and its signals connected.
1074 */
1075 void
1076 sp_shape_set_marker (SPObject *object, unsigned int key, const gchar *value)
1077 {
1078 SPItem *item = (SPItem *) object;
1079 SPShape *shape = (SPShape *) object;
1081 if (key > SP_MARKER_LOC_END) {
1082 return;
1083 }
1085 SPObject *mrk = sp_css_uri_reference_resolve (SP_OBJECT_DOCUMENT (object), value);
1086 if (mrk != shape->marker[key]) {
1087 if (shape->marker[key]) {
1088 SPItemView *v;
1090 /* Detach marker */
1091 shape->release_connect[key].disconnect();
1092 shape->modified_connect[key].disconnect();
1094 /* Hide marker */
1095 for (v = item->display; v != NULL; v = v->next) {
1096 sp_marker_hide ((SPMarker *) (shape->marker[key]),
1097 NR_ARENA_ITEM_GET_KEY (v->arenaitem) + key);
1098 /* fixme: Do we need explicit remove here? (Lauris) */
1099 /* nr_arena_item_set_mask (v->arenaitem, NULL); */
1100 }
1102 /* Unref marker */
1103 shape->marker[key] = sp_object_hunref (shape->marker[key], object);
1104 }
1105 if (SP_IS_MARKER (mrk)) {
1106 shape->marker[key] = sp_object_href (mrk, object);
1107 shape->release_connect[key] = mrk->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_shape_marker_release), shape));
1108 shape->modified_connect[key] = mrk->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_shape_marker_modified), shape));
1109 }
1110 }
1111 }
1115 /* Shape section */
1117 /**
1118 * Calls any registered handlers for the set_shape action
1119 */
1120 void
1121 sp_shape_set_shape (SPShape *shape)
1122 {
1123 g_return_if_fail (shape != NULL);
1124 g_return_if_fail (SP_IS_SHAPE (shape));
1126 if (SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape) {
1127 SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape (shape);
1128 }
1129 }
1131 /**
1132 * Adds a curve to the shape. If owner is specified, a reference
1133 * will be made, otherwise the curve will be copied into the shape.
1134 * Any existing curve in the shape will be unreferenced first.
1135 * This routine also triggers a request to update the display.
1136 */
1137 void
1138 sp_shape_set_curve (SPShape *shape, SPCurve *curve, unsigned int owner)
1139 {
1140 if (shape->curve) {
1141 shape->curve = shape->curve->unref();
1142 }
1143 if (curve) {
1144 if (owner) {
1145 shape->curve = curve->ref();
1146 } else {
1147 shape->curve = curve->copy();
1148 }
1149 }
1150 SP_OBJECT(shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1151 }
1153 /**
1154 * Return duplicate of curve (if any exists) or NULL if there is no curve
1155 */
1156 SPCurve *
1157 sp_shape_get_curve (SPShape *shape)
1158 {
1159 if (shape->curve) {
1160 return shape->curve->copy();
1161 }
1162 return NULL;
1163 }
1165 /**
1166 * Same as sp_shape_set_curve but without updating the display
1167 */
1168 void
1169 sp_shape_set_curve_insync (SPShape *shape, SPCurve *curve, unsigned int owner)
1170 {
1171 if (shape->curve) {
1172 shape->curve = shape->curve->unref();
1173 }
1174 if (curve) {
1175 if (owner) {
1176 shape->curve = curve->ref();
1177 } else {
1178 shape->curve = curve->copy();
1179 }
1180 }
1181 }
1183 /**
1184 * Return all nodes in a path that are to be considered for snapping
1185 */
1186 static void sp_shape_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs)
1187 {
1188 g_assert(item != NULL);
1189 g_assert(SP_IS_SHAPE(item));
1191 SPShape const *shape = SP_SHAPE(item);
1192 if (shape->curve == NULL) {
1193 return;
1194 }
1196 // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes
1197 if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) {
1198 return;
1199 }
1201 Geom::PathVector const &pathv = shape->curve->get_pathvector();
1202 if (pathv.empty())
1203 return;
1205 Geom::Matrix const i2d (sp_item_i2d_affine (item));
1207 if (snapprefs->getSnapObjectMidpoints()) {
1208 Geom::OptRect bbox = item->getBounds(sp_item_i2d_affine(item));
1209 if (bbox) {
1210 p.push_back(Inkscape::SnapCandidatePoint(bbox->midpoint(), Inkscape::SNAPSOURCE_OBJECT_MIDPOINT, Inkscape::SNAPTARGET_OBJECT_MIDPOINT));
1211 }
1212 }
1214 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
1215 if (snapprefs->getSnapToItemNode()) {
1216 // Add the first point of the path
1217 p.push_back(Inkscape::SnapCandidatePoint(path_it->initialPoint() * i2d, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP));
1218 }
1220 Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve
1221 Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); // outgoing curve
1222 while (curve_it1 != path_it->end_default())
1223 {
1224 // For each path: consider midpoints of line segments for snapping
1225 if (snapprefs->getSnapLineMidpoints()) { // only do this when we're snapping nodes (enforces strict snapping)
1226 if (Geom::LineSegment const* line_segment = dynamic_cast<Geom::LineSegment const*>(&(*curve_it1))) {
1227 p.push_back(Inkscape::SnapCandidatePoint(Geom::middle_point(*line_segment) * i2d, Inkscape::SNAPSOURCE_LINE_MIDPOINT, Inkscape::SNAPTARGET_LINE_MIDPOINT));
1228 }
1229 }
1231 if (curve_it2 == path_it->end_default()) { // Test will only pass for the last iteration of the while loop
1232 if (snapprefs->getSnapToItemNode() && !path_it->closed()) {
1233 // Add the last point of the path, but only for open paths
1234 // (for closed paths the first and last point will coincide)
1235 p.push_back(Inkscape::SnapCandidatePoint((*curve_it1).finalPoint() * i2d, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP));
1236 }
1237 } else {
1238 /* Test whether to add the node between curve_it1 and curve_it2.
1239 * Loop to end_default (so only iterating through the stroked part); */
1241 Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2);
1243 bool c1 = snapprefs->getSnapToItemNode() && (nodetype == Geom::NODE_CUSP || nodetype == Geom::NODE_NONE);
1244 bool c2 = snapprefs->getSnapSmoothNodes() && (nodetype == Geom::NODE_SMOOTH || nodetype == Geom::NODE_SYMM);
1246 if (c1 || c2) {
1247 Inkscape::SnapSourceType sst;
1248 Inkscape::SnapTargetType stt;
1249 switch (nodetype) {
1250 case Geom::NODE_CUSP:
1251 sst = Inkscape::SNAPSOURCE_NODE_CUSP;
1252 stt = Inkscape::SNAPTARGET_NODE_CUSP;
1253 break;
1254 case Geom::NODE_SMOOTH:
1255 case Geom::NODE_SYMM:
1256 sst = Inkscape::SNAPSOURCE_NODE_SMOOTH;
1257 stt = Inkscape::SNAPTARGET_NODE_SMOOTH;
1258 break;
1259 default:
1260 sst = Inkscape::SNAPSOURCE_UNDEFINED;
1261 stt = Inkscape::SNAPTARGET_UNDEFINED;
1262 break;
1263 }
1264 p.push_back(Inkscape::SnapCandidatePoint(curve_it1->finalPoint() * i2d, sst, stt));
1265 }
1266 }
1268 ++curve_it1;
1269 ++curve_it2;
1270 }
1272 // Find the internal intersections of each path and consider these for snapping
1273 // (using "Method 1" as described in Inkscape::ObjectSnapper::_collectNodes())
1274 if (snapprefs->getSnapIntersectionCS()) {
1275 Geom::Crossings cs;
1276 try {
1277 cs = self_crossings(*path_it);
1278 if (cs.size() > 0) { // There might be multiple intersections...
1279 for (Geom::Crossings::const_iterator i = cs.begin(); i != cs.end(); i++) {
1280 Geom::Point p_ix = (*path_it).pointAt((*i).ta);
1281 p.push_back(Inkscape::SnapCandidatePoint(p_ix * i2d, Inkscape::SNAPSOURCE_PATH_INTERSECTION, Inkscape::SNAPTARGET_PATH_INTERSECTION));
1282 }
1283 }
1284 } catch (Geom::RangeError &e) {
1285 // do nothing
1286 // The exception could be Geom::InfiniteSolutions: then no snappoints should be added
1287 }
1289 }
1290 }
1292 }
1294 /*
1295 Local Variables:
1296 mode:c++
1297 c-file-style:"stroustrup"
1298 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1299 indent-tabs-mode:nil
1300 fill-column:99
1301 End:
1302 */
1303 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :