Code

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