Code

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