Code

Extensions. Fix for bug #647744 ([inx] min/max float values ignored with locale using...
[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     Geom::PathVector const & pathv = shape->curve->get_pathvector();
415     // the first vertex should get a start marker, the last an end marker, and all the others a mid marker
416     // see bug 456148
418     // START marker
419     {
420         Geom::Matrix const m (sp_shape_marker_get_transform_at_start(pathv.begin()->front()));
421         for (int i = 0; i < 2; i++) {  // SP_MARKER_LOC and SP_MARKER_LOC_START
422             if ( shape->marker[i] ) {
423                 sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
424                                          NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
425                                          style->stroke_width.computed);
426                  counter[i]++;
427             }
428         }
429     }
431     // MID marker
432     if (shape->marker[SP_MARKER_LOC_MID] || shape->marker[SP_MARKER_LOC]) {
433         for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
434             // START position
435             if ( path_it != pathv.begin() 
436                  && ! ((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
437             {
438                 Geom::Matrix const m (sp_shape_marker_get_transform_at_start(path_it->front()));
439                 for (int i = 0; i < 3; i += 2) {  // SP_MARKER_LOC and SP_MARKER_LOC_MID
440                     if ( shape->marker[i] ) {
441                         sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
442                                                  NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
443                                                  style->stroke_width.computed);
444                          counter[i]++;
445                     }
446                 }
447             }
448             // MID position
449             if ( path_it->size_default() > 1) {
450                 Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
451                 Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
452                 while (curve_it2 != path_it->end_default())
453                 {
454                     /* Put marker between curve_it1 and curve_it2.
455                      * Loop to end_default (so including closing segment), because when a path is closed,
456                      * there should be a midpoint marker between last segment and closing straight line segment
457                      */
458                     Geom::Matrix const m (sp_shape_marker_get_transform(*curve_it1, *curve_it2));
459                     for (int i = 0; i < 3; i += 2) {  // SP_MARKER_LOC and SP_MARKER_LOC_MID
460                         if (shape->marker[i]) {
461                             sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
462                                                      NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
463                                                      style->stroke_width.computed);
464                             counter[i]++;
465                         }
466                     }
468                     ++curve_it1;
469                     ++curve_it2;
470                 }
471             }
472             // END position
473             if ( path_it != (pathv.end()-1) && !path_it->empty()) {
474                 Geom::Curve const &lastcurve = path_it->back_default();
475                 Geom::Matrix const m = sp_shape_marker_get_transform_at_end(lastcurve);
476                 for (int i = 0; i < 3; i += 2) {  // SP_MARKER_LOC and SP_MARKER_LOC_MID
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             }
485         }
486     }
488     // END marker
489     if ( shape->marker[SP_MARKER_LOC_END] || shape->marker[SP_MARKER_LOC] ) {
490         /* Get reference to last curve in the path.
491          * For moveto-only path, this returns the "closing line segment". */
492         Geom::Path const &path_last = pathv.back();
493         unsigned int index = path_last.size_default();
494         if (index > 0) {
495             index--;
496         }
497         Geom::Curve const &lastcurve = path_last[index];
498         Geom::Matrix const m = sp_shape_marker_get_transform_at_end(lastcurve);
500         for (int i = 0; i < 4; i += 3) {  // SP_MARKER_LOC and SP_MARKER_LOC_END
501             if (shape->marker[i]) {
502                 sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
503                                          NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
504                                          style->stroke_width.computed);
505                 counter[i]++;
506             }
507         }
508     }
511 /**
512  * Sets modified flag for all sub-item views.
513  */
514 static void
515 sp_shape_modified (SPObject *object, unsigned int flags)
517     SPShape *shape = SP_SHAPE (object);
519     if (((SPObjectClass *) (parent_class))->modified) {
520       (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
521     }
523     if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
524         for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
525             nr_arena_shape_set_style (NR_ARENA_SHAPE (v->arenaitem), object->style);
526         }
527     }
530 /**
531  * Calculates the bounding box for item, storing it into bbox.
532  * This also includes the bounding boxes of any markers included in the shape.
533  */
534 static void sp_shape_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags)
536     SPShape const *shape = SP_SHAPE (item);
537     if (shape->curve) {
538         Geom::OptRect geombbox = bounds_exact_transformed(shape->curve->get_pathvector(), transform);
539         if (geombbox) {
540             NRRect  cbbox;
541             cbbox.x0 = (*geombbox)[0][0];
542             cbbox.y0 = (*geombbox)[1][0];
543             cbbox.x1 = (*geombbox)[0][1];
544             cbbox.y1 = (*geombbox)[1][1];
546             switch ((SPItem::BBoxType) flags) {
547                 case SPItem::GEOMETRIC_BBOX: {
548                     // do nothing
549                     break;
550                 }
551                 case SPItem::RENDERING_BBOX: {
552                     // convert the stroke to a path and calculate that path's geometric bbox
553                     SPStyle* style=SP_OBJECT_STYLE (item);
554                     if (!style->stroke.isNone()) {
555                         Geom::PathVector *pathv = item_outline(item);
556                         if (pathv) {
557                             Geom::OptRect geomstrokebbox = bounds_exact_transformed(*pathv, transform);
558                             if (geomstrokebbox) {
559                                 NRRect  strokebbox;
560                                 strokebbox.x0 = (*geomstrokebbox)[0][0];
561                                 strokebbox.y0 = (*geomstrokebbox)[1][0];
562                                 strokebbox.x1 = (*geomstrokebbox)[0][1];
563                                 strokebbox.y1 = (*geomstrokebbox)[1][1];
564                                 nr_rect_d_union (&cbbox, &cbbox, &strokebbox);
565                             }
566                             delete pathv;
567                         }
568                     }
569                     break;
570                 }
571                 default:
572                 case SPItem::APPROXIMATE_BBOX: {
573                     SPStyle* style=SP_OBJECT_STYLE (item);
574                     if (!style->stroke.isNone()) {
575                         double const scale = transform.descrim();
576                         if ( fabs(style->stroke_width.computed * scale) > 0.01 ) { // sinon c'est 0=oon veut pas de bord
577                             double const width = MAX(0.125, style->stroke_width.computed * scale);
578                             if ( fabs(cbbox.x1-cbbox.x0) > -0.00001 && fabs(cbbox.y1-cbbox.y0) > -0.00001 ) {
579                                 cbbox.x0-=0.5*width;
580                                 cbbox.x1+=0.5*width;
581                                 cbbox.y0-=0.5*width;
582                                 cbbox.y1+=0.5*width;
583                             }
584                         }
585                     }
587                     // Union with bboxes of the markers, if any
588                     if (sp_shape_has_markers (shape)) {
589                         /** \todo make code prettier! */
590                         Geom::PathVector const & pathv = shape->curve->get_pathvector();
591                         // START marker
592                         for (unsigned i = 0; i < 2; i++) { // SP_MARKER_LOC and SP_MARKER_LOC_START
593                             if ( shape->marker[i] ) {
594                                 SPMarker* marker = SP_MARKER (shape->marker[i]);
595                                 SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
597                                 if (marker_item) {
598                                     Geom::Matrix tr(sp_shape_marker_get_transform_at_start(pathv.begin()->front()));
599                                     if (!marker->orient_auto) {
600                                         Geom::Point transl = tr.translation();
601                                         tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
602                                     }
603                                     if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
604                                         tr = Geom::Scale(style->stroke_width.computed) * tr;
605                                     }
607                                     // total marker transform
608                                     tr = marker_item->transform * marker->c2p * tr * transform;
610                                     // get bbox of the marker with that transform
611                                     NRRect marker_bbox;
612                                     sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true);
613                                     // union it with the shape bbox
614                                     nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
615                                 }
616                             }
617                         }
618                         // MID marker
619                         for (unsigned i = 0; i < 3; i += 2) { // SP_MARKER_LOC and SP_MARKER_LOC_MID
620                             SPMarker* marker = SP_MARKER (shape->marker[i]);
621                             if ( !shape->marker[i] ) continue;
622                             SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
623                             if ( !marker_item ) continue;
625                             for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
626                                 // START position
627                                 if ( path_it != pathv.begin() 
628                                      && ! ((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
629                                 {
630                                     Geom::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front()));
631                                     if (!marker->orient_auto) {
632                                         Geom::Point transl = tr.translation();
633                                         tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
634                                     }
635                                     if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
636                                         tr = Geom::Scale(style->stroke_width.computed) * tr;
637                                     }
638                                     tr = marker_item->transform * marker->c2p * tr * transform;
639                                     NRRect marker_bbox;
640                                     sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true);
641                                     nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
642                                 }
643                                 // MID position
644                                 if ( path_it->size_default() > 1) {
645                                     Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
646                                     Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
647                                     while (curve_it2 != path_it->end_default())
648                                     {
649                                         /* Put marker between curve_it1 and curve_it2.
650                                          * Loop to end_default (so including closing segment), because when a path is closed,
651                                          * there should be a midpoint marker between last segment and closing straight line segment */
653                                         SPMarker* marker = SP_MARKER (shape->marker[i]);
654                                         SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
656                                         if (marker_item) {
657                                             Geom::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
658                                             if (!marker->orient_auto) {
659                                                 Geom::Point transl = tr.translation();
660                                                 tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
661                                             }
662                                             if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
663                                                 tr = Geom::Scale(style->stroke_width.computed) * tr;
664                                             }
665                                             tr = marker_item->transform * marker->c2p * tr * transform;
666                                             NRRect marker_bbox;
667                                             sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true);
668                                             nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
669                                         }
671                                         ++curve_it1;
672                                         ++curve_it2;
673                                     }
674                                 }
675                                 // END position
676                                 if ( path_it != (pathv.end()-1) && !path_it->empty()) {
677                                     Geom::Curve const &lastcurve = path_it->back_default();
678                                     Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
679                                     if (!marker->orient_auto) {
680                                         Geom::Point transl = tr.translation();
681                                         tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
682                                     }
683                                     if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
684                                         tr = Geom::Scale(style->stroke_width.computed) * tr;
685                                     }
686                                     tr = marker_item->transform * marker->c2p * tr * transform;
687                                     NRRect marker_bbox;
688                                     sp_item_invoke_bbox (marker_item, &marker_bbox, tr, true);
689                                     nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
690                                 }
691                             }
692                         }
693                         // END marker
694                         for (unsigned i = 0; i < 4; i += 3) { // SP_MARKER_LOC and SP_MARKER_LOC_END
695                             if ( shape->marker[i] ) {
696                                 SPMarker* marker = SP_MARKER (shape->marker[i]);
697                                 SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
699                                 if (marker_item) {
700                                     /* Get reference to last curve in the path.
701                                      * For moveto-only path, this returns the "closing line segment". */
702                                     Geom::Path const &path_last = pathv.back();
703                                     unsigned int index = path_last.size_default();
704                                     if (index > 0) {
705                                         index--;
706                                     }
707                                     Geom::Curve const &lastcurve = path_last[index];
709                                     Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
710                                     if (!marker->orient_auto) {
711                                         Geom::Point transl = tr.translation();
712                                         tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
713                                     }
714                                     if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
715                                         tr = Geom::Scale(style->stroke_width.computed) * tr;
716                                     }
718                                     // total marker transform
719                                     tr = marker_item->transform * marker->c2p * tr * transform;
721                                     // get bbox of the marker with that transform
722                                     NRRect marker_bbox;
723                                     sp_item_invoke_bbox (marker_item, &marker_bbox, tr, true);
724                                     // union it with the shape bbox
725                                     nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
726                                 }
727                             }
728                         }
729                     }
730                     break;
731                 } // end case approximate bbox type 
732             }  // end switch bboxtype
734             // copy our bbox to the variable we're given
735             *bbox = cbbox;
736         }
737     }
740 static void
741 sp_shape_print_invoke_marker_printing(SPObject* obj, Geom::Matrix tr, SPStyle* style, SPPrintContext *ctx) {
742     SPMarker *marker = SP_MARKER(obj);
743     if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
744         tr = Geom::Scale(style->stroke_width.computed) * tr;
745     }
747     SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
748     tr = marker_item->transform * marker->c2p * tr;
750     Geom::Matrix old_tr = marker_item->transform;
751     marker_item->transform = tr;
752     sp_item_invoke_print (marker_item, ctx);
753     marker_item->transform = old_tr;
755 /**
756  * Prepares shape for printing.  Handles printing of comments for printing
757  * debugging, sizes the item to fit into the document width/height,
758  * applies print fill/stroke, sets transforms for markers, and adds
759  * comment labels.
760  */
761 void
762 sp_shape_print (SPItem *item, SPPrintContext *ctx)
764     NRRect pbox, dbox, bbox;
766     SPShape *shape = SP_SHAPE(item);
768     if (!shape->curve) return;
770         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
771         gint add_comments = prefs->getBool("/printing/debug/add-label-comments");
772         if (add_comments) {
773             gchar * comment = g_strdup_printf("begin '%s'",
774                                               SP_OBJECT(item)->defaultLabel());
775             sp_print_comment(ctx, comment);
776             g_free(comment);
777         }
779     /* fixme: Think (Lauris) */
780     sp_item_invoke_bbox(item, &pbox, Geom::identity(), TRUE);
781     dbox.x0 = 0.0;
782     dbox.y0 = 0.0;
783     dbox.x1 = sp_document_width (SP_OBJECT_DOCUMENT (item));
784     dbox.y1 = sp_document_height (SP_OBJECT_DOCUMENT (item));
785     sp_item_bbox_desktop (item, &bbox);
786     Geom::Matrix const i2d(sp_item_i2d_affine(item));
788     SPStyle* style = SP_OBJECT_STYLE (item);
790     if (!style->fill.isNone()) {
791         sp_print_fill (ctx, shape->curve->get_pathvector(), &i2d, style, &pbox, &dbox, &bbox);
792     }
794     if (!style->stroke.isNone()) {
795         sp_print_stroke (ctx, shape->curve->get_pathvector(), &i2d, style, &pbox, &dbox, &bbox);
796     }
798     /** \todo make code prettier */
799     Geom::PathVector const & pathv = shape->curve->get_pathvector();
800     // START marker
801     for (int i = 0; i < 2; i++) {  // SP_MARKER_LOC and SP_MARKER_LOC_START    
802         if ( shape->marker[i] ) {
803             Geom::Matrix tr(sp_shape_marker_get_transform_at_start(pathv.begin()->front()));
804             sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
805         }
806     }
807     // MID marker
808     for (int i = 0; i < 3; i += 2) {  // SP_MARKER_LOC and SP_MARKER_LOC_MID
809         if (shape->marker[i]) {
810             for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
811                 // START position
812                 if ( path_it != pathv.begin() 
813                      && ! ((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
814                 {
815                     Geom::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front()));
816                     sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
817                 }
818                 // MID position
819                 if ( path_it->size_default() > 1) {
820                     Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
821                     Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
822                     while (curve_it2 != path_it->end_default())
823                     {
824                         /* Put marker between curve_it1 and curve_it2.
825                          * Loop to end_default (so including closing segment), because when a path is closed,
826                          * there should be a midpoint marker between last segment and closing straight line segment */
827                         Geom::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
829                         sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
831                         ++curve_it1;
832                         ++curve_it2;
833                     }
834                 }
835                 if ( path_it != (pathv.end()-1) && !path_it->empty()) {
836                     Geom::Curve const &lastcurve = path_it->back_default();
837                     Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
838                     sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
839                 }
840             }
841         }
842     }
843     // END marker
844     if ( shape->marker[SP_MARKER_LOC_END] || shape->marker[SP_MARKER_LOC]) {
845         /* Get reference to last curve in the path.
846          * For moveto-only path, this returns the "closing line segment". */
847         Geom::Path const &path_last = pathv.back();
848         unsigned int index = path_last.size_default();
849         if (index > 0) {
850             index--;
851         }
852         Geom::Curve const &lastcurve = path_last[index];
854         Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
856         for (int i = 0; i < 4; i += 3) {  // SP_MARKER_LOC and SP_MARKER_LOC_END
857             if (shape->marker[i]) {
858                 sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
859             }
860         }
861     }
863         if (add_comments) {
864             gchar * comment = g_strdup_printf("end '%s'",
865                                               SP_OBJECT(item)->defaultLabel());
866             sp_print_comment(ctx, comment);
867             g_free(comment);
868         }
871 /**
872  * Sets style, path, and paintbox.  Updates marker views, including dimensions.
873  */
874 static NRArenaItem *
875 sp_shape_show (SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int /*flags*/)
877     SPObject *object = SP_OBJECT(item);
878     SPShape *shape = SP_SHAPE(item);
880     NRArenaItem *arenaitem = NRArenaShape::create(arena);
881     NRArenaShape * const s = NR_ARENA_SHAPE(arenaitem);
882     nr_arena_shape_set_style(s, object->style);
883     nr_arena_shape_set_path(s, shape->curve, false);
884     Geom::OptRect paintbox = item->getBounds(Geom::identity());
885     if (paintbox) {
886         s->setPaintBox(*paintbox);
887     }
889     /* This stanza checks that an object's marker style agrees with
890      * the marker objects it has allocated.  sp_shape_set_marker ensures
891      * that the appropriate marker objects are present (or absent) to
892      * match the style.
893      */
894     for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
895         sp_shape_set_marker (object, i, object->style->marker[i].value);
896       }
898     if (sp_shape_has_markers (shape)) {
900         /* provide key and dimension the marker views */
901         if (!arenaitem->key) {
902             NR_ARENA_ITEM_SET_KEY (arenaitem, sp_item_display_key_new (SP_MARKER_LOC_QTY));
903         }
905         for (int i = 0; i < SP_MARKER_LOC_QTY; i++) {
906             if (shape->marker[i]) {
907                 sp_marker_show_dimension ((SPMarker *) shape->marker[i],
908                                           NR_ARENA_ITEM_GET_KEY (arenaitem) + i,
909                                           sp_shape_number_of_markers (shape, i));
910             }
911         }
913         /* Update marker views */
914         sp_shape_update_marker_view (shape, arenaitem);
915     }
917     return arenaitem;
920 /**
921  * Hides/removes marker views from the shape.
922  */
923 static void
924 sp_shape_hide (SPItem *item, unsigned int key)
926     SPShape *shape;
927     SPItemView *v;
928     int i;
930     shape = (SPShape *) item;
932     for (i=0; i<SP_MARKER_LOC_QTY; i++) {
933       if (shape->marker[i]) {
934         for (v = item->display; v != NULL; v = v->next) {
935                 if (key == v->key) {
936           sp_marker_hide ((SPMarker *) shape->marker[i],
937                                     NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
938                 }
939         }
940       }
941     }
943     if (((SPItemClass *) parent_class)->hide) {
944       ((SPItemClass *) parent_class)->hide (item, key);
945     }
948 /**
949 * \param shape Shape.
950 * \return TRUE if the shape has any markers, or FALSE if not.
951 */
952 int
953 sp_shape_has_markers (SPShape const *shape)
955     /* Note, we're ignoring 'marker' settings, which technically should apply for
956        all three settings.  This should be fixed later such that if 'marker' is
957        specified, then all three should appear. */
959     return (
960         shape->curve &&
961         (shape->marker[SP_MARKER_LOC] ||
962          shape->marker[SP_MARKER_LOC_START] ||
963          shape->marker[SP_MARKER_LOC_MID] ||
964          shape->marker[SP_MARKER_LOC_END])
965         );
969 /**
970 * \param shape Shape.
971 * \param type Marker type (e.g. SP_MARKER_LOC_START)
972 * \return Number of markers that the shape has of this type.
973 */
974 int
975 sp_shape_number_of_markers (SPShape *shape, int type)
977     Geom::PathVector const & pathv = shape->curve->get_pathvector();
978     if (pathv.size() == 0) {
979         return 0;
980     }
982     switch(type) {
983         case SP_MARKER_LOC:
984         {
985             if ( shape->marker[SP_MARKER_LOC] ) {
986                 guint n = 0;
987                 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
988                     n += path_it->size_default() + 1;
989                 }
990                 return n;
991             } else {
992                 return 0;
993             }
994         }
995         case SP_MARKER_LOC_START:
996             // there is only a start marker on the first path of a pathvector
997             return shape->marker[SP_MARKER_LOC_START] ? 1 : 0;
999         case SP_MARKER_LOC_MID:
1000         {
1001             if ( shape->marker[SP_MARKER_LOC_MID] ) {
1002                 guint n = 0;
1003                 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
1004                     n += path_it->size_default() + 1;
1005                 }
1006                 return n - 2; // minus the start and end marker.
1007             } else {
1008                 return 0;
1009             }
1010         }
1012         case SP_MARKER_LOC_END:
1013         {
1014             // there is only an end marker on the last path of a pathvector
1015             return shape->marker[SP_MARKER_LOC_END] ? 1 : 0;
1016         }
1018         default:
1019             return 0;
1020     }
1023 /**
1024  * Checks if the given marker is used in the shape, and if so, it
1025  * releases it by calling sp_marker_hide.  Also detaches signals
1026  * and unrefs the marker from the shape.
1027  */
1028 static void
1029 sp_shape_marker_release (SPObject *marker, SPShape *shape)
1031     SPItem *item;
1032     int i;
1034     item = (SPItem *) shape;
1036     for (i = 0; i < SP_MARKER_LOC_QTY; i++) {
1037         if (marker == shape->marker[i]) {
1038             SPItemView *v;
1039             /* Hide marker */
1040             for (v = item->display; v != NULL; v = v->next) {
1041               sp_marker_hide ((SPMarker *) (shape->marker[i]), NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
1042               /* fixme: Do we need explicit remove here? (Lauris) */
1043               /* nr_arena_item_set_mask (v->arenaitem, NULL); */
1044             }
1045             /* Detach marker */
1046             shape->release_connect[i].disconnect();
1047             shape->modified_connect[i].disconnect();
1048             shape->marker[i] = sp_object_hunref (shape->marker[i], item);
1049         }
1050     }
1053 /**
1054  * No-op.  Exists for handling 'modified' messages
1055  */
1056 static void
1057 sp_shape_marker_modified (SPObject */*marker*/, guint /*flags*/, SPItem */*item*/)
1059     /* I think mask does update automagically */
1060     /* g_warning ("Item %s mask %s modified", SP_OBJECT_ID (item), SP_OBJECT_ID (mask)); */
1063 /**
1064  * Adds a new marker to shape object at the location indicated by key.  value
1065  * must be a valid URI reference resolvable from the shape object (i.e., present
1066  * in the document <defs>).  If the shape object already has a marker
1067  * registered at the given position, it is removed first.  Then the
1068  * new marker is hrefed and its signals connected.
1069  */
1070 void
1071 sp_shape_set_marker (SPObject *object, unsigned int key, const gchar *value)
1073     SPItem *item = (SPItem *) object;
1074     SPShape *shape = (SPShape *) object;
1076     if (key > SP_MARKER_LOC_END) {
1077         return;
1078     }
1080     SPObject *mrk = sp_css_uri_reference_resolve (SP_OBJECT_DOCUMENT (object), value);
1081     if (mrk != shape->marker[key]) {
1082         if (shape->marker[key]) {
1083             SPItemView *v;
1085             /* Detach marker */
1086             shape->release_connect[key].disconnect();
1087             shape->modified_connect[key].disconnect();
1089             /* Hide marker */
1090             for (v = item->display; v != NULL; v = v->next) {
1091                 sp_marker_hide ((SPMarker *) (shape->marker[key]),
1092                                 NR_ARENA_ITEM_GET_KEY (v->arenaitem) + key);
1093                 /* fixme: Do we need explicit remove here? (Lauris) */
1094                 /* nr_arena_item_set_mask (v->arenaitem, NULL); */
1095             }
1097             /* Unref marker */
1098             shape->marker[key] = sp_object_hunref (shape->marker[key], object);
1099         }
1100         if (SP_IS_MARKER (mrk)) {
1101             shape->marker[key] = sp_object_href (mrk, object);
1102             shape->release_connect[key] = mrk->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_shape_marker_release), shape));
1103             shape->modified_connect[key] = mrk->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_shape_marker_modified), shape));
1104         }
1105     }
1110 /* Shape section */
1112 /**
1113  * Calls any registered handlers for the set_shape action
1114  */
1115 void
1116 sp_shape_set_shape (SPShape *shape)
1118     g_return_if_fail (shape != NULL);
1119     g_return_if_fail (SP_IS_SHAPE (shape));
1121     if (SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape) {
1122       SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape (shape);
1123     }
1126 /**
1127  * Adds a curve to the shape.  If owner is specified, a reference
1128  * will be made, otherwise the curve will be copied into the shape.
1129  * Any existing curve in the shape will be unreferenced first.
1130  * This routine also triggers a request to update the display.
1131  */
1132 void
1133 sp_shape_set_curve (SPShape *shape, SPCurve *curve, unsigned int owner)
1135     if (shape->curve) {
1136         shape->curve = shape->curve->unref();
1137     }
1138     if (curve) {
1139         if (owner) {
1140             shape->curve = curve->ref();
1141         } else {
1142             shape->curve = curve->copy();
1143         }
1144     }
1145         SP_OBJECT(shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1148 /**
1149  * Return duplicate of curve (if any exists) or NULL if there is no curve
1150  */
1151 SPCurve *
1152 sp_shape_get_curve (SPShape *shape)
1154     if (shape->curve) {
1155         return shape->curve->copy();
1156     }
1157     return NULL;
1160 /**
1161  * Same as sp_shape_set_curve but without updating the display
1162  */
1163 void
1164 sp_shape_set_curve_insync (SPShape *shape, SPCurve *curve, unsigned int owner)
1166     if (shape->curve) {
1167         shape->curve = shape->curve->unref();
1168     }
1169     if (curve) {
1170         if (owner) {
1171             shape->curve = curve->ref();
1172         } else {
1173             shape->curve = curve->copy();
1174         }
1175     }
1178 /**
1179  * Return all nodes in a path that are to be considered for snapping
1180  */
1181 static void sp_shape_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs)
1183     g_assert(item != NULL);
1184     g_assert(SP_IS_SHAPE(item));
1186     SPShape const *shape = SP_SHAPE(item);
1187     if (shape->curve == NULL) {
1188         return;
1189     }
1191     // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes
1192     if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) {
1193         return;
1194     }
1196     Geom::PathVector const &pathv = shape->curve->get_pathvector();
1197     if (pathv.empty())
1198         return;
1200     Geom::Matrix const i2d (sp_item_i2d_affine (item));
1202     if (snapprefs->getSnapObjectMidpoints()) {
1203         Geom::OptRect bbox = item->getBounds(sp_item_i2d_affine(item));
1204         if (bbox) {
1205             p.push_back(Inkscape::SnapCandidatePoint(bbox->midpoint(), Inkscape::SNAPSOURCE_OBJECT_MIDPOINT, Inkscape::SNAPTARGET_OBJECT_MIDPOINT));
1206         }
1207     }
1209     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
1210         if (snapprefs->getSnapToItemNode()) {
1211             // Add the first point of the path
1212             p.push_back(Inkscape::SnapCandidatePoint(path_it->initialPoint() * i2d, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP));
1213         }
1215         Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
1216         Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
1217         while (curve_it1 != path_it->end_default())
1218         {
1219             // For each path: consider midpoints of line segments for snapping
1220             if (snapprefs->getSnapLineMidpoints()) { // only do this when we're snapping nodes (enforces strict snapping)
1221                 if (Geom::LineSegment const* line_segment = dynamic_cast<Geom::LineSegment const*>(&(*curve_it1))) {
1222                     p.push_back(Inkscape::SnapCandidatePoint(Geom::middle_point(*line_segment) * i2d, Inkscape::SNAPSOURCE_LINE_MIDPOINT, Inkscape::SNAPTARGET_LINE_MIDPOINT));
1223                 }
1224             }
1226             if (curve_it2 == path_it->end_default()) { // Test will only pass for the last iteration of the while loop
1227                 if (snapprefs->getSnapToItemNode() && !path_it->closed()) {
1228                     // Add the last point of the path, but only for open paths
1229                     // (for closed paths the first and last point will coincide)
1230                     p.push_back(Inkscape::SnapCandidatePoint((*curve_it1).finalPoint() * i2d, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP));
1231                 }
1232             } else {
1233                 /* Test whether to add the node between curve_it1 and curve_it2.
1234                  * Loop to end_default (so only iterating through the stroked part); */
1236                 Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2);
1238                 bool c1 = snapprefs->getSnapToItemNode() && (nodetype == Geom::NODE_CUSP || nodetype == Geom::NODE_NONE);
1239                 bool c2 = snapprefs->getSnapSmoothNodes() && (nodetype == Geom::NODE_SMOOTH || nodetype == Geom::NODE_SYMM);
1241                 if (c1 || c2) {
1242                     Inkscape::SnapSourceType sst;
1243                     Inkscape::SnapTargetType stt;
1244                     switch (nodetype) {
1245                     case Geom::NODE_CUSP:
1246                         sst = Inkscape::SNAPSOURCE_NODE_CUSP;
1247                         stt = Inkscape::SNAPTARGET_NODE_CUSP;
1248                         break;
1249                     case Geom::NODE_SMOOTH:
1250                     case Geom::NODE_SYMM:
1251                         sst = Inkscape::SNAPSOURCE_NODE_SMOOTH;
1252                         stt = Inkscape::SNAPTARGET_NODE_SMOOTH;
1253                         break;
1254                     default:
1255                         sst = Inkscape::SNAPSOURCE_UNDEFINED;
1256                         stt = Inkscape::SNAPTARGET_UNDEFINED;
1257                         break;
1258                     }
1259                     p.push_back(Inkscape::SnapCandidatePoint(curve_it1->finalPoint() * i2d, sst, stt));
1260                 }
1261             }
1263             ++curve_it1;
1264             ++curve_it2;
1265         }
1267         // Find the internal intersections of each path and consider these for snapping
1268         // (using "Method 1" as described in Inkscape::ObjectSnapper::_collectNodes())
1269         if (snapprefs->getSnapIntersectionCS()) {
1270             Geom::Crossings cs;
1271             cs = self_crossings(*path_it);
1272             if (cs.size() > 0) { // There might be multiple intersections...
1273                 for (Geom::Crossings::const_iterator i = cs.begin(); i != cs.end(); i++) {
1274                     Geom::Point p_ix = (*path_it).pointAt((*i).ta);
1275                     p.push_back(Inkscape::SnapCandidatePoint(p_ix * i2d, Inkscape::SNAPSOURCE_PATH_INTERSECTION, Inkscape::SNAPTARGET_PATH_INTERSECTION));
1276                 }
1277             }
1278         }
1279     }
1283 /*
1284   Local Variables:
1285   mode:c++
1286   c-file-style:"stroustrup"
1287   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1288   indent-tabs-mode:nil
1289   fill-column:99
1290   End:
1291 */
1292 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :