Code

fix per SVG spec: bbox for paint servers must be exclusive of a stroke width etc...
[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 "helper/geom.h"
27 #include "helper/geom-nodetype.h"
29 #include <sigc++/functors/ptr_fun.h>
30 #include <sigc++/adaptors/bind.h>
32 #include "macros.h"
33 #include "display/nr-arena-shape.h"
34 #include "display/curve.h"
35 #include "print.h"
36 #include "document.h"
37 #include "style.h"
38 #include "marker.h"
39 #include "sp-path.h"
40 #include "prefs-utils.h"
41 #include "attributes.h"
43 #include "live_effects/lpeobject.h"
44 #include "uri.h"
45 #include "extract-uri.h"
46 #include "uri-references.h"
47 #include "bad-uri-exception.h"
48 #include "xml/repr.h"
50 #include "util/mathfns.h" // for triangle_area()
52 #define noSHAPE_VERBOSE
54 static void sp_shape_class_init (SPShapeClass *klass);
55 static void sp_shape_init (SPShape *shape);
56 static void sp_shape_finalize (GObject *object);
58 static void sp_shape_build (SPObject * object, SPDocument * document, Inkscape::XML::Node * repr);
59 static void sp_shape_release (SPObject *object);
61 static void sp_shape_set(SPObject *object, unsigned key, gchar const *value);
62 static void sp_shape_update (SPObject *object, SPCtx *ctx, unsigned int flags);
63 static void sp_shape_modified (SPObject *object, unsigned int flags);
64 static Inkscape::XML::Node *sp_shape_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
66 static void sp_shape_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags);
67 void sp_shape_print (SPItem * item, SPPrintContext * ctx);
68 static NRArenaItem *sp_shape_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags);
69 static void sp_shape_hide (SPItem *item, unsigned int key);
70 static void sp_shape_snappoints (SPItem const *item, SnapPointsIter p);
72 static void sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai);
74 static SPLPEItemClass *parent_class;
76 /**
77  * Registers the SPShape class with Gdk and returns its type number.
78  */
79 GType
80 sp_shape_get_type (void)
81 {
82         static GType type = 0;
83         if (!type) {
84                 GTypeInfo info = {
85                         sizeof (SPShapeClass),
86                         NULL, NULL,
87                         (GClassInitFunc) sp_shape_class_init,
88                         NULL, NULL,
89                         sizeof (SPShape),
90                         16,
91                         (GInstanceInitFunc) sp_shape_init,
92                         NULL,   /* value_table */
93                 };
94                 type = g_type_register_static (SP_TYPE_LPE_ITEM, "SPShape", &info, (GTypeFlags)0);
95         }
96         return type;
97 }
99 /**
100  * Initializes a SPShapeClass object.  Establishes the function pointers to the class'
101  * member routines in the class vtable, and sets pointers to parent classes.
102  */
103 static void
104 sp_shape_class_init (SPShapeClass *klass)
106     GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
107     SPObjectClass *sp_object_class = SP_OBJECT_CLASS(klass);
108     SPItemClass * item_class = SP_ITEM_CLASS(klass);
109     SPLPEItemClass * lpe_item_class = SP_LPE_ITEM_CLASS(klass);
111     parent_class = (SPLPEItemClass *)g_type_class_peek_parent (klass);
113     gobject_class->finalize = sp_shape_finalize;
115         sp_object_class->build = sp_shape_build;
116         sp_object_class->release = sp_shape_release;
117     sp_object_class->set = sp_shape_set;
118         sp_object_class->update = sp_shape_update;
119         sp_object_class->modified = sp_shape_modified;
120     sp_object_class->write = sp_shape_write;
122         item_class->bbox = sp_shape_bbox;
123         item_class->print = sp_shape_print;
124         item_class->show = sp_shape_show;
125         item_class->hide = sp_shape_hide;
126     item_class->snappoints = sp_shape_snappoints;
127     lpe_item_class->update_patheffect = NULL;
129     klass->set_shape = NULL;
132 /**
133  * Initializes an SPShape object.
134  */
135 static void
136 sp_shape_init (SPShape *shape)
138     for ( int i = 0 ; i < SP_MARKER_LOC_QTY ; i++ ) {
139         new (&shape->release_connect[i]) sigc::connection();
140         new (&shape->modified_connect[i]) sigc::connection();
141     }
144 static void
145 sp_shape_finalize (GObject *object)
147     SPShape *shape=(SPShape *)object;
149     for ( int i = 0 ; i < SP_MARKER_LOC_QTY ; i++ ) {
150         shape->release_connect[i].disconnect();
151         shape->release_connect[i].~connection();
152         shape->modified_connect[i].disconnect();
153         shape->modified_connect[i].~connection();
154     }
156     if (((GObjectClass *) (parent_class))->finalize) {
157         (* ((GObjectClass *) (parent_class))->finalize)(object);
158     }
161 /**
162  * Virtual build callback for SPMarker.
163  *
164  * This is to be invoked immediately after creation of an SPShape.
165  *
166  * \see sp_object_build()
167  */
168 static void
169 sp_shape_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
171     if (((SPObjectClass *) (parent_class))->build) {
172        (*((SPObjectClass *) (parent_class))->build) (object, document, repr);
173     }
176 /**
177  * Removes, releases and unrefs all children of object
178  *
179  * This is the inverse of sp_shape_build().  It must be invoked as soon
180  * as the shape is removed from the tree, even if it is still referenced
181  * by other objects.  This routine also disconnects/unrefs markers and
182  * curves attached to it.
183  *
184  * \see sp_object_release()
185  */
186 static void
187 sp_shape_release (SPObject *object)
189         SPItem *item;
190         SPShape *shape;
191         SPItemView *v;
192         int i;
194         item = (SPItem *) object;
195         shape = (SPShape *) object;
197         for (i=SP_MARKER_LOC_START; i<SP_MARKER_LOC_QTY; i++) {
198           if (shape->marker[i]) {
199             sp_signal_disconnect_by_data (shape->marker[i], object);
200             for (v = item->display; v != NULL; v = v->next) {
201               sp_marker_hide ((SPMarker *) shape->marker[i], NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
202             }
203             shape->marker[i] = sp_object_hunref (shape->marker[i], object);
204           }
205         }
206         if (shape->curve) {
207                 shape->curve = shape->curve->unref();
208         }
209     
210         if (((SPObjectClass *) parent_class)->release) {
211           ((SPObjectClass *) parent_class)->release (object);
212         }
217 static void
218 sp_shape_set(SPObject *object, unsigned int key, gchar const *value)
220     if (((SPObjectClass *) parent_class)->set) {
221         ((SPObjectClass *) parent_class)->set(object, key, value);
222     }
225 static Inkscape::XML::Node *
226 sp_shape_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags)
228     if (((SPObjectClass *)(parent_class))->write) {
229         ((SPObjectClass *)(parent_class))->write(object, doc, repr, flags);
230     }
232     return repr;
235 /** 
236  * Updates the shape when its attributes have changed.  Also establishes
237  * marker objects to match the style settings.  
238  */
239 static void
240 sp_shape_update (SPObject *object, SPCtx *ctx, unsigned int flags)
242     SPItem *item = (SPItem *) object;
243     SPShape *shape = (SPShape *) object;
245     if (((SPObjectClass *) (parent_class))->update) {
246         (* ((SPObjectClass *) (parent_class))->update) (object, ctx, flags);
247     }
249     /* This stanza checks that an object's marker style agrees with
250      * the marker objects it has allocated.  sp_shape_set_marker ensures
251      * that the appropriate marker objects are present (or absent) to
252      * match the style.
253      */
254     /* TODO:  It would be nice if this could be done at an earlier level */
255     for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
256         sp_shape_set_marker (object, i, object->style->marker[i].value);
257           }
259     if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
260         SPStyle *style;
261         style = SP_OBJECT_STYLE (object);
262         if (style->stroke_width.unit == SP_CSS_UNIT_PERCENT) {
263             SPItemCtx *ictx = (SPItemCtx *) ctx;
264             double const aw = 1.0 / NR::expansion(ictx->i2vp);
265             style->stroke_width.computed = style->stroke_width.value * aw;
266             for (SPItemView *v = ((SPItem *) (shape))->display; v != NULL; v = v->next) {
267                 nr_arena_shape_set_style ((NRArenaShape *) v->arenaitem, style);
268             }
269         }
270     }
272     if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) {
273         /* This is suboptimal, because changing parent style schedules recalculation */
274         /* But on the other hand - how can we know that parent does not tie style and transform */
275         boost::optional<NR::Rect> paintbox = SP_ITEM(object)->getBounds(NR::identity(), SPItem::GEOMETRIC_BBOX);
276         for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
277             NRArenaShape * const s = NR_ARENA_SHAPE(v->arenaitem);
278             if (flags & SP_OBJECT_MODIFIED_FLAG) {
279                 nr_arena_shape_set_path(s, shape->curve, (flags & SP_OBJECT_USER_MODIFIED_FLAG_B));
280             }
281             if (paintbox) {
282                 s->setPaintBox(*paintbox);
283             }
284         }
285     }
287     if (sp_shape_has_markers (shape)) {
288         /* Dimension marker views */
289         for (SPItemView *v = item->display; v != NULL; v = v->next) {
291             if (!v->arenaitem->key) {
292                 /* Get enough keys for all, start, mid and end marker types,
293                 ** and set this view's arenaitem key to the first of these keys.
294                 */
295                 NR_ARENA_ITEM_SET_KEY (
296                     v->arenaitem,
297                     sp_item_display_key_new (SP_MARKER_LOC_QTY)
298                     );
299             }
301             for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
302                 if (shape->marker[i]) {
303                     sp_marker_show_dimension ((SPMarker *) shape->marker[i],
304                                               NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i - SP_MARKER_LOC,
305                                               sp_shape_number_of_markers (shape, i));
306                 }
307             }
308         }
310         /* Update marker views */
311         for (SPItemView *v = item->display; v != NULL; v = v->next) {
312             sp_shape_update_marker_view (shape, v->arenaitem);
313         }
314     }
317 /**
318  * Calculate the transform required to get a marker's path object in the
319  * right place for particular path segment on a shape.
320  *
321  * \see sp_shape_marker_update_marker_view.
322  *
323  * From SVG spec:
324  * The axes of the temporary new user coordinate system are aligned according to the orient attribute on the 'marker'
325  * element and the slope of the curve at the given vertex. (Note: if there is a discontinuity at a vertex, the slope
326  * is the average of the slopes of the two segments of the curve that join at the given vertex. If a slope cannot be
327  * determined, the slope is assumed to be zero.)
328  *
329  * Reference: http://www.w3.org/TR/SVG11/painting.html#MarkerElement, the `orient' attribute.
330  * Reference for behaviour of zero-length segments:
331  * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
332  */
333 Geom::Matrix
334 sp_shape_marker_get_transform(Geom::Curve const & c1, Geom::Curve const & c2)
336     Geom::Point p = c1.pointAt(1);
337     Geom::Curve * c1_reverse = c1.reverse();
338     Geom::Point tang1 = - c1_reverse->unitTangentAt(0);
339     delete c1_reverse;
340     Geom::Point tang2 = c2.unitTangentAt(0);
342     double const angle1 = Geom::atan2(tang1);
343     double const angle2 = Geom::atan2(tang2);
345     double ret_angle;
346     ret_angle = .5 * (angle1 + angle2);
348     if ( fabs( angle2 - angle1 ) > M_PI ) {
349         /* ret_angle is in the middle of the larger of the two sectors between angle1 and
350          * angle2, so flip it by 180degrees to force it to the middle of the smaller sector.
351          *
352          * (Imagine a circle with rays drawn at angle1 and angle2 from the centre of the
353          * circle.  Those two rays divide the circle into two sectors.)
354          */
355         ret_angle += M_PI;
356     }
358     return Geom::Rotate(ret_angle) * Geom::Translate(p);
360 Geom::Matrix
361 sp_shape_marker_get_transform_at_start(Geom::Curve const & c)
363     Geom::Point p = c.pointAt(0);
364     Geom::Matrix ret = Geom::Translate(p);
366     if ( !c.isDegenerate() ) {
367         Geom::Point tang = c.unitTangentAt(0);
368         double const angle = Geom::atan2(tang);
369         ret = Geom::Rotate(angle) * Geom::Translate(p);
370     } else {
371         /* FIXME: the svg spec says to search for a better alternative than zero angle directionality:
372          * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes */
373     }
375     return ret;
377 Geom::Matrix
378 sp_shape_marker_get_transform_at_end(Geom::Curve const & c)
380     Geom::Point p = c.pointAt(1);
381     Geom::Matrix ret = Geom::Translate(p);
383     if ( !c.isDegenerate() ) {
384         Geom::Curve * c_reverse = c.reverse();
385         Geom::Point tang = - c_reverse->unitTangentAt(0);
386         delete c_reverse;
387         double const angle = Geom::atan2(tang);
388         ret = Geom::Rotate(angle) * Geom::Translate(p);
389     } else {
390         /* FIXME: the svg spec says to search for a better alternative than zero angle directionality:
391          * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes */
392     }
394     return ret;
397 /**
398  * Updates the instances (views) of a given marker in a shape.
399  * Marker views have to be scaled already.  The transformation
400  * is retrieved and then shown by calling sp_marker_show_instance.
401  *
402  * TODO: correctly handle the 'marker' attribute.
403  * "Using the marker property from a style sheet is equivalent to using all three (start, mid, end)."
404  * See painting-marker-03-f.svg in SVG 1.1 Full test suite.
405  */
406 static void
407 sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai)
409     SPStyle *style = ((SPObject *) shape)->style;
411     // position arguments to sp_marker_show_instance, basically counts the amount of markers.
412     int start_pos = 0;
413     int mid_pos = 0;
414     int end_pos = 0;
416     Geom::PathVector const & pathv = shape->curve->get_pathvector();
417     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
418         if ( shape->marker[SP_MARKER_LOC_START] ) {
419             Geom::Matrix const m (sp_shape_marker_get_transform_at_start(path_it->front()));
420             sp_marker_show_instance ((SPMarker* ) shape->marker[SP_MARKER_LOC_START], ai,
421                                      NR_ARENA_ITEM_GET_KEY(ai) + SP_MARKER_LOC_START, start_pos, m,
422                                      style->stroke_width.computed);
423              start_pos++;
424         }
426         if ( shape->marker[SP_MARKER_LOC_MID] && (path_it->size_default() > 1) ) {
427             Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
428             Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
429             while (curve_it2 != path_it->end_default())
430             {
431                 /* Put marker between curve_it1 and curve_it2.
432                  * Loop to end_default (so including closing segment), because when a path is closed,
433                  * there should be a midpoint marker between last segment and closing straight line segment
434                  */
435                 Geom::Matrix const m (sp_shape_marker_get_transform(*curve_it1, *curve_it2));
436                 sp_marker_show_instance ((SPMarker* ) shape->marker[SP_MARKER_LOC_MID], ai,
437                                          NR_ARENA_ITEM_GET_KEY(ai) + SP_MARKER_LOC_MID, mid_pos, m,
438                                          style->stroke_width.computed);
439                 mid_pos++;
441                 ++curve_it1;
442                 ++curve_it2;
443             }
444         }
446         if ( shape->marker[SP_MARKER_LOC_END] ) {
447             /* Get reference to last curve in the path.
448              * For moveto-only path, this returns the "closing line segment". */
449             unsigned int index = path_it->size_default();
450             if (index > 0) {
451                 index--;
452             }
453             Geom::Curve const &lastcurve = (*path_it)[index];
455             Geom::Matrix const m = sp_shape_marker_get_transform_at_end(lastcurve);
456             sp_marker_show_instance ((SPMarker* ) shape->marker[SP_MARKER_LOC_END], ai,
457                                      NR_ARENA_ITEM_GET_KEY(ai) + SP_MARKER_LOC_END, end_pos, m,
458                                      style->stroke_width.computed);
459             end_pos++;
460         }
461     }
464 /**
465  * Sets modified flag for all sub-item views.
466  */
467 static void
468 sp_shape_modified (SPObject *object, unsigned int flags)
470         SPShape *shape = SP_SHAPE (object);
472         if (((SPObjectClass *) (parent_class))->modified) {
473           (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
474         }
476         if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
477                 for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
478                         nr_arena_shape_set_style (NR_ARENA_SHAPE (v->arenaitem), object->style);
479                 }
480         }
483 /**
484  * Calculates the bounding box for item, storing it into bbox.
485  * This also includes the bounding boxes of any markers included in the shape.
486  */
487 static void sp_shape_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags)
489     SPShape const *shape = SP_SHAPE (item);
491     if (shape->curve) {
493         NRRect  cbbox;
495         Geom::Rect geombbox = bounds_exact_transformed(shape->curve->get_pathvector(), transform);
496         cbbox.x0 = geombbox[0][0];
497         cbbox.y0 = geombbox[1][0];
498         cbbox.x1 = geombbox[0][1];
499         cbbox.y1 = geombbox[1][1];
501         if ((SPItem::BBoxType) flags != SPItem::GEOMETRIC_BBOX) {
502             
503             SPStyle* style=SP_OBJECT_STYLE (item);
504             if (!style->stroke.isNone()) {
505                 double const scale = expansion(transform);
506                 if ( fabs(style->stroke_width.computed * scale) > 0.01 ) { // sinon c'est 0=oon veut pas de bord
507                     double const width = MAX(0.125, style->stroke_width.computed * scale);
508                     if ( fabs(cbbox.x1-cbbox.x0) > -0.00001 && fabs(cbbox.y1-cbbox.y0) > -0.00001 ) {
509                         cbbox.x0-=0.5*width;
510                         cbbox.x1+=0.5*width;
511                         cbbox.y0-=0.5*width;
512                         cbbox.y1+=0.5*width;
513                     }
514                 }
515             }
517             // Union with bboxes of the markers, if any
518             if (sp_shape_has_markers (shape)) {
519                 /* TODO: make code prettier: lots of variables can be taken out of the loop! */
520                 Geom::PathVector const & pathv = shape->curve->get_pathvector();
521                 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
522                     if ( shape->marker[SP_MARKER_LOC_START] ) {
523                         SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_START]);
524                         SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_START]));
526                         NR::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front()));
528                         if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
529                             tr = NR::scale(style->stroke_width.computed) * tr;
530                         }
532                         // total marker transform
533                         tr = marker_item->transform * marker->c2p * tr * transform;
535                         // get bbox of the marker with that transform
536                         NRRect marker_bbox;
537                         sp_item_invoke_bbox (marker_item, &marker_bbox, tr, true);
538                         // union it with the shape bbox
539                         nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
540                     }
542                     if ( shape->marker[SP_MARKER_LOC_MID] && (path_it->size_default() > 1) ) {
543                         Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
544                         Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
545                         while (curve_it2 != path_it->end_default())
546                         {
547                             /* Put marker between curve_it1 and curve_it2.
548                              * Loop to end_default (so including closing segment), because when a path is closed,
549                              * there should be a midpoint marker between last segment and closing straight line segment */
551                             SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_MID]);
552                             SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_MID]));
554                             NR::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
556                             if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
557                                 tr = NR::scale(style->stroke_width.computed) * tr;
558                             }
560                             // total marker transform
561                             tr = marker_item->transform * marker->c2p * tr * transform;
563                             // get bbox of the marker with that transform
564                             NRRect marker_bbox;
565                             sp_item_invoke_bbox (marker_item, &marker_bbox, tr, true);
566                             // union it with the shape bbox
567                             nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
569                             ++curve_it1;
570                             ++curve_it2;
571                         }
572                     }
574                     if ( shape->marker[SP_MARKER_LOC_END] ) {
575                         SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_END]);
576                         SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_END]));
578                         /* Get reference to last curve in the path.
579                          * For moveto-only path, this returns the "closing line segment". */
580                         unsigned int index = path_it->size_default();
581                         if (index > 0) {
582                             index--;
583                         }
584                         Geom::Curve const &lastcurve = (*path_it)[index];
586                         NR::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
588                         if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
589                             tr = NR::scale(style->stroke_width.computed) * tr;
590                         }
592                         // total marker transform
593                         tr = marker_item->transform * marker->c2p * tr * transform;
595                         // get bbox of the marker with that transform
596                         NRRect marker_bbox;
597                         sp_item_invoke_bbox (marker_item, &marker_bbox, tr, true);
598                         // union it with the shape bbox
599                         nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
600                     }
601                 }
602             }
603         }
605         // copy our bbox to the variable we're given
606         *bbox = cbbox;
607     }
610 /**
611  * Prepares shape for printing.  Handles printing of comments for printing
612  * debugging, sizes the item to fit into the document width/height,
613  * applies print fill/stroke, sets transforms for markers, and adds
614  * comment labels.
615  */
616 void
617 sp_shape_print (SPItem *item, SPPrintContext *ctx)
619         NRRect pbox, dbox, bbox;
621         SPShape *shape = SP_SHAPE(item);
623         if (!shape->curve) return;
625         gint add_comments = prefs_get_int_attribute_limited ("printing.debug", "add-label-comments", 0, 0, 1);
626         if (add_comments) {
627             gchar * comment = g_strdup_printf("begin '%s'",
628                                               SP_OBJECT(item)->defaultLabel());
629             sp_print_comment(ctx, comment);
630             g_free(comment);
631         }
633     /* fixme: Think (Lauris) */
634     sp_item_invoke_bbox(item, &pbox, NR::identity(), TRUE);
635     dbox.x0 = 0.0;
636     dbox.y0 = 0.0;
637     dbox.x1 = sp_document_width (SP_OBJECT_DOCUMENT (item));
638     dbox.y1 = sp_document_height (SP_OBJECT_DOCUMENT (item));
639     sp_item_bbox_desktop (item, &bbox);
640     Geom::Matrix const i2d(sp_item_i2d_affine(item));
642         SPStyle* style = SP_OBJECT_STYLE (item);
644     if (!style->fill.isNone()) {
645         sp_print_fill (ctx, shape->curve->get_pathvector(), &i2d, style, &pbox, &dbox, &bbox);
646     }
648     if (!style->stroke.isNone()) {
649         sp_print_stroke (ctx, shape->curve->get_pathvector(), &i2d, style, &pbox, &dbox, &bbox);
650     }
652     /* TODO: make code prettier: lots of variables can be taken out of the loop! */
653     Geom::PathVector const & pathv = shape->curve->get_pathvector();
654     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
655         if ( shape->marker[SP_MARKER_LOC_START] ) {
656             SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_START]);
657             SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_START]));
659             NR::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front()));
661             if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
662                 tr = NR::scale(style->stroke_width.computed) * tr;
663             }
665             tr = marker_item->transform * marker->c2p * tr;
667             NR::Matrix old_tr = marker_item->transform;
668             marker_item->transform = tr;
669             sp_item_invoke_print (marker_item, ctx);
670             marker_item->transform = old_tr;
671         }
673         if ( shape->marker[SP_MARKER_LOC_MID] && (path_it->size_default() > 1) ) {
674             Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
675             Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
676             while (curve_it2 != path_it->end_default())
677             {
678                 /* Put marker between curve_it1 and curve_it2.
679                  * Loop to end_default (so including closing segment), because when a path is closed,
680                  * there should be a midpoint marker between last segment and closing straight line segment */
682                 SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_MID]);
683                 SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_MID]));
685                 NR::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
687                 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
688                     tr = NR::scale(style->stroke_width.computed) * tr;
689                 }
691                 tr = marker_item->transform * marker->c2p * tr;
693                 NR::Matrix old_tr = marker_item->transform;
694                 marker_item->transform = tr;
695                 sp_item_invoke_print (marker_item, ctx);
696                 marker_item->transform = old_tr;
698                 ++curve_it1;
699                 ++curve_it2;
700             }
701         }
703         if ( shape->marker[SP_MARKER_LOC_END] ) {
704             SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_END]);
705             SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_END]));
707             /* Get reference to last curve in the path.
708              * For moveto-only path, this returns the "closing line segment". */
709             unsigned int index = path_it->size_default();
710             if (index > 0) {
711                 index--;
712             }
713             Geom::Curve const &lastcurve = (*path_it)[index];
715             NR::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
717             if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
718                 tr = NR::scale(style->stroke_width.computed) * tr;
719             }
721             tr = marker_item->transform * marker->c2p * tr;
723             NR::Matrix old_tr = marker_item->transform;
724             marker_item->transform = tr;
725             sp_item_invoke_print (marker_item, ctx);
726             marker_item->transform = old_tr;
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         boost::optional<NR::Rect> paintbox = item->getBounds(NR::identity());
752         if (paintbox) {
753             s->setPaintBox(*paintbox);
754         }
756         if (sp_shape_has_markers (shape)) {
758             /* Dimension the marker views */
759             if (!arenaitem->key) {
760                 NR_ARENA_ITEM_SET_KEY (arenaitem, sp_item_display_key_new (SP_MARKER_LOC_QTY));
761             }
763             for (int i = 0; i < SP_MARKER_LOC_QTY; i++) {
764                 if (shape->marker[i]) {
765                     sp_marker_show_dimension ((SPMarker *) shape->marker[i],
766                                               NR_ARENA_ITEM_GET_KEY (arenaitem) + i - SP_MARKER_LOC,
767                                               sp_shape_number_of_markers (shape, i));
768                 }
769             }
772             /* Update marker views */
773             sp_shape_update_marker_view (shape, arenaitem);
774         }
776         return arenaitem;
779 /**
780  * Hides/removes marker views from the shape.
781  */
782 static void
783 sp_shape_hide (SPItem *item, unsigned int key)
785         SPShape *shape;
786         SPItemView *v;
787         int i;
789         shape = (SPShape *) item;
791         for (i=0; i<SP_MARKER_LOC_QTY; i++) {
792           if (shape->marker[i]) {
793             for (v = item->display; v != NULL; v = v->next) {
794                 if (key == v->key) {
795               sp_marker_hide ((SPMarker *) shape->marker[i],
796                                     NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
797                 }
798             }
799           }
800         }
802         if (((SPItemClass *) parent_class)->hide) {
803           ((SPItemClass *) parent_class)->hide (item, key);
804         }
807 /**
808 * \param shape Shape.
809 * \return TRUE if the shape has any markers, or FALSE if not.
810 */
811 int
812 sp_shape_has_markers (SPShape const *shape)
814     /* Note, we're ignoring 'marker' settings, which technically should apply for
815        all three settings.  This should be fixed later such that if 'marker' is
816        specified, then all three should appear. */
818     return (
819         shape->curve &&
820         (shape->marker[SP_MARKER_LOC_START] ||
821          shape->marker[SP_MARKER_LOC_MID] ||
822          shape->marker[SP_MARKER_LOC_END])
823         );
827 /**
828 * \param shape Shape.
829 * \param type Marker type (e.g. SP_MARKER_LOC_START)
830 * \return Number of markers that the shape has of this type.
831 */
832 int
833 sp_shape_number_of_markers (SPShape *shape, int type)
835     Geom::PathVector const & pathv = shape->curve->get_pathvector();
837     switch(type) {
838         case SP_MARKER_LOC_START:
839             return shape->marker[SP_MARKER_LOC_START] ? pathv.size() : 0;
841         case SP_MARKER_LOC_MID:
842         {
843             if ( shape->marker[SP_MARKER_LOC_MID] ) {
844             guint n = 0;
845                 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
846                     n += path_it->size();
847                     n += path_it->closed() ? 1 : 0;
848                 }
849                 return n;
850             } else {
851                 return 0;
852             }
853         }
855         case SP_MARKER_LOC_END:
856         {
857             if ( shape->marker[SP_MARKER_LOC_END] ) {
858                 guint n = 0;
859                 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
860                     if (!path_it->empty()) {
861                         n++;
862                     }
863                 }
864                 return n;
865             } else {
866                 return 0;
867             }
868         }
870         default:
871             return 0;
872     }
875 /**
876  * Checks if the given marker is used in the shape, and if so, it
877  * releases it by calling sp_marker_hide.  Also detaches signals
878  * and unrefs the marker from the shape.
879  */
880 static void
881 sp_shape_marker_release (SPObject *marker, SPShape *shape)
883         SPItem *item;
884         int i;
886         item = (SPItem *) shape;
888         for (i = SP_MARKER_LOC_START; i < SP_MARKER_LOC_QTY; i++) {
889           if (marker == shape->marker[i]) {
890             SPItemView *v;
891             /* Hide marker */
892             for (v = item->display; v != NULL; v = v->next) {
893               sp_marker_hide ((SPMarker *) (shape->marker[i]), NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
894               /* fixme: Do we need explicit remove here? (Lauris) */
895               /* nr_arena_item_set_mask (v->arenaitem, NULL); */
896             }
897             /* Detach marker */
898             sp_signal_disconnect_by_data (shape->marker[i], item);
899             shape->marker[i] = sp_object_hunref (shape->marker[i], item);
900           }
901         }
904 /**
905  * No-op.  Exists for handling 'modified' messages
906  */
907 static void
908 sp_shape_marker_modified (SPObject */*marker*/, guint /*flags*/, SPItem */*item*/)
910         /* I think mask does update automagically */
911         /* g_warning ("Item %s mask %s modified", SP_OBJECT_ID (item), SP_OBJECT_ID (mask)); */
914 /**
915  * Adds a new marker to shape object at the location indicated by key.  value 
916  * must be a valid URI reference resolvable from the shape object (i.e., present
917  * in the document <defs>).  If the shape object already has a marker
918  * registered at the given position, it is removed first.  Then the
919  * new marker is hrefed and its signals connected.
920  */
921 void
922 sp_shape_set_marker (SPObject *object, unsigned int key, const gchar *value)
924     SPItem *item = (SPItem *) object;
925     SPShape *shape = (SPShape *) object;
927     if (key < SP_MARKER_LOC_START || key > SP_MARKER_LOC_END) {
928         return;
929     }
931     SPObject *mrk = sp_css_uri_reference_resolve (SP_OBJECT_DOCUMENT (object), value);
932     if (mrk != shape->marker[key]) {
933         if (shape->marker[key]) {
934             SPItemView *v;
936             /* Detach marker */
937             shape->release_connect[key].disconnect();
938             shape->modified_connect[key].disconnect();
940             /* Hide marker */
941             for (v = item->display; v != NULL; v = v->next) {
942                 sp_marker_hide ((SPMarker *) (shape->marker[key]),
943                                 NR_ARENA_ITEM_GET_KEY (v->arenaitem) + key);
944                 /* fixme: Do we need explicit remove here? (Lauris) */
945                 /* nr_arena_item_set_mask (v->arenaitem, NULL); */
946             }
948             /* Unref marker */
949             shape->marker[key] = sp_object_hunref (shape->marker[key], object);
950         }
951         if (SP_IS_MARKER (mrk)) {
952             shape->marker[key] = sp_object_href (mrk, object);
953             shape->release_connect[key] = mrk->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_shape_marker_release), shape));
954             shape->modified_connect[key] = mrk->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_shape_marker_modified), shape));
955         }
956     }
961 /* Shape section */
963 /**
964  * Calls any registered handlers for the set_shape action
965  */
966 void
967 sp_shape_set_shape (SPShape *shape)
969         g_return_if_fail (shape != NULL);
970         g_return_if_fail (SP_IS_SHAPE (shape));
972         if (SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape) {
973           SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape (shape);
974         }
977 /**
978  * Adds a curve to the shape.  If owner is specified, a reference
979  * will be made, otherwise the curve will be copied into the shape.
980  * Any existing curve in the shape will be unreferenced first.
981  * This routine also triggers a request to update the display.
982  */
983 void
984 sp_shape_set_curve (SPShape *shape, SPCurve *curve, unsigned int owner)
986         if (shape->curve) {
987                 shape->curve = shape->curve->unref();
988         }
989         if (curve) {
990                 if (owner) {
991                         shape->curve = curve->ref();
992                 } else {
993                         shape->curve = curve->copy();
994                 }
995         }
996         SP_OBJECT(shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
999 /**
1000  * Return duplicate of curve (if any exists) or NULL if there is no curve
1001  */
1002 SPCurve *
1003 sp_shape_get_curve (SPShape *shape)
1005         if (shape->curve) {
1006                 return shape->curve->copy();
1007         }
1008         return NULL;
1011 /**
1012  * Same as sp_shape_set_curve but without updating the display
1013  */
1014 void
1015 sp_shape_set_curve_insync (SPShape *shape, SPCurve *curve, unsigned int owner)
1017         if (shape->curve) {
1018                 shape->curve = shape->curve->unref();
1019         }
1020         if (curve) {
1021                 if (owner) {
1022                         shape->curve = curve->ref();
1023                 } else {
1024                         shape->curve = curve->copy();
1025                 }
1026         }
1029 /**
1030  * Return all nodes in a path that are to be considered for snapping
1031  */
1032 static void sp_shape_snappoints(SPItem const *item, SnapPointsIter p)
1034     g_assert(item != NULL);
1035     g_assert(SP_IS_SHAPE(item));
1037     SPShape const *shape = SP_SHAPE(item);
1038     if (shape->curve == NULL) {
1039         return;
1040     }
1042     Geom::PathVector const &pathv = shape->curve->get_pathvector();
1043     if (pathv.empty())
1044         return;
1046     Geom::Matrix const i2d (sp_item_i2d_affine (item));
1048     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
1049         *p = from_2geom(path_it->initialPoint() * i2d);
1051         Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
1052         Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
1053         while (curve_it2 != path_it->end_closed())
1054         {
1055             /* Test whether to add the node between curve_it1 and curve_it2.
1056              * Loop to end_closed (so always including closing segment), the last node to be added
1057              * is the node between the closing segment and the segment before that one. Regardless
1058              * of the path being closed. If the path is closed, the final point was already added by
1059              * adding the initial point. */
1061             Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2);
1063             // Only add cusp nodes. TODO: Shouldn't this be a preference instead?
1064             if (nodetype == Geom::NODE_NONE) {
1065                 *p = from_2geom(curve_it1->finalPoint() * i2d);
1066             }
1067             if (nodetype == Geom::NODE_CUSP) {
1068                 *p = from_2geom(curve_it1->finalPoint() * i2d);
1069             }
1071             ++curve_it1;
1072             ++curve_it2;
1073         }
1074     }
1077 /*
1078   Local Variables:
1079   mode:c++
1080   c-file-style:"stroustrup"
1081   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1082   indent-tabs-mode:nil
1083   fill-column:99
1084   End:
1085 */
1086 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :