Code

make 'marker' property work (shorthand for start, mid and end markers); inkscape...
[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, SnapPointsIter 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 - SP_MARKER_LOC,
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();
412     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
413       // START position
414         Geom::Matrix const m (sp_shape_marker_get_transform_at_start(path_it->front()));
415         for (int i = 0; i < 2; i++) {  // SP_MARKER_LOC and SP_MARKER_LOC_START
416             if ( shape->marker[i] ) {
417                 sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
418                                          NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
419                                          style->stroke_width.computed);
420                  counter[i]++;
421             }
422         }
424       // MID position
425         if ( (shape->marker[SP_MARKER_LOC_MID] || shape->marker[SP_MARKER_LOC]) && (path_it->size_default() > 1) ) {
426             Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
427             Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
428             while (curve_it2 != path_it->end_default())
429             {
430                 /* Put marker between curve_it1 and curve_it2.
431                  * Loop to end_default (so including closing segment), because when a path is closed,
432                  * there should be a midpoint marker between last segment and closing straight line segment
433                  */
434                 Geom::Matrix const m (sp_shape_marker_get_transform(*curve_it1, *curve_it2));
435                 for (int i = 0; i < 3; i += 2) {  // SP_MARKER_LOC and SP_MARKER_LOC_MID
436                     if (shape->marker[i]) {
437                         sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
438                                                  NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
439                                                  style->stroke_width.computed);
440                         counter[i]++;
441                     }
442                 }
444                 ++curve_it1;
445                 ++curve_it2;
446             }
447         }
449       // END position
450         if ( shape->marker[SP_MARKER_LOC_END] || shape->marker[SP_MARKER_LOC] ) {
451             /* Get reference to last curve in the path.
452              * For moveto-only path, this returns the "closing line segment". */
453             unsigned int index = path_it->size_default();
454             if (index > 0) {
455                 index--;
456             }
457             Geom::Curve const &lastcurve = (*path_it)[index];
458             Geom::Matrix const m = sp_shape_marker_get_transform_at_end(lastcurve);
460             for (int i = 0; i < 4; i += 3) {  // SP_MARKER_LOC and SP_MARKER_LOC_END
461                 if (shape->marker[i]) {
462                     sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
463                                              NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
464                                              style->stroke_width.computed);
465                     counter[i]++;
466                 }
467             }
468         }
469     }
472 /**
473  * Sets modified flag for all sub-item views.
474  */
475 static void
476 sp_shape_modified (SPObject *object, unsigned int flags)
478     SPShape *shape = SP_SHAPE (object);
480     if (((SPObjectClass *) (parent_class))->modified) {
481       (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
482     }
484     if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
485         for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
486             nr_arena_shape_set_style (NR_ARENA_SHAPE (v->arenaitem), object->style);
487         }
488     }
491 /**
492  * Calculates the bounding box for item, storing it into bbox.
493  * This also includes the bounding boxes of any markers included in the shape.
494  */
495 static void sp_shape_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags)
497     SPShape const *shape = SP_SHAPE (item);
498     if (shape->curve) {
499         Geom::OptRect geombbox = bounds_exact_transformed(shape->curve->get_pathvector(), transform);
500         if (geombbox) {
501             NRRect  cbbox;
502             cbbox.x0 = (*geombbox)[0][0];
503             cbbox.y0 = (*geombbox)[1][0];
504             cbbox.x1 = (*geombbox)[0][1];
505             cbbox.y1 = (*geombbox)[1][1];
507             if ((SPItem::BBoxType) flags != SPItem::GEOMETRIC_BBOX) {
509                 SPStyle* style=SP_OBJECT_STYLE (item);
510                 if (!style->stroke.isNone()) {
511                     double const scale = transform.descrim();
512                     if ( fabs(style->stroke_width.computed * scale) > 0.01 ) { // sinon c'est 0=oon veut pas de bord
513                         double const width = MAX(0.125, style->stroke_width.computed * scale);
514                         if ( fabs(cbbox.x1-cbbox.x0) > -0.00001 && fabs(cbbox.y1-cbbox.y0) > -0.00001 ) {
515                             cbbox.x0-=0.5*width;
516                             cbbox.x1+=0.5*width;
517                             cbbox.y0-=0.5*width;
518                             cbbox.y1+=0.5*width;
519                         }
520                     }
521                 }
523                 // Union with bboxes of the markers, if any
524                 if (sp_shape_has_markers (shape)) {
525                     /* TODO: make code prettier: lots of variables can be taken out of the loop! */
526                     Geom::PathVector const & pathv = shape->curve->get_pathvector();
527                     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
528                         if ( shape->marker[SP_MARKER_LOC_START] ) {
529                             SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_START]);
530                             SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
532                             Geom::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front()));
534                             if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
535                                 tr = Geom::Scale(style->stroke_width.computed) * tr;
536                             }
538                             // total marker transform
539                             tr = marker_item->transform * marker->c2p * tr * transform;
541                             // get bbox of the marker with that transform
542                             NRRect marker_bbox;
543                             sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true);
544                             // union it with the shape bbox
545                             nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
546                         }
548                         if ( shape->marker[SP_MARKER_LOC_MID] && (path_it->size_default() > 1) ) {
549                             Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
550                             Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
551                             while (curve_it2 != path_it->end_default())
552                             {
553                                 /* Put marker between curve_it1 and curve_it2.
554                                  * Loop to end_default (so including closing segment), because when a path is closed,
555                                  * there should be a midpoint marker between last segment and closing straight line segment */
557                                 SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_MID]);
558                                 SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
560                                 Geom::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
562                                 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
563                                     tr = Geom::Scale(style->stroke_width.computed) * tr;
564                                 }
566                                 // total marker transform
567                                 tr = marker_item->transform * marker->c2p * tr * transform;
569                                 // get bbox of the marker with that transform
570                                 NRRect marker_bbox;
571                                 sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true);
572                                 // union it with the shape bbox
573                                 nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
575                                 ++curve_it1;
576                                 ++curve_it2;
577                             }
578                         }
580                         if ( shape->marker[SP_MARKER_LOC_END] ) {
581                             SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_END]);
582                             SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
584                             /* Get reference to last curve in the path.
585                              * For moveto-only path, this returns the "closing line segment". */
586                             unsigned int index = path_it->size_default();
587                             if (index > 0) {
588                                 index--;
589                             }
590                             Geom::Curve const &lastcurve = (*path_it)[index];
592                             Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
594                             if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
595                                 tr = Geom::Scale(style->stroke_width.computed) * tr;
596                             }
598                             // total marker transform
599                             tr = marker_item->transform * marker->c2p * tr * transform;
601                             // get bbox of the marker with that transform
602                             NRRect marker_bbox;
603                             sp_item_invoke_bbox (marker_item, &marker_bbox, tr, true);
604                             // union it with the shape bbox
605                             nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
606                         }
607                     }
608                 }
609             }
611             // copy our bbox to the variable we're given
612             *bbox = cbbox;
613         }
614     }
617 static void
618 sp_shape_print_invoke_marker_printing(SPObject* obj, Geom::Matrix tr, SPStyle* style, SPPrintContext *ctx) {
619     SPMarker *marker = SP_MARKER(obj);
620     if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
621         tr = Geom::Scale(style->stroke_width.computed) * tr;
622     }
624     SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
625     tr = marker_item->transform * marker->c2p * tr;
627     Geom::Matrix old_tr = marker_item->transform;
628     marker_item->transform = tr;
629     sp_item_invoke_print (marker_item, ctx);
630     marker_item->transform = old_tr;
632 /**
633  * Prepares shape for printing.  Handles printing of comments for printing
634  * debugging, sizes the item to fit into the document width/height,
635  * applies print fill/stroke, sets transforms for markers, and adds
636  * comment labels.
637  */
638 void
639 sp_shape_print (SPItem *item, SPPrintContext *ctx)
641     NRRect pbox, dbox, bbox;
643     SPShape *shape = SP_SHAPE(item);
645     if (!shape->curve) return;
647         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
648         gint add_comments = prefs->getBool("/printing/debug/add-label-comments");
649         if (add_comments) {
650             gchar * comment = g_strdup_printf("begin '%s'",
651                                               SP_OBJECT(item)->defaultLabel());
652             sp_print_comment(ctx, comment);
653             g_free(comment);
654         }
656     /* fixme: Think (Lauris) */
657     sp_item_invoke_bbox(item, &pbox, Geom::identity(), TRUE);
658     dbox.x0 = 0.0;
659     dbox.y0 = 0.0;
660     dbox.x1 = sp_document_width (SP_OBJECT_DOCUMENT (item));
661     dbox.y1 = sp_document_height (SP_OBJECT_DOCUMENT (item));
662     sp_item_bbox_desktop (item, &bbox);
663     Geom::Matrix const i2d(sp_item_i2d_affine(item));
665     SPStyle* style = SP_OBJECT_STYLE (item);
667     if (!style->fill.isNone()) {
668         sp_print_fill (ctx, shape->curve->get_pathvector(), &i2d, style, &pbox, &dbox, &bbox);
669     }
671     if (!style->stroke.isNone()) {
672         sp_print_stroke (ctx, shape->curve->get_pathvector(), &i2d, style, &pbox, &dbox, &bbox);
673     }
675     /* TODO: make code prettier: lots of variables can be taken out of the loop! */
676     Geom::PathVector const & pathv = shape->curve->get_pathvector();
677     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
678         if ( shape->marker[SP_MARKER_LOC_START] || shape->marker[SP_MARKER_LOC]) {
679             Geom::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front()));
680             if (shape->marker[SP_MARKER_LOC_START]) {
681                 sp_shape_print_invoke_marker_printing(shape->marker[SP_MARKER_LOC_START], tr, style, ctx);
682             }
683             if (shape->marker[SP_MARKER_LOC]) {
684                 sp_shape_print_invoke_marker_printing(shape->marker[SP_MARKER_LOC], tr, style, ctx);
685             }
686         }
688         if ( (shape->marker[SP_MARKER_LOC_MID] || shape->marker[SP_MARKER_LOC]) && (path_it->size_default() > 1) ) {
689             Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
690             Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
691             while (curve_it2 != path_it->end_default())
692             {
693                 /* Put marker between curve_it1 and curve_it2.
694                  * Loop to end_default (so including closing segment), because when a path is closed,
695                  * there should be a midpoint marker between last segment and closing straight line segment */
696                 Geom::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
698                 if (shape->marker[SP_MARKER_LOC_MID]) {
699                     sp_shape_print_invoke_marker_printing(shape->marker[SP_MARKER_LOC_MID], tr, style, ctx);
700                 }
701                 if (shape->marker[SP_MARKER_LOC]) {
702                     sp_shape_print_invoke_marker_printing(shape->marker[SP_MARKER_LOC], tr, style, ctx);
703                 }
705                 ++curve_it1;
706                 ++curve_it2;
707             }
708         }
710         if ( shape->marker[SP_MARKER_LOC_END] || shape->marker[SP_MARKER_LOC]) {
711             /* Get reference to last curve in the path.
712              * For moveto-only path, this returns the "closing line segment". */
713             unsigned int index = path_it->size_default();
714             if (index > 0) {
715                 index--;
716             }
717             Geom::Curve const &lastcurve = (*path_it)[index];
719             Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
721             if (shape->marker[SP_MARKER_LOC_END]) {
722                 sp_shape_print_invoke_marker_printing(shape->marker[SP_MARKER_LOC_END], tr, style, ctx);
723             }
724             if (shape->marker[SP_MARKER_LOC]) {
725                 sp_shape_print_invoke_marker_printing(shape->marker[SP_MARKER_LOC], tr, style, ctx);
726             }
727         }
728     }
730         if (add_comments) {
731             gchar * comment = g_strdup_printf("end '%s'",
732                                               SP_OBJECT(item)->defaultLabel());
733             sp_print_comment(ctx, comment);
734             g_free(comment);
735         }
738 /**
739  * Sets style, path, and paintbox.  Updates marker views, including dimensions.
740  */
741 static NRArenaItem *
742 sp_shape_show (SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int /*flags*/)
744     SPObject *object = SP_OBJECT(item);
745     SPShape *shape = SP_SHAPE(item);
747     NRArenaItem *arenaitem = NRArenaShape::create(arena);
748     NRArenaShape * const s = NR_ARENA_SHAPE(arenaitem);
749     nr_arena_shape_set_style(s, object->style);
750     nr_arena_shape_set_path(s, shape->curve, false);
751     Geom::OptRect paintbox = item->getBounds(Geom::identity());
752     if (paintbox) {
753         s->setPaintBox(*paintbox);
754     }
756     /* This stanza checks that an object's marker style agrees with
757      * the marker objects it has allocated.  sp_shape_set_marker ensures
758      * that the appropriate marker objects are present (or absent) to
759      * match the style.
760      */
761     for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
762         sp_shape_set_marker (object, i, object->style->marker[i].value);
763       }
765     if (sp_shape_has_markers (shape)) {
767         /* provide key and dimension the marker views */
768         if (!arenaitem->key) {
769             NR_ARENA_ITEM_SET_KEY (arenaitem, sp_item_display_key_new (SP_MARKER_LOC_QTY));
770         }
772         for (int i = 0; i < SP_MARKER_LOC_QTY; i++) {
773             if (shape->marker[i]) {
774                 sp_marker_show_dimension ((SPMarker *) shape->marker[i],
775                                           NR_ARENA_ITEM_GET_KEY (arenaitem) + i - SP_MARKER_LOC,
776                                           sp_shape_number_of_markers (shape, i));
777             }
778         }
780         /* Update marker views */
781         sp_shape_update_marker_view (shape, arenaitem);
782     }
784     return arenaitem;
787 /**
788  * Hides/removes marker views from the shape.
789  */
790 static void
791 sp_shape_hide (SPItem *item, unsigned int key)
793     SPShape *shape;
794     SPItemView *v;
795     int i;
797     shape = (SPShape *) item;
799     for (i=0; i<SP_MARKER_LOC_QTY; i++) {
800       if (shape->marker[i]) {
801         for (v = item->display; v != NULL; v = v->next) {
802                 if (key == v->key) {
803           sp_marker_hide ((SPMarker *) shape->marker[i],
804                                     NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
805                 }
806         }
807       }
808     }
810     if (((SPItemClass *) parent_class)->hide) {
811       ((SPItemClass *) parent_class)->hide (item, key);
812     }
815 /**
816 * \param shape Shape.
817 * \return TRUE if the shape has any markers, or FALSE if not.
818 */
819 int
820 sp_shape_has_markers (SPShape const *shape)
822     /* Note, we're ignoring 'marker' settings, which technically should apply for
823        all three settings.  This should be fixed later such that if 'marker' is
824        specified, then all three should appear. */
826     return (
827         shape->curve &&
828         (shape->marker[SP_MARKER_LOC] ||
829          shape->marker[SP_MARKER_LOC_START] ||
830          shape->marker[SP_MARKER_LOC_MID] ||
831          shape->marker[SP_MARKER_LOC_END])
832         );
836 /**
837 * \param shape Shape.
838 * \param type Marker type (e.g. SP_MARKER_LOC_START)
839 * \return Number of markers that the shape has of this type.
840 */
841 int
842 sp_shape_number_of_markers (SPShape *shape, int type)
844     Geom::PathVector const & pathv = shape->curve->get_pathvector();
846     switch(type) {
847         case SP_MARKER_LOC:
848         {
849             if ( shape->marker[SP_MARKER_LOC] ) {
850                 guint n = 2*pathv.size();
851                 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
852                     n += path_it->size();
853                     n += path_it->closed() ? 1 : 0;
854                 }
855                 return n;
856             } else {
857                 return 0;
858             }
859         }
860         case SP_MARKER_LOC_START:
861             return shape->marker[SP_MARKER_LOC_START] ? pathv.size() : 0;
863         case SP_MARKER_LOC_MID:
864         {
865             if ( shape->marker[SP_MARKER_LOC_MID] ) {
866             guint n = 0;
867                 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
868                     n += path_it->size();
869                     n += path_it->closed() ? 1 : 0;
870                 }
871                 return n;
872             } else {
873                 return 0;
874             }
875         }
877         case SP_MARKER_LOC_END:
878         {
879             return shape->marker[SP_MARKER_LOC_END] ? pathv.size() : 0;
880         }
882         default:
883             return 0;
884     }
887 /**
888  * Checks if the given marker is used in the shape, and if so, it
889  * releases it by calling sp_marker_hide.  Also detaches signals
890  * and unrefs the marker from the shape.
891  */
892 static void
893 sp_shape_marker_release (SPObject *marker, SPShape *shape)
895     SPItem *item;
896     int i;
898     item = (SPItem *) shape;
900     for (i = 0; i < SP_MARKER_LOC_QTY; i++) {
901         if (marker == shape->marker[i]) {
902             SPItemView *v;
903             /* Hide marker */
904             for (v = item->display; v != NULL; v = v->next) {
905               sp_marker_hide ((SPMarker *) (shape->marker[i]), NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
906               /* fixme: Do we need explicit remove here? (Lauris) */
907               /* nr_arena_item_set_mask (v->arenaitem, NULL); */
908             }
909             /* Detach marker */
910             shape->release_connect[i].disconnect();
911             shape->modified_connect[i].disconnect();
912             shape->marker[i] = sp_object_hunref (shape->marker[i], item);
913         }
914     }
917 /**
918  * No-op.  Exists for handling 'modified' messages
919  */
920 static void
921 sp_shape_marker_modified (SPObject */*marker*/, guint /*flags*/, SPItem */*item*/)
923     /* I think mask does update automagically */
924     /* g_warning ("Item %s mask %s modified", SP_OBJECT_ID (item), SP_OBJECT_ID (mask)); */
927 /**
928  * Adds a new marker to shape object at the location indicated by key.  value
929  * must be a valid URI reference resolvable from the shape object (i.e., present
930  * in the document <defs>).  If the shape object already has a marker
931  * registered at the given position, it is removed first.  Then the
932  * new marker is hrefed and its signals connected.
933  */
934 void
935 sp_shape_set_marker (SPObject *object, unsigned int key, const gchar *value)
937     SPItem *item = (SPItem *) object;
938     SPShape *shape = (SPShape *) object;
940     if (key < 0 || key > SP_MARKER_LOC_END) {
941         return;
942     }
944     SPObject *mrk = sp_css_uri_reference_resolve (SP_OBJECT_DOCUMENT (object), value);
945     if (mrk != shape->marker[key]) {
946         if (shape->marker[key]) {
947             SPItemView *v;
949             /* Detach marker */
950             shape->release_connect[key].disconnect();
951             shape->modified_connect[key].disconnect();
953             /* Hide marker */
954             for (v = item->display; v != NULL; v = v->next) {
955                 sp_marker_hide ((SPMarker *) (shape->marker[key]),
956                                 NR_ARENA_ITEM_GET_KEY (v->arenaitem) + key);
957                 /* fixme: Do we need explicit remove here? (Lauris) */
958                 /* nr_arena_item_set_mask (v->arenaitem, NULL); */
959             }
961             /* Unref marker */
962             shape->marker[key] = sp_object_hunref (shape->marker[key], object);
963         }
964         if (SP_IS_MARKER (mrk)) {
965             shape->marker[key] = sp_object_href (mrk, object);
966             shape->release_connect[key] = mrk->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_shape_marker_release), shape));
967             shape->modified_connect[key] = mrk->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_shape_marker_modified), shape));
968         }
969     }
974 /* Shape section */
976 /**
977  * Calls any registered handlers for the set_shape action
978  */
979 void
980 sp_shape_set_shape (SPShape *shape)
982     g_return_if_fail (shape != NULL);
983     g_return_if_fail (SP_IS_SHAPE (shape));
985     if (SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape) {
986       SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape (shape);
987     }
990 /**
991  * Adds a curve to the shape.  If owner is specified, a reference
992  * will be made, otherwise the curve will be copied into the shape.
993  * Any existing curve in the shape will be unreferenced first.
994  * This routine also triggers a request to update the display.
995  */
996 void
997 sp_shape_set_curve (SPShape *shape, SPCurve *curve, unsigned int owner)
999     if (shape->curve) {
1000         shape->curve = shape->curve->unref();
1001     }
1002     if (curve) {
1003         if (owner) {
1004             shape->curve = curve->ref();
1005         } else {
1006             shape->curve = curve->copy();
1007         }
1008     }
1009         SP_OBJECT(shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1012 /**
1013  * Return duplicate of curve (if any exists) or NULL if there is no curve
1014  */
1015 SPCurve *
1016 sp_shape_get_curve (SPShape *shape)
1018     if (shape->curve) {
1019         return shape->curve->copy();
1020     }
1021     return NULL;
1024 /**
1025  * Same as sp_shape_set_curve but without updating the display
1026  */
1027 void
1028 sp_shape_set_curve_insync (SPShape *shape, SPCurve *curve, unsigned int owner)
1030     if (shape->curve) {
1031         shape->curve = shape->curve->unref();
1032     }
1033     if (curve) {
1034         if (owner) {
1035             shape->curve = curve->ref();
1036         } else {
1037             shape->curve = curve->copy();
1038         }
1039     }
1042 /**
1043  * Return all nodes in a path that are to be considered for snapping
1044  */
1045 static void sp_shape_snappoints(SPItem const *item, SnapPointsIter p, Inkscape::SnapPreferences const *snapprefs)
1047     g_assert(item != NULL);
1048     g_assert(SP_IS_SHAPE(item));
1050     SPShape const *shape = SP_SHAPE(item);
1051     if (shape->curve == NULL) {
1052         return;
1053     }
1055     Geom::PathVector const &pathv = shape->curve->get_pathvector();
1056     if (pathv.empty())
1057         return;
1059     Geom::Matrix const i2d (sp_item_i2d_affine (item));
1061     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
1062         *p = from_2geom(path_it->initialPoint() * i2d);
1064         Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
1065         Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
1066         while (curve_it2 != path_it->end_closed())
1067         {
1068             /* Test whether to add the node between curve_it1 and curve_it2.
1069              * Loop to end_closed (so always including closing segment); the last node to be added
1070              * is the node between the closing segment and the segment before that, regardless
1071              * of the path being closed or not. If the path is closed, the final point was already added by
1072              * adding the initial point. */
1074             Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2);
1076             // Depending on the snapping preferences, either add only cusp nodes, or add add both cusp and smooth nodes
1077             if (snapprefs->getSnapSmoothNodes() || nodetype == Geom::NODE_NONE || nodetype == Geom::NODE_CUSP) {
1078                 *p = from_2geom(curve_it1->finalPoint() * i2d);
1079             }
1081             // Consider midpoints of line segments for snapping
1082             if (snapprefs->getSnapMidpoints()) {
1083                 if (Geom::LineSegment const* line_segment = dynamic_cast<Geom::LineSegment const*>(&(*curve_it1))) {
1084                     *p = from_2geom(Geom::middle_point(*line_segment) * i2d);
1085                 }
1086             }
1088             ++curve_it1;
1089             ++curve_it2;
1090         }
1092         // Find the internal intersections of each path and consider these for snapping (using "Method 1" as desciribed in Inkscape::ObjectSnapper::_collectNodes())
1093         if (snapprefs->getSnapIntersectionCS()) {
1094             Geom::Crossings cs;
1095             cs = self_crossings(*path_it);
1096             if (cs.size() > 0) { // There might be multiple intersections...
1097                 for (Geom::Crossings::const_iterator i = cs.begin(); i != cs.end(); i++) {
1098                     Geom::Point p_ix = (*path_it).pointAt((*i).ta);
1099                     *p = from_2geom(p_ix * i2d);
1100                 }
1101             }
1102         }
1103     }
1109 /*
1110   Local Variables:
1111   mode:c++
1112   c-file-style:"stroustrup"
1113   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1114   indent-tabs-mode:nil
1115   fill-column:99
1116   End:
1117 */
1118 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :