Code

A simple layout document as to what, why and how is cppification.
[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 SPLPEItemClass * SPShapeClass::parent_class=0;
81 /**
82  * Registers the SPShape class with Gdk and returns its type number.
83  */
84 GType
85 SPShape::getType (void)
86 {
87     static GType type = 0;
88     if (!type) {
89         GTypeInfo info = {
90             sizeof (SPShapeClass),
91             NULL, NULL,
92             (GClassInitFunc) SPShapeClass::sp_shape_class_init,
93             NULL, NULL,
94             sizeof (SPShape),
95             16,
96             (GInstanceInitFunc) sp_shape_init,
97             NULL,    /* value_table */
98         };
99         type = g_type_register_static (SP_TYPE_LPE_ITEM, "SPShape", &info, (GTypeFlags)0);
100     }
101     return type;
104 /**
105  * Initializes a SPShapeClass object.  Establishes the function pointers to the class'
106  * member routines in the class vtable, and sets pointers to parent classes.
107  */
108 void
109 SPShapeClass::sp_shape_class_init (SPShapeClass *klass)
111     GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
112     SPObjectClass *sp_object_class = SP_OBJECT_CLASS(klass);
113     SPItemClass * item_class = SP_ITEM_CLASS(klass);
114     SPLPEItemClass * lpe_item_class = SP_LPE_ITEM_CLASS(klass);
116     parent_class = (SPLPEItemClass *)g_type_class_peek_parent (klass);
118     gobject_class->finalize = SPShape::sp_shape_finalize;
120     sp_object_class->build = SPShape::sp_shape_build;
121     sp_object_class->release = SPShape::sp_shape_release;
122     sp_object_class->set = SPShape::sp_shape_set;
123     sp_object_class->update = SPShape::sp_shape_update;
124     sp_object_class->modified = SPShape::sp_shape_modified;
125     sp_object_class->write = SPShape::sp_shape_write;
127     item_class->bbox = SPShape::sp_shape_bbox;
128     item_class->print = sp_shape_print;
129     item_class->show = SPShape::sp_shape_show;
130     item_class->hide = SPShape::sp_shape_hide;
131     item_class->snappoints = SPShape::sp_shape_snappoints;
132     lpe_item_class->update_patheffect = NULL;
134     klass->set_shape = NULL;
137 /**
138  * Initializes an SPShape object.
139  */
140 void
141 SPShape::sp_shape_init (SPShape *shape)
143     for ( int i = 0 ; i < SP_MARKER_LOC_QTY ; i++ ) {
144         new (&shape->release_connect[i]) sigc::connection();
145         new (&shape->modified_connect[i]) sigc::connection();
146         shape->marker[i] = NULL;
147     }
148     shape->curve = NULL;
151 void
152 SPShape::sp_shape_finalize (GObject *object)
154     SPShape *shape=(SPShape *)object;
156     for ( int i = 0 ; i < SP_MARKER_LOC_QTY ; i++ ) {
157         shape->release_connect[i].disconnect();
158         shape->release_connect[i].~connection();
159         shape->modified_connect[i].disconnect();
160         shape->modified_connect[i].~connection();
161     }
163     if (((GObjectClass *) (SPShapeClass::parent_class))->finalize) {
164         (* ((GObjectClass *) (SPShapeClass::parent_class))->finalize)(object);
165     }
168 /**
169  * Virtual build callback for SPMarker.
170  *
171  * This is to be invoked immediately after creation of an SPShape.
172  *
173  * \see sp_object_build()
174  */
175 void
176 SPShape::sp_shape_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
178     if (((SPObjectClass *) (SPShapeClass::parent_class))->build) {
179        (*((SPObjectClass *) (SPShapeClass::parent_class))->build) (object, document, repr);
180     }
182     for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
183         sp_shape_set_marker (object, i, object->style->marker[i].value);
184       }
187 /**
188  * Removes, releases and unrefs all children of object
189  *
190  * This is the inverse of sp_shape_build().  It must be invoked as soon
191  * as the shape is removed from the tree, even if it is still referenced
192  * by other objects.  This routine also disconnects/unrefs markers and
193  * curves attached to it.
194  *
195  * \see sp_object_release()
196  */
197 void
198 SPShape::sp_shape_release (SPObject *object)
200     SPItem *item;
201     SPShape *shape;
202     SPItemView *v;
203     int i;
205     item = (SPItem *) object;
206     shape = (SPShape *) object;
208     for (i = 0; i < SP_MARKER_LOC_QTY; i++) {
209         if (shape->marker[i]) {
210             for (v = item->display; v != NULL; v = v->next) {
211               sp_marker_hide ((SPMarker *) shape->marker[i], NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
212             }
213             shape->release_connect[i].disconnect();
214             shape->modified_connect[i].disconnect();
215             shape->marker[i] = sp_object_hunref (shape->marker[i], object);
216         }
217     }
218     if (shape->curve) {
219         shape->curve = shape->curve->unref();
220     }
222     if (((SPObjectClass *) SPShapeClass::parent_class)->release) {
223       ((SPObjectClass *) SPShapeClass::parent_class)->release (object);
224     }
229 void
230 SPShape::sp_shape_set(SPObject *object, unsigned int key, gchar const *value)
232     if (((SPObjectClass *) SPShapeClass::parent_class)->set) {
233         ((SPObjectClass *) SPShapeClass::parent_class)->set(object, key, value);
234     }
237 Inkscape::XML::Node *
238 SPShape::sp_shape_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags)
240     if (((SPObjectClass *)(SPShapeClass::parent_class))->write) {
241         ((SPObjectClass *)(SPShapeClass::parent_class))->write(object, doc, repr, flags);
242     }
244     return repr;
247 /**
248  * Updates the shape when its attributes have changed.  Also establishes
249  * marker objects to match the style settings.
250  */
251 void
252 SPShape::sp_shape_update (SPObject *object, SPCtx *ctx, unsigned int flags)
254     SPItem *item = (SPItem *) object;
255     SPShape *shape = (SPShape *) object;
257     if (((SPObjectClass *) (SPShapeClass::parent_class))->update) {
258         (* ((SPObjectClass *) (SPShapeClass::parent_class))->update) (object, ctx, flags);
259     }
261     /* This stanza checks that an object's marker style agrees with
262      * the marker objects it has allocated.  sp_shape_set_marker ensures
263      * that the appropriate marker objects are present (or absent) to
264      * match the style.
265      */
266     for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
267         sp_shape_set_marker (object, i, object->style->marker[i].value);
268       }
270     if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
271         SPStyle *style;
272         style = SP_OBJECT_STYLE (object);
273         if (style->stroke_width.unit == SP_CSS_UNIT_PERCENT) {
274             SPItemCtx *ictx = (SPItemCtx *) ctx;
275             double const aw = 1.0 / NR::expansion(ictx->i2vp);
276             style->stroke_width.computed = style->stroke_width.value * aw;
277             for (SPItemView *v = ((SPItem *) (shape))->display; v != NULL; v = v->next) {
278                 nr_arena_shape_set_style ((NRArenaShape *) v->arenaitem, style);
279             }
280         }
281     }
283     if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) {
284         /* This is suboptimal, because changing parent style schedules recalculation */
285         /* But on the other hand - how can we know that parent does not tie style and transform */
286         Geom::OptRect paintbox = SP_ITEM(object)->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
287         for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
288             NRArenaShape * const s = NR_ARENA_SHAPE(v->arenaitem);
289             if (flags & SP_OBJECT_MODIFIED_FLAG) {
290                 nr_arena_shape_set_path(s, shape->curve, (flags & SP_OBJECT_USER_MODIFIED_FLAG_B));
291             }
292             if (paintbox) {
293                 s->setPaintBox(*paintbox);
294             }
295         }
296     }
298     if (shape->hasMarkers ()) {
299         /* Dimension marker views */
300         for (SPItemView *v = item->display; v != NULL; v = v->next) {
301             if (!v->arenaitem->key) {
302                 NR_ARENA_ITEM_SET_KEY (v->arenaitem, SPItem::display_key_new (SP_MARKER_LOC_QTY));
303             }
304             for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
305                 if (shape->marker[i]) {
306                     sp_marker_show_dimension ((SPMarker *) shape->marker[i],
307                                               NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i,
308                                               shape->numberOfMarkers (i));
309                 }
310             }
311         }
313         /* Update marker views */
314         for (SPItemView *v = item->display; v != NULL; v = v->next) {
315             sp_shape_update_marker_view (shape, v->arenaitem);
316         }
317     }
320 /**
321  * Calculate the transform required to get a marker's path object in the
322  * right place for particular path segment on a shape.
323  *
324  * \see sp_shape_marker_update_marker_view.
325  *
326  * From SVG spec:
327  * The axes of the temporary new user coordinate system are aligned according to the orient attribute on the 'marker'
328  * element and the slope of the curve at the given vertex. (Note: if there is a discontinuity at a vertex, the slope
329  * is the average of the slopes of the two segments of the curve that join at the given vertex. If a slope cannot be
330  * determined, the slope is assumed to be zero.)
331  *
332  * Reference: http://www.w3.org/TR/SVG11/painting.html#MarkerElement, the `orient' attribute.
333  * Reference for behaviour of zero-length segments:
334  * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
335  */
336 Geom::Matrix
337 sp_shape_marker_get_transform(Geom::Curve const & c1, Geom::Curve const & c2)
339     Geom::Point p = c1.pointAt(1);
340     Geom::Curve * c1_reverse = c1.reverse();
341     Geom::Point tang1 = - c1_reverse->unitTangentAt(0);
342     delete c1_reverse;
343     Geom::Point tang2 = c2.unitTangentAt(0);
345     double const angle1 = Geom::atan2(tang1);
346     double const angle2 = Geom::atan2(tang2);
348     double ret_angle;
349     ret_angle = .5 * (angle1 + angle2);
351     if ( fabs( angle2 - angle1 ) > M_PI ) {
352         /* ret_angle is in the middle of the larger of the two sectors between angle1 and
353          * angle2, so flip it by 180degrees to force it to the middle of the smaller sector.
354          *
355          * (Imagine a circle with rays drawn at angle1 and angle2 from the centre of the
356          * circle.  Those two rays divide the circle into two sectors.)
357          */
358         ret_angle += M_PI;
359     }
361     return Geom::Rotate(ret_angle) * Geom::Translate(p);
363 Geom::Matrix
364 sp_shape_marker_get_transform_at_start(Geom::Curve const & c)
366     Geom::Point p = c.pointAt(0);
367     Geom::Matrix ret = Geom::Translate(p);
369     if ( !c.isDegenerate() ) {
370         Geom::Point tang = c.unitTangentAt(0);
371         double const angle = Geom::atan2(tang);
372         ret = Geom::Rotate(angle) * Geom::Translate(p);
373     } else {
374         /* FIXME: the svg spec says to search for a better alternative than zero angle directionality:
375          * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes */
376     }
378     return ret;
380 Geom::Matrix
381 sp_shape_marker_get_transform_at_end(Geom::Curve const & c)
383     Geom::Point p = c.pointAt(1);
384     Geom::Matrix ret = Geom::Translate(p);
386     if ( !c.isDegenerate() ) {
387         Geom::Curve * c_reverse = c.reverse();
388         Geom::Point tang = - c_reverse->unitTangentAt(0);
389         delete c_reverse;
390         double const angle = Geom::atan2(tang);
391         ret = Geom::Rotate(angle) * Geom::Translate(p);
392     } else {
393         /* FIXME: the svg spec says to search for a better alternative than zero angle directionality:
394          * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes */
395     }
397     return ret;
400 /**
401  * Updates the instances (views) of a given marker in a shape.
402  * Marker views have to be scaled already.  The transformation
403  * is retrieved and then shown by calling sp_marker_show_instance.
404  *
405  * @todo figure out what to do when both 'marker' and for instance 'marker-end' are set.
406  */
407 void
408 SPShape::sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai)
410     SPStyle *style = ((SPObject *) shape)->style;
412     // position arguments to sp_marker_show_instance, basically counts the amount of markers.
413     int counter[4] = {0};
415     Geom::PathVector const & pathv = shape->curve->get_pathvector();
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 void
517 SPShape::sp_shape_modified (SPObject *object, unsigned int flags)
519     SPShape *shape = SP_SHAPE (object);
521     if (((SPObjectClass *) (SPShapeClass::parent_class))->modified) {
522       (* ((SPObjectClass *) (SPShapeClass::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 void SPShape::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 (shape->hasMarkers ()) {
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                                     marker_item->invoke_bbox ( &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                                     marker_item->invoke_bbox ( &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                                             marker_item->invoke_bbox ( &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                                     marker_item->invoke_bbox ( &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                                     marker_item->invoke_bbox ( &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     marker_item->invoke_print (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         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
773         gint add_comments = prefs->getBool("/printing/debug/add-label-comments");
774         if (add_comments) {
775             gchar * comment = g_strdup_printf("begin '%s'",
776                                               SP_OBJECT(item)->defaultLabel());
777             sp_print_comment(ctx, comment);
778             g_free(comment);
779         }
781     /* fixme: Think (Lauris) */
782     item->invoke_bbox( &pbox, Geom::identity(), TRUE);
783     dbox.x0 = 0.0;
784     dbox.y0 = 0.0;
785     dbox.x1 = SP_OBJECT_DOCUMENT (item)->getWidth ();
786     dbox.y1 = SP_OBJECT_DOCUMENT (item)->getHeight ();
787     item->getBboxDesktop (&bbox);
788     Geom::Matrix const i2d(item->i2d_affine());
790     SPStyle* style = SP_OBJECT_STYLE (item);
792     if (!style->fill.isNone()) {
793         sp_print_fill (ctx, shape->curve->get_pathvector(), &i2d, style, &pbox, &dbox, &bbox);
794     }
796     if (!style->stroke.isNone()) {
797         sp_print_stroke (ctx, shape->curve->get_pathvector(), &i2d, style, &pbox, &dbox, &bbox);
798     }
800     /** \todo make code prettier */
801     Geom::PathVector const & pathv = shape->curve->get_pathvector();
802     // START marker
803     for (int i = 0; i < 2; i++) {  // SP_MARKER_LOC and SP_MARKER_LOC_START    
804         if ( shape->marker[i] ) {
805             Geom::Matrix tr(sp_shape_marker_get_transform_at_start(pathv.begin()->front()));
806             sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
807         }
808     }
809     // MID marker
810     for (int i = 0; i < 3; i += 2) {  // SP_MARKER_LOC and SP_MARKER_LOC_MID
811         if (shape->marker[i]) {
812             for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
813                 // START position
814                 if ( path_it != pathv.begin() 
815                      && ! ((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
816                 {
817                     Geom::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front()));
818                     sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
819                 }
820                 // MID position
821                 if ( path_it->size_default() > 1) {
822                     Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
823                     Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
824                     while (curve_it2 != path_it->end_default())
825                     {
826                         /* Put marker between curve_it1 and curve_it2.
827                          * Loop to end_default (so including closing segment), because when a path is closed,
828                          * there should be a midpoint marker between last segment and closing straight line segment */
829                         Geom::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
831                         sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
833                         ++curve_it1;
834                         ++curve_it2;
835                     }
836                 }
837                 if ( path_it != (pathv.end()-1) && !path_it->empty()) {
838                     Geom::Curve const &lastcurve = path_it->back_default();
839                     Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
840                     sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
841                 }
842             }
843         }
844     }
845     // END marker
846     if ( shape->marker[SP_MARKER_LOC_END] || shape->marker[SP_MARKER_LOC]) {
847         /* Get reference to last curve in the path.
848          * For moveto-only path, this returns the "closing line segment". */
849         Geom::Path const &path_last = pathv.back();
850         unsigned int index = path_last.size_default();
851         if (index > 0) {
852             index--;
853         }
854         Geom::Curve const &lastcurve = path_last[index];
856         Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
858         for (int i = 0; i < 4; i += 3) {  // SP_MARKER_LOC and SP_MARKER_LOC_END
859             if (shape->marker[i]) {
860                 sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
861             }
862         }
863     }
865         if (add_comments) {
866             gchar * comment = g_strdup_printf("end '%s'",
867                                               SP_OBJECT(item)->defaultLabel());
868             sp_print_comment(ctx, comment);
869             g_free(comment);
870         }
873 /**
874  * Sets style, path, and paintbox.  Updates marker views, including dimensions.
875  */
876 NRArenaItem *
877 SPShape::sp_shape_show (SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int /*flags*/)
879     SPObject *object = SP_OBJECT(item);
880     SPShape *shape = SP_SHAPE(item);
882     NRArenaItem *arenaitem = NRArenaShape::create(arena);
883     NRArenaShape * const s = NR_ARENA_SHAPE(arenaitem);
884     nr_arena_shape_set_style(s, object->style);
885     nr_arena_shape_set_path(s, shape->curve, false);
886     Geom::OptRect paintbox = item->getBounds(Geom::identity());
887     if (paintbox) {
888         s->setPaintBox(*paintbox);
889     }
891     /* This stanza checks that an object's marker style agrees with
892      * the marker objects it has allocated.  sp_shape_set_marker ensures
893      * that the appropriate marker objects are present (or absent) to
894      * match the style.
895      */
896     for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
897         sp_shape_set_marker (object, i, object->style->marker[i].value);
898       }
900     if (shape->hasMarkers ()) {
902         /* provide key and dimension the marker views */
903         if (!arenaitem->key) {
904             NR_ARENA_ITEM_SET_KEY (arenaitem, SPItem::display_key_new (SP_MARKER_LOC_QTY));
905         }
907         for (int i = 0; i < SP_MARKER_LOC_QTY; i++) {
908             if (shape->marker[i]) {
909                 sp_marker_show_dimension ((SPMarker *) shape->marker[i],
910                                           NR_ARENA_ITEM_GET_KEY (arenaitem) + i,
911                                           shape->numberOfMarkers (i));
912             }
913         }
915         /* Update marker views */
916         sp_shape_update_marker_view (shape, arenaitem);
917     }
919     return arenaitem;
922 /**
923  * Hides/removes marker views from the shape.
924  */
925 void
926 SPShape::sp_shape_hide (SPItem *item, unsigned int key)
928     SPShape *shape;
929     SPItemView *v;
930     int i;
932     shape = (SPShape *) item;
934     for (i=0; i<SP_MARKER_LOC_QTY; i++) {
935       if (shape->marker[i]) {
936         for (v = item->display; v != NULL; v = v->next) {
937                 if (key == v->key) {
938           sp_marker_hide ((SPMarker *) shape->marker[i],
939                                     NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
940                 }
941         }
942       }
943     }
945     if (((SPItemClass *) SPShapeClass::parent_class)->hide) {
946       ((SPItemClass *) SPShapeClass::parent_class)->hide (item, key);
947     }
950 /**
951 * \param shape Shape.
952 * \return TRUE if the shape has any markers, or FALSE if not.
953 */
954 int
955 SPShape::hasMarkers () const
957     /* Note, we're ignoring 'marker' settings, which technically should apply for
958        all three settings.  This should be fixed later such that if 'marker' is
959        specified, then all three should appear. */
961     return (
962         this->curve &&
963         (this->marker[SP_MARKER_LOC] ||
964          this->marker[SP_MARKER_LOC_START] ||
965          this->marker[SP_MARKER_LOC_MID] ||
966          this->marker[SP_MARKER_LOC_END])
967         );
971 /**
972 * \param shape Shape.
973 * \param type Marker type (e.g. SP_MARKER_LOC_START)
974 * \return Number of markers that the shape has of this type.
975 */
976 int
977 SPShape::numberOfMarkers (int type)
979     Geom::PathVector const & pathv = this->curve->get_pathvector();
980     if (pathv.size() == 0) {
981         return 0;
982     }
984     switch(type) {
985         case SP_MARKER_LOC:
986         {
987             if ( this->marker[SP_MARKER_LOC] ) {
988                 guint n = 0;
989                 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
990                     n += path_it->size_default() + 1;
991                 }
992                 return n;
993             } else {
994                 return 0;
995             }
996         }
997         case SP_MARKER_LOC_START:
998             // there is only a start marker on the first path of a pathvector
999             return this->marker[SP_MARKER_LOC_START] ? 1 : 0;
1001         case SP_MARKER_LOC_MID:
1002         {
1003             if ( this->marker[SP_MARKER_LOC_MID] ) {
1004                 guint n = 0;
1005                 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
1006                     n += path_it->size_default() + 1;
1007                 }
1008                 return n - 2; // minus the start and end marker.
1009             } else {
1010                 return 0;
1011             }
1012         }
1014         case SP_MARKER_LOC_END:
1015         {
1016             // there is only an end marker on the last path of a pathvector
1017             return this->marker[SP_MARKER_LOC_END] ? 1 : 0;
1018         }
1020         default:
1021             return 0;
1022     }
1025 /**
1026  * Checks if the given marker is used in the shape, and if so, it
1027  * releases it by calling sp_marker_hide.  Also detaches signals
1028  * and unrefs the marker from the shape.
1029  */
1030 static void
1031 sp_shape_marker_release (SPObject *marker, SPShape *shape)
1033     SPItem *item;
1034     int i;
1036     item = (SPItem *) shape;
1038     for (i = 0; i < SP_MARKER_LOC_QTY; i++) {
1039         if (marker == shape->marker[i]) {
1040             SPItemView *v;
1041             /* Hide marker */
1042             for (v = item->display; v != NULL; v = v->next) {
1043               sp_marker_hide ((SPMarker *) (shape->marker[i]), NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
1044               /* fixme: Do we need explicit remove here? (Lauris) */
1045               /* nr_arena_item_set_mask (v->arenaitem, NULL); */
1046             }
1047             /* Detach marker */
1048             shape->release_connect[i].disconnect();
1049             shape->modified_connect[i].disconnect();
1050             shape->marker[i] = sp_object_hunref (shape->marker[i], item);
1051         }
1052     }
1055 /**
1056  * No-op.  Exists for handling 'modified' messages
1057  */
1058 static void
1059 sp_shape_marker_modified (SPObject */*marker*/, guint /*flags*/, SPItem */*item*/)
1061     /* I think mask does update automagically */
1062     /* g_warning ("Item %s mask %s modified", SP_OBJECT_ID (item), SP_OBJECT_ID (mask)); */
1065 /**
1066  * Adds a new marker to shape object at the location indicated by key.  value
1067  * must be a valid URI reference resolvable from the shape object (i.e., present
1068  * in the document <defs>).  If the shape object already has a marker
1069  * registered at the given position, it is removed first.  Then the
1070  * new marker is hrefed and its signals connected.
1071  */
1072 void
1073 sp_shape_set_marker (SPObject *object, unsigned int key, const gchar *value)
1075     SPItem *item = (SPItem *) object;
1076     SPShape *shape = (SPShape *) object;
1078     if (key > SP_MARKER_LOC_END) {
1079         return;
1080     }
1082     SPObject *mrk = sp_css_uri_reference_resolve (SP_OBJECT_DOCUMENT (object), value);
1083     if (mrk != shape->marker[key]) {
1084         if (shape->marker[key]) {
1085             SPItemView *v;
1087             /* Detach marker */
1088             shape->release_connect[key].disconnect();
1089             shape->modified_connect[key].disconnect();
1091             /* Hide marker */
1092             for (v = item->display; v != NULL; v = v->next) {
1093                 sp_marker_hide ((SPMarker *) (shape->marker[key]),
1094                                 NR_ARENA_ITEM_GET_KEY (v->arenaitem) + key);
1095                 /* fixme: Do we need explicit remove here? (Lauris) */
1096                 /* nr_arena_item_set_mask (v->arenaitem, NULL); */
1097             }
1099             /* Unref marker */
1100             shape->marker[key] = sp_object_hunref (shape->marker[key], object);
1101         }
1102         if (SP_IS_MARKER (mrk)) {
1103             shape->marker[key] = sp_object_href (mrk, object);
1104             shape->release_connect[key] = mrk->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_shape_marker_release), shape));
1105             shape->modified_connect[key] = mrk->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_shape_marker_modified), shape));
1106         }
1107     }
1112 /* Shape section */
1114 /**
1115  * Calls any registered handlers for the set_shape action
1116  */
1117 void
1118 SPShape::setShape ()
1120     //g_return_if_fail (shape != NULL);
1121     //g_return_if_fail (SP_IS_SHAPE (shape));
1123     if (SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (this))->set_shape) {
1124       SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (this))->set_shape (this);
1125     }
1128 /**
1129  * Adds a curve to the shape.  If owner is specified, a reference
1130  * will be made, otherwise the curve will be copied into the shape.
1131  * Any existing curve in the shape will be unreferenced first.
1132  * This routine also triggers a request to update the display.
1133  */
1134 void
1135 SPShape::setCurve (SPCurve *curve, unsigned int owner)
1137     if (this->curve) {
1138         this->curve = this->curve->unref();
1139     }
1140     if (curve) {
1141         if (owner) {
1142             this->curve = curve->ref();
1143         } else {
1144             this->curve = curve->copy();
1145         }
1146     }
1147         SP_OBJECT(this)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1150 /**
1151  * Return duplicate of curve (if any exists) or NULL if there is no curve
1152  */
1153 SPCurve *
1154 SPShape::getCurve ()
1156     if (this->curve) {
1157         return this->curve->copy();
1158     }
1159     return NULL;
1162 /**
1163  * Same as sp_shape_set_curve but without updating the display
1164  */
1165 void
1166 SPShape::setCurveInsync (SPCurve *curve, unsigned int owner)
1168     if (this->curve) {
1169         this->curve = this->curve->unref();
1170     }
1171     if (curve) {
1172         if (owner) {
1173             this->curve = curve->ref();
1174         } else {
1175             this->curve = curve->copy();
1176         }
1177     }
1180 /**
1181  * Return all nodes in a path that are to be considered for snapping
1182  */
1183 void SPShape::sp_shape_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs)
1185     g_assert(item != NULL);
1186     g_assert(SP_IS_SHAPE(item));
1188     SPShape const *shape = SP_SHAPE(item);
1189     if (shape->curve == NULL) {
1190         return;
1191     }
1193     // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes
1194     if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) {
1195         return;
1196     }
1198     Geom::PathVector const &pathv = shape->curve->get_pathvector();
1199     if (pathv.empty())
1200         return;
1202     Geom::Matrix const i2d (item->i2d_affine ());
1204     if (snapprefs->getSnapObjectMidpoints()) {
1205         Geom::OptRect bbox = item->getBounds(item->i2d_affine());
1206         if (bbox) {
1207             p.push_back(Inkscape::SnapCandidatePoint(bbox->midpoint(), Inkscape::SNAPSOURCE_OBJECT_MIDPOINT, Inkscape::SNAPTARGET_OBJECT_MIDPOINT));
1208         }
1209     }
1211     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
1212         if (snapprefs->getSnapToItemNode()) {
1213             p.push_back(Inkscape::SnapCandidatePoint(path_it->initialPoint() * i2d, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP));
1214         }
1216         Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
1217         Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
1218         while (curve_it2 != path_it->end_closed())
1219         {
1220             /* Test whether to add the node between curve_it1 and curve_it2.
1221              * Loop to end_closed (so always including closing segment); the last node to be added
1222              * is the node between the closing segment and the segment before that, regardless
1223              * of the path being closed or not. If the path is closed, the final point was already added by
1224              * adding the initial point. */
1226             Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2);
1228             bool c1 = snapprefs->getSnapToItemNode() && (nodetype == Geom::NODE_CUSP || nodetype == Geom::NODE_NONE);
1229             bool c2 = snapprefs->getSnapSmoothNodes() && (nodetype == Geom::NODE_SMOOTH || nodetype == Geom::NODE_SYMM);
1231             if (c1 || c2) {
1232                 Inkscape::SnapSourceType sst;
1233                 Inkscape::SnapTargetType stt;
1234                 switch (nodetype) {
1235                 case Geom::NODE_CUSP:
1236                     sst = Inkscape::SNAPSOURCE_NODE_CUSP;
1237                     stt = Inkscape::SNAPTARGET_NODE_CUSP;
1238                     break;
1239                 case Geom::NODE_SMOOTH:
1240                 case Geom::NODE_SYMM:
1241                     sst = Inkscape::SNAPSOURCE_NODE_SMOOTH;
1242                     stt = Inkscape::SNAPTARGET_NODE_SMOOTH;
1243                     break;
1244                 default:
1245                     sst = Inkscape::SNAPSOURCE_UNDEFINED;
1246                     stt = Inkscape::SNAPTARGET_UNDEFINED;
1247                     break;
1248                 }
1249                 p.push_back(Inkscape::SnapCandidatePoint(curve_it1->finalPoint() * i2d, sst, stt));
1250             }
1252             // Consider midpoints of line segments for snapping
1253             if (snapprefs->getSnapLineMidpoints()) { // only do this when we're snapping nodes (enforce strict snapping)
1254                 if (Geom::LineSegment const* line_segment = dynamic_cast<Geom::LineSegment const*>(&(*curve_it1))) {
1255                     p.push_back(Inkscape::SnapCandidatePoint(Geom::middle_point(*line_segment) * i2d, Inkscape::SNAPSOURCE_LINE_MIDPOINT, Inkscape::SNAPTARGET_LINE_MIDPOINT));
1256                 }
1257             }
1259             ++curve_it1;
1260             ++curve_it2;
1261         }
1263         // Find the internal intersections of each path and consider these for snapping
1264         // (using "Method 1" as described in Inkscape::ObjectSnapper::_collectNodes())
1265         if (snapprefs->getSnapIntersectionCS()) {
1266             Geom::Crossings cs;
1267             cs = self_crossings(*path_it);
1268             if (cs.size() > 0) { // There might be multiple intersections...
1269                 for (Geom::Crossings::const_iterator i = cs.begin(); i != cs.end(); i++) {
1270                     Geom::Point p_ix = (*path_it).pointAt((*i).ta);
1271                     p.push_back(Inkscape::SnapCandidatePoint(p_ix * i2d, Inkscape::SNAPSOURCE_PATH_INTERSECTION, Inkscape::SNAPTARGET_PATH_INTERSECTION));
1272                 }
1273             }
1274         }
1275     }
1279 /*
1280   Local Variables:
1281   mode:c++
1282   c-file-style:"stroustrup"
1283   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1284   indent-tabs-mode:nil
1285   fill-column:99
1286   End:
1287 */
1288 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :