Code

Connector tool: make connectors avoid the convex hull of shapes.
[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 #define noSHAPE_VERBOSE
55 static void sp_shape_class_init (SPShapeClass *klass);
56 static void sp_shape_init (SPShape *shape);
57 static void sp_shape_finalize (GObject *object);
59 static void sp_shape_build (SPObject * object, SPDocument * document, Inkscape::XML::Node * repr);
60 static void sp_shape_release (SPObject *object);
62 static void sp_shape_set(SPObject *object, unsigned key, gchar const *value);
63 static void sp_shape_update (SPObject *object, SPCtx *ctx, unsigned int flags);
64 static void sp_shape_modified (SPObject *object, unsigned int flags);
65 static Inkscape::XML::Node *sp_shape_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
67 static void sp_shape_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags);
68 void sp_shape_print (SPItem * item, SPPrintContext * ctx);
69 static NRArenaItem *sp_shape_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags);
70 static void sp_shape_hide (SPItem *item, unsigned int key);
71 static void sp_shape_snappoints (SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs);
73 static void sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai);
75 static SPLPEItemClass *parent_class;
77 /**
78  * Registers the SPShape class with Gdk and returns its type number.
79  */
80 GType
81 sp_shape_get_type (void)
82 {
83     static GType type = 0;
84     if (!type) {
85         GTypeInfo info = {
86             sizeof (SPShapeClass),
87             NULL, NULL,
88             (GClassInitFunc) sp_shape_class_init,
89             NULL, NULL,
90             sizeof (SPShape),
91             16,
92             (GInstanceInitFunc) sp_shape_init,
93             NULL,    /* value_table */
94         };
95         type = g_type_register_static (SP_TYPE_LPE_ITEM, "SPShape", &info, (GTypeFlags)0);
96     }
97     return type;
98 }
100 /**
101  * Initializes a SPShapeClass object.  Establishes the function pointers to the class'
102  * member routines in the class vtable, and sets pointers to parent classes.
103  */
104 static void
105 sp_shape_class_init (SPShapeClass *klass)
107     GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
108     SPObjectClass *sp_object_class = SP_OBJECT_CLASS(klass);
109     SPItemClass * item_class = SP_ITEM_CLASS(klass);
110     SPLPEItemClass * lpe_item_class = SP_LPE_ITEM_CLASS(klass);
112     parent_class = (SPLPEItemClass *)g_type_class_peek_parent (klass);
114     gobject_class->finalize = sp_shape_finalize;
116     sp_object_class->build = sp_shape_build;
117     sp_object_class->release = sp_shape_release;
118     sp_object_class->set = sp_shape_set;
119     sp_object_class->update = sp_shape_update;
120     sp_object_class->modified = sp_shape_modified;
121     sp_object_class->write = sp_shape_write;
123     item_class->bbox = sp_shape_bbox;
124     item_class->print = sp_shape_print;
125     item_class->show = sp_shape_show;
126     item_class->hide = sp_shape_hide;
127     item_class->snappoints = sp_shape_snappoints;
128     lpe_item_class->update_patheffect = NULL;
130     klass->set_shape = NULL;
133 /**
134  * Initializes an SPShape object.
135  */
136 static void
137 sp_shape_init (SPShape *shape)
139     for ( int i = 0 ; i < SP_MARKER_LOC_QTY ; i++ ) {
140         new (&shape->release_connect[i]) sigc::connection();
141         new (&shape->modified_connect[i]) sigc::connection();
142         shape->marker[i] = NULL;
143     }
144     shape->curve = NULL;
147 static void
148 sp_shape_finalize (GObject *object)
150     SPShape *shape=(SPShape *)object;
152     for ( int i = 0 ; i < SP_MARKER_LOC_QTY ; i++ ) {
153         shape->release_connect[i].disconnect();
154         shape->release_connect[i].~connection();
155         shape->modified_connect[i].disconnect();
156         shape->modified_connect[i].~connection();
157     }
159     if (((GObjectClass *) (parent_class))->finalize) {
160         (* ((GObjectClass *) (parent_class))->finalize)(object);
161     }
164 /**
165  * Virtual build callback for SPMarker.
166  *
167  * This is to be invoked immediately after creation of an SPShape.
168  *
169  * \see sp_object_build()
170  */
171 static void
172 sp_shape_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
174     if (((SPObjectClass *) (parent_class))->build) {
175        (*((SPObjectClass *) (parent_class))->build) (object, document, repr);
176     }
178     for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
179         sp_shape_set_marker (object, i, object->style->marker[i].value);
180       }
183 /**
184  * Removes, releases and unrefs all children of object
185  *
186  * This is the inverse of sp_shape_build().  It must be invoked as soon
187  * as the shape is removed from the tree, even if it is still referenced
188  * by other objects.  This routine also disconnects/unrefs markers and
189  * curves attached to it.
190  *
191  * \see sp_object_release()
192  */
193 static void
194 sp_shape_release (SPObject *object)
196     SPItem *item;
197     SPShape *shape;
198     SPItemView *v;
199     int i;
201     item = (SPItem *) object;
202     shape = (SPShape *) object;
204     for (i = 0; i < SP_MARKER_LOC_QTY; i++) {
205         if (shape->marker[i]) {
206             for (v = item->display; v != NULL; v = v->next) {
207               sp_marker_hide ((SPMarker *) shape->marker[i], NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
208             }
209             shape->release_connect[i].disconnect();
210             shape->modified_connect[i].disconnect();
211             shape->marker[i] = sp_object_hunref (shape->marker[i], object);
212         }
213     }
214     if (shape->curve) {
215         shape->curve = shape->curve->unref();
216     }
218     if (((SPObjectClass *) parent_class)->release) {
219       ((SPObjectClass *) parent_class)->release (object);
220     }
225 static void
226 sp_shape_set(SPObject *object, unsigned int key, gchar const *value)
228     if (((SPObjectClass *) parent_class)->set) {
229         ((SPObjectClass *) parent_class)->set(object, key, value);
230     }
233 static Inkscape::XML::Node *
234 sp_shape_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags)
236     if (((SPObjectClass *)(parent_class))->write) {
237         ((SPObjectClass *)(parent_class))->write(object, doc, repr, flags);
238     }
240     return repr;
243 /**
244  * Updates the shape when its attributes have changed.  Also establishes
245  * marker objects to match the style settings.
246  */
247 static void
248 sp_shape_update (SPObject *object, SPCtx *ctx, unsigned int flags)
250     SPItem *item = (SPItem *) object;
251     SPShape *shape = (SPShape *) object;
253     if (((SPObjectClass *) (parent_class))->update) {
254         (* ((SPObjectClass *) (parent_class))->update) (object, ctx, flags);
255     }
257     /* This stanza checks that an object's marker style agrees with
258      * the marker objects it has allocated.  sp_shape_set_marker ensures
259      * that the appropriate marker objects are present (or absent) to
260      * match the style.
261      */
262     for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
263         sp_shape_set_marker (object, i, object->style->marker[i].value);
264       }
266     if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
267         SPStyle *style;
268         style = SP_OBJECT_STYLE (object);
269         if (style->stroke_width.unit == SP_CSS_UNIT_PERCENT) {
270             SPItemCtx *ictx = (SPItemCtx *) ctx;
271             double const aw = 1.0 / NR::expansion(ictx->i2vp);
272             style->stroke_width.computed = style->stroke_width.value * aw;
273             for (SPItemView *v = ((SPItem *) (shape))->display; v != NULL; v = v->next) {
274                 nr_arena_shape_set_style ((NRArenaShape *) v->arenaitem, style);
275             }
276         }
277     }
279     if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) {
280         /* This is suboptimal, because changing parent style schedules recalculation */
281         /* But on the other hand - how can we know that parent does not tie style and transform */
282         Geom::OptRect paintbox = SP_ITEM(object)->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
283         for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
284             NRArenaShape * const s = NR_ARENA_SHAPE(v->arenaitem);
285             if (flags & SP_OBJECT_MODIFIED_FLAG) {
286                 nr_arena_shape_set_path(s, shape->curve, (flags & SP_OBJECT_USER_MODIFIED_FLAG_B));
287             }
288             if (paintbox) {
289                 s->setPaintBox(*paintbox);
290             }
291         }
292     }
294     if (sp_shape_has_markers (shape)) {
295         /* Dimension marker views */
296         for (SPItemView *v = item->display; v != NULL; v = v->next) {
297             if (!v->arenaitem->key) {
298                 NR_ARENA_ITEM_SET_KEY (v->arenaitem, sp_item_display_key_new (SP_MARKER_LOC_QTY));
299             }
300             for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
301                 if (shape->marker[i]) {
302                     sp_marker_show_dimension ((SPMarker *) shape->marker[i],
303                                               NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i,
304                                               sp_shape_number_of_markers (shape, i));
305                 }
306             }
307         }
309         /* Update marker views */
310         for (SPItemView *v = item->display; v != NULL; v = v->next) {
311             sp_shape_update_marker_view (shape, v->arenaitem);
312         }
313     }
316 /**
317  * Calculate the transform required to get a marker's path object in the
318  * right place for particular path segment on a shape.
319  *
320  * \see sp_shape_marker_update_marker_view.
321  *
322  * From SVG spec:
323  * The axes of the temporary new user coordinate system are aligned according to the orient attribute on the 'marker'
324  * element and the slope of the curve at the given vertex. (Note: if there is a discontinuity at a vertex, the slope
325  * is the average of the slopes of the two segments of the curve that join at the given vertex. If a slope cannot be
326  * determined, the slope is assumed to be zero.)
327  *
328  * Reference: http://www.w3.org/TR/SVG11/painting.html#MarkerElement, the `orient' attribute.
329  * Reference for behaviour of zero-length segments:
330  * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
331  */
332 Geom::Matrix
333 sp_shape_marker_get_transform(Geom::Curve const & c1, Geom::Curve const & c2)
335     Geom::Point p = c1.pointAt(1);
336     Geom::Curve * c1_reverse = c1.reverse();
337     Geom::Point tang1 = - c1_reverse->unitTangentAt(0);
338     delete c1_reverse;
339     Geom::Point tang2 = c2.unitTangentAt(0);
341     double const angle1 = Geom::atan2(tang1);
342     double const angle2 = Geom::atan2(tang2);
344     double ret_angle;
345     ret_angle = .5 * (angle1 + angle2);
347     if ( fabs( angle2 - angle1 ) > M_PI ) {
348         /* ret_angle is in the middle of the larger of the two sectors between angle1 and
349          * angle2, so flip it by 180degrees to force it to the middle of the smaller sector.
350          *
351          * (Imagine a circle with rays drawn at angle1 and angle2 from the centre of the
352          * circle.  Those two rays divide the circle into two sectors.)
353          */
354         ret_angle += M_PI;
355     }
357     return Geom::Rotate(ret_angle) * Geom::Translate(p);
359 Geom::Matrix
360 sp_shape_marker_get_transform_at_start(Geom::Curve const & c)
362     Geom::Point p = c.pointAt(0);
363     Geom::Matrix ret = Geom::Translate(p);
365     if ( !c.isDegenerate() ) {
366         Geom::Point tang = c.unitTangentAt(0);
367         double const angle = Geom::atan2(tang);
368         ret = Geom::Rotate(angle) * Geom::Translate(p);
369     } else {
370         /* FIXME: the svg spec says to search for a better alternative than zero angle directionality:
371          * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes */
372     }
374     return ret;
376 Geom::Matrix
377 sp_shape_marker_get_transform_at_end(Geom::Curve const & c)
379     Geom::Point p = c.pointAt(1);
380     Geom::Matrix ret = Geom::Translate(p);
382     if ( !c.isDegenerate() ) {
383         Geom::Curve * c_reverse = c.reverse();
384         Geom::Point tang = - c_reverse->unitTangentAt(0);
385         delete c_reverse;
386         double const angle = Geom::atan2(tang);
387         ret = Geom::Rotate(angle) * Geom::Translate(p);
388     } else {
389         /* FIXME: the svg spec says to search for a better alternative than zero angle directionality:
390          * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes */
391     }
393     return ret;
396 /**
397  * Updates the instances (views) of a given marker in a shape.
398  * Marker views have to be scaled already.  The transformation
399  * is retrieved and then shown by calling sp_marker_show_instance.
400  *
401  * @todo figure out what to do when both 'marker' and for instance 'marker-end' are set.
402  */
403 static void
404 sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai)
406     SPStyle *style = ((SPObject *) shape)->style;
408     // position arguments to sp_marker_show_instance, basically counts the amount of markers.
409     int counter[4] = {0};
411     Geom::PathVector const & pathv = shape->curve->get_pathvector();
413     // the first vertex should get a start marker, the last an end marker, and all the others a mid marker
414     // see bug 456148
416     // START marker
417     {
418         Geom::Matrix const m (sp_shape_marker_get_transform_at_start(pathv.begin()->front()));
419         for (int i = 0; i < 2; i++) {  // SP_MARKER_LOC and SP_MARKER_LOC_START
420             if ( shape->marker[i] ) {
421                 sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
422                                          NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
423                                          style->stroke_width.computed);
424                  counter[i]++;
425             }
426         }
427     }
429     // MID marker
430     if (shape->marker[SP_MARKER_LOC_MID] || shape->marker[SP_MARKER_LOC]) {
431         for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
432             // START position
433             if ( path_it != pathv.begin() 
434                  && ! ((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
435             {
436                 Geom::Matrix const m (sp_shape_marker_get_transform_at_start(path_it->front()));
437                 for (int i = 0; i < 3; i += 2) {  // SP_MARKER_LOC and SP_MARKER_LOC_MID
438                     if ( shape->marker[i] ) {
439                         sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
440                                                  NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
441                                                  style->stroke_width.computed);
442                          counter[i]++;
443                     }
444                 }
445             }
446             // MID position
447             if ( path_it->size_default() > 1) {
448                 Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
449                 Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
450                 while (curve_it2 != path_it->end_default())
451                 {
452                     /* Put marker between curve_it1 and curve_it2.
453                      * Loop to end_default (so including closing segment), because when a path is closed,
454                      * there should be a midpoint marker between last segment and closing straight line segment
455                      */
456                     Geom::Matrix const m (sp_shape_marker_get_transform(*curve_it1, *curve_it2));
457                     for (int i = 0; i < 3; i += 2) {  // SP_MARKER_LOC and SP_MARKER_LOC_MID
458                         if (shape->marker[i]) {
459                             sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
460                                                      NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
461                                                      style->stroke_width.computed);
462                             counter[i]++;
463                         }
464                     }
466                     ++curve_it1;
467                     ++curve_it2;
468                 }
469             }
470             // END position
471             if ( path_it != (pathv.end()-1) && !path_it->empty()) {
472                 Geom::Curve const &lastcurve = path_it->back_default();
473                 Geom::Matrix const m = sp_shape_marker_get_transform_at_end(lastcurve);
474                 for (int i = 0; i < 3; i += 2) {  // SP_MARKER_LOC and SP_MARKER_LOC_MID
475                     if (shape->marker[i]) {
476                         sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
477                                                  NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
478                                                  style->stroke_width.computed);
479                         counter[i]++;
480                     }
481                 }
482             }
483         }
484     }
486     // END marker
487     if ( shape->marker[SP_MARKER_LOC_END] || shape->marker[SP_MARKER_LOC] ) {
488         /* Get reference to last curve in the path.
489          * For moveto-only path, this returns the "closing line segment". */
490         Geom::Path const &path_last = pathv.back();
491         unsigned int index = path_last.size_default();
492         if (index > 0) {
493             index--;
494         }
495         Geom::Curve const &lastcurve = path_last[index];
496         Geom::Matrix const m = sp_shape_marker_get_transform_at_end(lastcurve);
498         for (int i = 0; i < 4; i += 3) {  // SP_MARKER_LOC and SP_MARKER_LOC_END
499             if (shape->marker[i]) {
500                 sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
501                                          NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
502                                          style->stroke_width.computed);
503                 counter[i]++;
504             }
505         }
506     }
509 /**
510  * Sets modified flag for all sub-item views.
511  */
512 static void
513 sp_shape_modified (SPObject *object, unsigned int flags)
515     SPShape *shape = SP_SHAPE (object);
517     if (((SPObjectClass *) (parent_class))->modified) {
518       (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
519     }
521     if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
522         for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
523             nr_arena_shape_set_style (NR_ARENA_SHAPE (v->arenaitem), object->style);
524         }
525     }
528 /**
529  * Calculates the bounding box for item, storing it into bbox.
530  * This also includes the bounding boxes of any markers included in the shape.
531  */
532 static void sp_shape_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags)
534     SPShape const *shape = SP_SHAPE (item);
535     if (shape->curve) {
536         Geom::OptRect geombbox = bounds_exact_transformed(shape->curve->get_pathvector(), transform);
537         if (geombbox) {
538             NRRect  cbbox;
539             cbbox.x0 = (*geombbox)[0][0];
540             cbbox.y0 = (*geombbox)[1][0];
541             cbbox.x1 = (*geombbox)[0][1];
542             cbbox.y1 = (*geombbox)[1][1];
544             if ((SPItem::BBoxType) flags != SPItem::GEOMETRIC_BBOX) {
546                 SPStyle* style=SP_OBJECT_STYLE (item);
547                 if (!style->stroke.isNone()) {
548                     double const scale = transform.descrim();
549                     if ( fabs(style->stroke_width.computed * scale) > 0.01 ) { // sinon c'est 0=oon veut pas de bord
550                         double const width = MAX(0.125, style->stroke_width.computed * scale);
551                         if ( fabs(cbbox.x1-cbbox.x0) > -0.00001 && fabs(cbbox.y1-cbbox.y0) > -0.00001 ) {
552                             cbbox.x0-=0.5*width;
553                             cbbox.x1+=0.5*width;
554                             cbbox.y0-=0.5*width;
555                             cbbox.y1+=0.5*width;
556                         }
557                     }
558                 }
560                 // Union with bboxes of the markers, if any
561                 if (sp_shape_has_markers (shape)) {
562                     /** \todo make code prettier! */
563                     Geom::PathVector const & pathv = shape->curve->get_pathvector();
564                     // START marker
565                     for (unsigned i = 0; i < 2; i++) { // SP_MARKER_LOC and SP_MARKER_LOC_START
566                         if ( shape->marker[i] ) {
567                             SPMarker* marker = SP_MARKER (shape->marker[i]);
568                             SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
570                             if (marker_item) {
571                                 Geom::Matrix tr(sp_shape_marker_get_transform_at_start(pathv.begin()->front()));
572                                 if (!marker->orient_auto) {
573                                     Geom::Point transl = tr.translation();
574                                     tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
575                                 }
576                                 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
577                                     tr = Geom::Scale(style->stroke_width.computed) * tr;
578                                 }
580                                 // total marker transform
581                                 tr = marker_item->transform * marker->c2p * tr * transform;
583                                 // get bbox of the marker with that transform
584                                 NRRect marker_bbox;
585                                 sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true);
586                                 // union it with the shape bbox
587                                 nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
588                             }
589                         }
590                     }
591                     // MID marker
592                     for (unsigned i = 0; i < 3; i += 2) { // SP_MARKER_LOC and SP_MARKER_LOC_MID
593                         SPMarker* marker = SP_MARKER (shape->marker[i]);
594                         if ( !shape->marker[i] ) continue;
595                         SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
596                         if ( !marker_item ) continue;
598                         for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
599                             // START position
600                             if ( path_it != pathv.begin() 
601                                  && ! ((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
602                             {
603                                 Geom::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front()));
604                                 if (!marker->orient_auto) {
605                                     Geom::Point transl = tr.translation();
606                                     tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
607                                 }
608                                 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
609                                     tr = Geom::Scale(style->stroke_width.computed) * tr;
610                                 }
611                                 tr = marker_item->transform * marker->c2p * tr * transform;
612                                 NRRect marker_bbox;
613                                 sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true);
614                                 nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
615                             }
616                             // MID position
617                             if ( path_it->size_default() > 1) {
618                                 Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
619                                 Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
620                                 while (curve_it2 != path_it->end_default())
621                                 {
622                                     /* Put marker between curve_it1 and curve_it2.
623                                      * Loop to end_default (so including closing segment), because when a path is closed,
624                                      * there should be a midpoint marker between last segment and closing straight line segment */
626                                     SPMarker* marker = SP_MARKER (shape->marker[i]);
627                                     SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
629                                     if (marker_item) {
630                                         Geom::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
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                                     }
644                                     ++curve_it1;
645                                     ++curve_it2;
646                                 }
647                             }
648                             // END position
649                             if ( path_it != (pathv.end()-1) && !path_it->empty()) {
650                                 Geom::Curve const &lastcurve = path_it->back_default();
651                                 Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
652                                 if (!marker->orient_auto) {
653                                     Geom::Point transl = tr.translation();
654                                     tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
655                                 }
656                                 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
657                                     tr = Geom::Scale(style->stroke_width.computed) * tr;
658                                 }
659                                 tr = marker_item->transform * marker->c2p * tr * transform;
660                                 NRRect marker_bbox;
661                                 sp_item_invoke_bbox (marker_item, &marker_bbox, tr, true);
662                                 nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
663                             }
664                         }
665                     }
666                     // END marker
667                     for (unsigned i = 0; i < 4; i += 3) { // SP_MARKER_LOC and SP_MARKER_LOC_END
668                         if ( shape->marker[i] ) {
669                             SPMarker* marker = SP_MARKER (shape->marker[i]);
670                             SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
672                             if (marker_item) {
673                                 /* Get reference to last curve in the path.
674                                  * For moveto-only path, this returns the "closing line segment". */
675                                 Geom::Path const &path_last = pathv.back();
676                                 unsigned int index = path_last.size_default();
677                                 if (index > 0) {
678                                     index--;
679                                 }
680                                 Geom::Curve const &lastcurve = path_last[index];
682                                 Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
683                                 if (!marker->orient_auto) {
684                                     Geom::Point transl = tr.translation();
685                                     tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
686                                 }
687                                 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
688                                     tr = Geom::Scale(style->stroke_width.computed) * tr;
689                                 }
691                                 // total marker transform
692                                 tr = marker_item->transform * marker->c2p * tr * transform;
694                                 // get bbox of the marker with that transform
695                                 NRRect marker_bbox;
696                                 sp_item_invoke_bbox (marker_item, &marker_bbox, tr, true);
697                                 // union it with the shape bbox
698                                 nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
699                             }
700                         }
701                     }
702                 }
703             }
705             // copy our bbox to the variable we're given
706             *bbox = cbbox;
707         }
708     }
711 static void
712 sp_shape_print_invoke_marker_printing(SPObject* obj, Geom::Matrix tr, SPStyle* style, SPPrintContext *ctx) {
713     SPMarker *marker = SP_MARKER(obj);
714     if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
715         tr = Geom::Scale(style->stroke_width.computed) * tr;
716     }
718     SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
719     tr = marker_item->transform * marker->c2p * tr;
721     Geom::Matrix old_tr = marker_item->transform;
722     marker_item->transform = tr;
723     sp_item_invoke_print (marker_item, ctx);
724     marker_item->transform = old_tr;
726 /**
727  * Prepares shape for printing.  Handles printing of comments for printing
728  * debugging, sizes the item to fit into the document width/height,
729  * applies print fill/stroke, sets transforms for markers, and adds
730  * comment labels.
731  */
732 void
733 sp_shape_print (SPItem *item, SPPrintContext *ctx)
735     NRRect pbox, dbox, bbox;
737     SPShape *shape = SP_SHAPE(item);
739     if (!shape->curve) return;
741         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
742         gint add_comments = prefs->getBool("/printing/debug/add-label-comments");
743         if (add_comments) {
744             gchar * comment = g_strdup_printf("begin '%s'",
745                                               SP_OBJECT(item)->defaultLabel());
746             sp_print_comment(ctx, comment);
747             g_free(comment);
748         }
750     /* fixme: Think (Lauris) */
751     sp_item_invoke_bbox(item, &pbox, Geom::identity(), TRUE);
752     dbox.x0 = 0.0;
753     dbox.y0 = 0.0;
754     dbox.x1 = sp_document_width (SP_OBJECT_DOCUMENT (item));
755     dbox.y1 = sp_document_height (SP_OBJECT_DOCUMENT (item));
756     sp_item_bbox_desktop (item, &bbox);
757     Geom::Matrix const i2d(sp_item_i2d_affine(item));
759     SPStyle* style = SP_OBJECT_STYLE (item);
761     if (!style->fill.isNone()) {
762         sp_print_fill (ctx, shape->curve->get_pathvector(), &i2d, style, &pbox, &dbox, &bbox);
763     }
765     if (!style->stroke.isNone()) {
766         sp_print_stroke (ctx, shape->curve->get_pathvector(), &i2d, style, &pbox, &dbox, &bbox);
767     }
769     /** \todo make code prettier */
770     Geom::PathVector const & pathv = shape->curve->get_pathvector();
771     // START marker
772     for (int i = 0; i < 2; i++) {  // SP_MARKER_LOC and SP_MARKER_LOC_START    
773         if ( shape->marker[i] ) {
774             Geom::Matrix tr(sp_shape_marker_get_transform_at_start(pathv.begin()->front()));
775             sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
776         }
777     }
778     // MID marker
779     for (int i = 0; i < 3; i += 2) {  // SP_MARKER_LOC and SP_MARKER_LOC_MID
780         if (shape->marker[i]) {
781             for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
782                 // START position
783                 if ( path_it != pathv.begin() 
784                      && ! ((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
785                 {
786                     Geom::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front()));
787                     sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
788                 }
789                 // MID position
790                 if ( path_it->size_default() > 1) {
791                     Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
792                     Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
793                     while (curve_it2 != path_it->end_default())
794                     {
795                         /* Put marker between curve_it1 and curve_it2.
796                          * Loop to end_default (so including closing segment), because when a path is closed,
797                          * there should be a midpoint marker between last segment and closing straight line segment */
798                         Geom::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
800                         sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
802                         ++curve_it1;
803                         ++curve_it2;
804                     }
805                 }
806                 if ( path_it != (pathv.end()-1) && !path_it->empty()) {
807                     Geom::Curve const &lastcurve = path_it->back_default();
808                     Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
809                     sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
810                 }
811             }
812         }
813     }
814     // END marker
815     if ( shape->marker[SP_MARKER_LOC_END] || shape->marker[SP_MARKER_LOC]) {
816         /* Get reference to last curve in the path.
817          * For moveto-only path, this returns the "closing line segment". */
818         Geom::Path const &path_last = pathv.back();
819         unsigned int index = path_last.size_default();
820         if (index > 0) {
821             index--;
822         }
823         Geom::Curve const &lastcurve = path_last[index];
825         Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
827         for (int i = 0; i < 4; i += 3) {  // SP_MARKER_LOC and SP_MARKER_LOC_END
828             if (shape->marker[i]) {
829                 sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
830             }
831         }
832     }
834         if (add_comments) {
835             gchar * comment = g_strdup_printf("end '%s'",
836                                               SP_OBJECT(item)->defaultLabel());
837             sp_print_comment(ctx, comment);
838             g_free(comment);
839         }
842 /**
843  * Sets style, path, and paintbox.  Updates marker views, including dimensions.
844  */
845 static NRArenaItem *
846 sp_shape_show (SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int /*flags*/)
848     SPObject *object = SP_OBJECT(item);
849     SPShape *shape = SP_SHAPE(item);
851     NRArenaItem *arenaitem = NRArenaShape::create(arena);
852     NRArenaShape * const s = NR_ARENA_SHAPE(arenaitem);
853     nr_arena_shape_set_style(s, object->style);
854     nr_arena_shape_set_path(s, shape->curve, false);
855     Geom::OptRect paintbox = item->getBounds(Geom::identity());
856     if (paintbox) {
857         s->setPaintBox(*paintbox);
858     }
860     /* This stanza checks that an object's marker style agrees with
861      * the marker objects it has allocated.  sp_shape_set_marker ensures
862      * that the appropriate marker objects are present (or absent) to
863      * match the style.
864      */
865     for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
866         sp_shape_set_marker (object, i, object->style->marker[i].value);
867       }
869     if (sp_shape_has_markers (shape)) {
871         /* provide key and dimension the marker views */
872         if (!arenaitem->key) {
873             NR_ARENA_ITEM_SET_KEY (arenaitem, sp_item_display_key_new (SP_MARKER_LOC_QTY));
874         }
876         for (int i = 0; i < SP_MARKER_LOC_QTY; i++) {
877             if (shape->marker[i]) {
878                 sp_marker_show_dimension ((SPMarker *) shape->marker[i],
879                                           NR_ARENA_ITEM_GET_KEY (arenaitem) + i,
880                                           sp_shape_number_of_markers (shape, i));
881             }
882         }
884         /* Update marker views */
885         sp_shape_update_marker_view (shape, arenaitem);
886     }
888     return arenaitem;
891 /**
892  * Hides/removes marker views from the shape.
893  */
894 static void
895 sp_shape_hide (SPItem *item, unsigned int key)
897     SPShape *shape;
898     SPItemView *v;
899     int i;
901     shape = (SPShape *) item;
903     for (i=0; i<SP_MARKER_LOC_QTY; i++) {
904       if (shape->marker[i]) {
905         for (v = item->display; v != NULL; v = v->next) {
906                 if (key == v->key) {
907           sp_marker_hide ((SPMarker *) shape->marker[i],
908                                     NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
909                 }
910         }
911       }
912     }
914     if (((SPItemClass *) parent_class)->hide) {
915       ((SPItemClass *) parent_class)->hide (item, key);
916     }
919 /**
920 * \param shape Shape.
921 * \return TRUE if the shape has any markers, or FALSE if not.
922 */
923 int
924 sp_shape_has_markers (SPShape const *shape)
926     /* Note, we're ignoring 'marker' settings, which technically should apply for
927        all three settings.  This should be fixed later such that if 'marker' is
928        specified, then all three should appear. */
930     return (
931         shape->curve &&
932         (shape->marker[SP_MARKER_LOC] ||
933          shape->marker[SP_MARKER_LOC_START] ||
934          shape->marker[SP_MARKER_LOC_MID] ||
935          shape->marker[SP_MARKER_LOC_END])
936         );
940 /**
941 * \param shape Shape.
942 * \param type Marker type (e.g. SP_MARKER_LOC_START)
943 * \return Number of markers that the shape has of this type.
944 */
945 int
946 sp_shape_number_of_markers (SPShape *shape, int type)
948     Geom::PathVector const & pathv = shape->curve->get_pathvector();
949     if (pathv.size() == 0) {
950         return 0;
951     }
953     switch(type) {
954         case SP_MARKER_LOC:
955         {
956             if ( shape->marker[SP_MARKER_LOC] ) {
957                 guint n = 0;
958                 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
959                     n += path_it->size_default() + 1;
960                 }
961                 return n;
962             } else {
963                 return 0;
964             }
965         }
966         case SP_MARKER_LOC_START:
967             // there is only a start marker on the first path of a pathvector
968             return shape->marker[SP_MARKER_LOC_START] ? 1 : 0;
970         case SP_MARKER_LOC_MID:
971         {
972             if ( shape->marker[SP_MARKER_LOC_MID] ) {
973                 guint n = 0;
974                 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
975                     n += path_it->size_default() + 1;
976                 }
977                 return n - 2; // minus the start and end marker.
978             } else {
979                 return 0;
980             }
981         }
983         case SP_MARKER_LOC_END:
984         {
985             // there is only an end marker on the last path of a pathvector
986             return shape->marker[SP_MARKER_LOC_END] ? 1 : 0;
987         }
989         default:
990             return 0;
991     }
994 /**
995  * Checks if the given marker is used in the shape, and if so, it
996  * releases it by calling sp_marker_hide.  Also detaches signals
997  * and unrefs the marker from the shape.
998  */
999 static void
1000 sp_shape_marker_release (SPObject *marker, SPShape *shape)
1002     SPItem *item;
1003     int i;
1005     item = (SPItem *) shape;
1007     for (i = 0; i < SP_MARKER_LOC_QTY; i++) {
1008         if (marker == shape->marker[i]) {
1009             SPItemView *v;
1010             /* Hide marker */
1011             for (v = item->display; v != NULL; v = v->next) {
1012               sp_marker_hide ((SPMarker *) (shape->marker[i]), NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
1013               /* fixme: Do we need explicit remove here? (Lauris) */
1014               /* nr_arena_item_set_mask (v->arenaitem, NULL); */
1015             }
1016             /* Detach marker */
1017             shape->release_connect[i].disconnect();
1018             shape->modified_connect[i].disconnect();
1019             shape->marker[i] = sp_object_hunref (shape->marker[i], item);
1020         }
1021     }
1024 /**
1025  * No-op.  Exists for handling 'modified' messages
1026  */
1027 static void
1028 sp_shape_marker_modified (SPObject */*marker*/, guint /*flags*/, SPItem */*item*/)
1030     /* I think mask does update automagically */
1031     /* g_warning ("Item %s mask %s modified", SP_OBJECT_ID (item), SP_OBJECT_ID (mask)); */
1034 /**
1035  * Adds a new marker to shape object at the location indicated by key.  value
1036  * must be a valid URI reference resolvable from the shape object (i.e., present
1037  * in the document <defs>).  If the shape object already has a marker
1038  * registered at the given position, it is removed first.  Then the
1039  * new marker is hrefed and its signals connected.
1040  */
1041 void
1042 sp_shape_set_marker (SPObject *object, unsigned int key, const gchar *value)
1044     SPItem *item = (SPItem *) object;
1045     SPShape *shape = (SPShape *) object;
1047     if (key > SP_MARKER_LOC_END) {
1048         return;
1049     }
1051     SPObject *mrk = sp_css_uri_reference_resolve (SP_OBJECT_DOCUMENT (object), value);
1052     if (mrk != shape->marker[key]) {
1053         if (shape->marker[key]) {
1054             SPItemView *v;
1056             /* Detach marker */
1057             shape->release_connect[key].disconnect();
1058             shape->modified_connect[key].disconnect();
1060             /* Hide marker */
1061             for (v = item->display; v != NULL; v = v->next) {
1062                 sp_marker_hide ((SPMarker *) (shape->marker[key]),
1063                                 NR_ARENA_ITEM_GET_KEY (v->arenaitem) + key);
1064                 /* fixme: Do we need explicit remove here? (Lauris) */
1065                 /* nr_arena_item_set_mask (v->arenaitem, NULL); */
1066             }
1068             /* Unref marker */
1069             shape->marker[key] = sp_object_hunref (shape->marker[key], object);
1070         }
1071         if (SP_IS_MARKER (mrk)) {
1072             shape->marker[key] = sp_object_href (mrk, object);
1073             shape->release_connect[key] = mrk->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_shape_marker_release), shape));
1074             shape->modified_connect[key] = mrk->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_shape_marker_modified), shape));
1075         }
1076     }
1081 /* Shape section */
1083 /**
1084  * Calls any registered handlers for the set_shape action
1085  */
1086 void
1087 sp_shape_set_shape (SPShape *shape)
1089     g_return_if_fail (shape != NULL);
1090     g_return_if_fail (SP_IS_SHAPE (shape));
1092     if (SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape) {
1093       SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape (shape);
1094     }
1097 /**
1098  * Adds a curve to the shape.  If owner is specified, a reference
1099  * will be made, otherwise the curve will be copied into the shape.
1100  * Any existing curve in the shape will be unreferenced first.
1101  * This routine also triggers a request to update the display.
1102  */
1103 void
1104 sp_shape_set_curve (SPShape *shape, SPCurve *curve, unsigned int owner)
1106     if (shape->curve) {
1107         shape->curve = shape->curve->unref();
1108     }
1109     if (curve) {
1110         if (owner) {
1111             shape->curve = curve->ref();
1112         } else {
1113             shape->curve = curve->copy();
1114         }
1115     }
1116         SP_OBJECT(shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1119 /**
1120  * Return duplicate of curve (if any exists) or NULL if there is no curve
1121  */
1122 SPCurve *
1123 sp_shape_get_curve (SPShape *shape)
1125     if (shape->curve) {
1126         return shape->curve->copy();
1127     }
1128     return NULL;
1131 /**
1132  * Same as sp_shape_set_curve but without updating the display
1133  */
1134 void
1135 sp_shape_set_curve_insync (SPShape *shape, SPCurve *curve, unsigned int owner)
1137     if (shape->curve) {
1138         shape->curve = shape->curve->unref();
1139     }
1140     if (curve) {
1141         if (owner) {
1142             shape->curve = curve->ref();
1143         } else {
1144             shape->curve = curve->copy();
1145         }
1146     }
1149 /**
1150  * Return all nodes in a path that are to be considered for snapping
1151  */
1152 static void sp_shape_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs)
1154     g_assert(item != NULL);
1155     g_assert(SP_IS_SHAPE(item));
1157     SPShape const *shape = SP_SHAPE(item);
1158     if (shape->curve == NULL) {
1159         return;
1160     }
1162     // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes
1163     if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) {
1164         return;
1165     }
1167     Geom::PathVector const &pathv = shape->curve->get_pathvector();
1168     if (pathv.empty())
1169         return;
1171     Geom::Matrix const i2d (sp_item_i2d_affine (item));
1173     int type;
1175         if (snapprefs->getSnapObjectMidpoints()) {
1176                 Geom::OptRect bbox = item->getBounds(sp_item_i2d_affine(item));
1177                 if (bbox) {
1178                         type = target ? int(Inkscape::SNAPTARGET_OBJECT_MIDPOINT) : int(Inkscape::SNAPSOURCE_OBJECT_MIDPOINT);
1179                         p.push_back(std::make_pair(bbox->midpoint(), type));
1180                 }
1181         }
1183     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
1184         if (snapprefs->getSnapToItemNode()) {
1185                 type = target ? int(Inkscape::SNAPTARGET_NODE_CUSP) : int(Inkscape::SNAPSOURCE_NODE_CUSP);
1186                 p.push_back(std::make_pair(path_it->initialPoint() * i2d, type));
1187         }
1189         Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
1190         Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
1191         while (curve_it2 != path_it->end_closed())
1192         {
1193             /* Test whether to add the node between curve_it1 and curve_it2.
1194              * Loop to end_closed (so always including closing segment); the last node to be added
1195              * is the node between the closing segment and the segment before that, regardless
1196              * of the path being closed or not. If the path is closed, the final point was already added by
1197              * adding the initial point. */
1199             Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2);
1201             bool c1 = snapprefs->getSnapToItemNode() && (nodetype == Geom::NODE_CUSP || nodetype == Geom::NODE_NONE);
1202             bool c2 = snapprefs->getSnapSmoothNodes() && (nodetype == Geom::NODE_SMOOTH || nodetype == Geom::NODE_SYMM);
1204             if (c1 || c2) {
1205                 type = target ? int(Inkscape::SNAPTARGET_NODE_CUSP) : int(Inkscape::SNAPSOURCE_NODE_CUSP);
1206                                 p.push_back(std::make_pair(curve_it1->finalPoint() * i2d, type));
1207             }
1209                         // Consider midpoints of line segments for snapping
1210                         if (snapprefs->getSnapLineMidpoints()) { // only do this when we're snapping nodes (enforce strict snapping)
1211                                 if (Geom::LineSegment const* line_segment = dynamic_cast<Geom::LineSegment const*>(&(*curve_it1))) {
1212                                         type = target ? int(Inkscape::SNAPTARGET_LINE_MIDPOINT) : int(Inkscape::SNAPSOURCE_LINE_MIDPOINT);
1213                                         p.push_back(std::make_pair(Geom::middle_point(*line_segment) * i2d, type));
1214                                 }
1215                         }
1217             ++curve_it1;
1218             ++curve_it2;
1219         }
1221         // Find the internal intersections of each path and consider these for snapping
1222         // (using "Method 1" as described in Inkscape::ObjectSnapper::_collectNodes())
1223         if (snapprefs->getSnapIntersectionCS()) {
1224             Geom::Crossings cs;
1225             cs = self_crossings(*path_it);
1226             if (cs.size() > 0) { // There might be multiple intersections...
1227                 for (Geom::Crossings::const_iterator i = cs.begin(); i != cs.end(); i++) {
1228                     Geom::Point p_ix = (*path_it).pointAt((*i).ta);
1229                     type = target ? int(Inkscape::SNAPTARGET_PATH_INTERSECTION) : int(Inkscape::SNAPSOURCE_PATH_INTERSECTION);
1230                     p.push_back(std::make_pair(p_ix * i2d, type));
1231                 }
1232             }
1233         }
1234     }
1238 /*
1239   Local Variables:
1240   mode:c++
1241   c-file-style:"stroustrup"
1242   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1243   indent-tabs-mode:nil
1244   fill-column:99
1245   End:
1246 */
1247 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :