Code

Merging from trunk
[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, Geom::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<Geom::Rect> paintbox = SP_ITEM(object)->getBounds(Geom::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             for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
292                 if (shape->marker[i]) {
293                     sp_marker_show_dimension ((SPMarker *) shape->marker[i],
294                                               NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i - SP_MARKER_LOC,
295                                               sp_shape_number_of_markers (shape, i));
296                 }
297             }
298         }
300         /* Update marker views */
301         for (SPItemView *v = item->display; v != NULL; v = v->next) {
302             sp_shape_update_marker_view (shape, v->arenaitem);
303         }
304     }
307 /**
308  * Calculate the transform required to get a marker's path object in the
309  * right place for particular path segment on a shape.
310  *
311  * \see sp_shape_marker_update_marker_view.
312  *
313  * From SVG spec:
314  * The axes of the temporary new user coordinate system are aligned according to the orient attribute on the 'marker'
315  * element and the slope of the curve at the given vertex. (Note: if there is a discontinuity at a vertex, the slope
316  * is the average of the slopes of the two segments of the curve that join at the given vertex. If a slope cannot be
317  * determined, the slope is assumed to be zero.)
318  *
319  * Reference: http://www.w3.org/TR/SVG11/painting.html#MarkerElement, the `orient' attribute.
320  * Reference for behaviour of zero-length segments:
321  * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
322  */
323 Geom::Matrix
324 sp_shape_marker_get_transform(Geom::Curve const & c1, Geom::Curve const & c2)
326     Geom::Point p = c1.pointAt(1);
327     Geom::Curve * c1_reverse = c1.reverse();
328     Geom::Point tang1 = - c1_reverse->unitTangentAt(0);
329     delete c1_reverse;
330     Geom::Point tang2 = c2.unitTangentAt(0);
332     double const angle1 = Geom::atan2(tang1);
333     double const angle2 = Geom::atan2(tang2);
335     double ret_angle;
336     ret_angle = .5 * (angle1 + angle2);
338     if ( fabs( angle2 - angle1 ) > M_PI ) {
339         /* ret_angle is in the middle of the larger of the two sectors between angle1 and
340          * angle2, so flip it by 180degrees to force it to the middle of the smaller sector.
341          *
342          * (Imagine a circle with rays drawn at angle1 and angle2 from the centre of the
343          * circle.  Those two rays divide the circle into two sectors.)
344          */
345         ret_angle += M_PI;
346     }
348     return Geom::Rotate(ret_angle) * Geom::Translate(p);
350 Geom::Matrix
351 sp_shape_marker_get_transform_at_start(Geom::Curve const & c)
353     Geom::Point p = c.pointAt(0);
354     Geom::Matrix ret = Geom::Translate(p);
356     if ( !c.isDegenerate() ) {
357         Geom::Point tang = c.unitTangentAt(0);
358         double const angle = Geom::atan2(tang);
359         ret = Geom::Rotate(angle) * Geom::Translate(p);
360     } else {
361         /* FIXME: the svg spec says to search for a better alternative than zero angle directionality:
362          * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes */
363     }
365     return ret;
367 Geom::Matrix
368 sp_shape_marker_get_transform_at_end(Geom::Curve const & c)
370     Geom::Point p = c.pointAt(1);
371     Geom::Matrix ret = Geom::Translate(p);
373     if ( !c.isDegenerate() ) {
374         Geom::Curve * c_reverse = c.reverse();
375         Geom::Point tang = - c_reverse->unitTangentAt(0);
376         delete c_reverse;
377         double const angle = Geom::atan2(tang);
378         ret = Geom::Rotate(angle) * Geom::Translate(p);
379     } else {
380         /* FIXME: the svg spec says to search for a better alternative than zero angle directionality:
381          * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes */
382     }
384     return ret;
387 /**
388  * Updates the instances (views) of a given marker in a shape.
389  * Marker views have to be scaled already.  The transformation
390  * is retrieved and then shown by calling sp_marker_show_instance.
391  *
392  * TODO: correctly handle the 'marker' attribute.
393  * "Using the marker property from a style sheet is equivalent to using all three (start, mid, end)."
394  * See painting-marker-03-f.svg in SVG 1.1 Full test suite.
395  */
396 static void
397 sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai)
399     SPStyle *style = ((SPObject *) shape)->style;
401     // position arguments to sp_marker_show_instance, basically counts the amount of markers.
402     int start_pos = 0;
403     int mid_pos = 0;
404     int end_pos = 0;
406     Geom::PathVector const & pathv = shape->curve->get_pathvector();
407     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
408         if ( shape->marker[SP_MARKER_LOC_START] ) {
409             Geom::Matrix const m (sp_shape_marker_get_transform_at_start(path_it->front()));
410             sp_marker_show_instance ((SPMarker* ) shape->marker[SP_MARKER_LOC_START], ai,
411                                      NR_ARENA_ITEM_GET_KEY(ai) + SP_MARKER_LOC_START, start_pos, m,
412                                      style->stroke_width.computed);
413              start_pos++;
414         }
416         if ( shape->marker[SP_MARKER_LOC_MID] && (path_it->size_default() > 1) ) {
417             Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
418             Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
419             while (curve_it2 != path_it->end_default())
420             {
421                 /* Put marker between curve_it1 and curve_it2.
422                  * Loop to end_default (so including closing segment), because when a path is closed,
423                  * there should be a midpoint marker between last segment and closing straight line segment
424                  */
425                 Geom::Matrix const m (sp_shape_marker_get_transform(*curve_it1, *curve_it2));
426                 sp_marker_show_instance ((SPMarker* ) shape->marker[SP_MARKER_LOC_MID], ai,
427                                          NR_ARENA_ITEM_GET_KEY(ai) + SP_MARKER_LOC_MID, mid_pos, m,
428                                          style->stroke_width.computed);
429                 mid_pos++;
431                 ++curve_it1;
432                 ++curve_it2;
433             }
434         }
436         if ( shape->marker[SP_MARKER_LOC_END] ) {
437             /* Get reference to last curve in the path.
438              * For moveto-only path, this returns the "closing line segment". */
439             unsigned int index = path_it->size_default();
440             if (index > 0) {
441                 index--;
442             }
443             Geom::Curve const &lastcurve = (*path_it)[index];
445             Geom::Matrix const m = sp_shape_marker_get_transform_at_end(lastcurve);
446             sp_marker_show_instance ((SPMarker* ) shape->marker[SP_MARKER_LOC_END], ai,
447                                      NR_ARENA_ITEM_GET_KEY(ai) + SP_MARKER_LOC_END, end_pos, m,
448                                      style->stroke_width.computed);
449             end_pos++;
450         }
451     }
454 /**
455  * Sets modified flag for all sub-item views.
456  */
457 static void
458 sp_shape_modified (SPObject *object, unsigned int flags)
460         SPShape *shape = SP_SHAPE (object);
462         if (((SPObjectClass *) (parent_class))->modified) {
463           (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
464         }
466         if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
467                 for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
468                         nr_arena_shape_set_style (NR_ARENA_SHAPE (v->arenaitem), object->style);
469                 }
470         }
473 /**
474  * Calculates the bounding box for item, storing it into bbox.
475  * This also includes the bounding boxes of any markers included in the shape.
476  */
477 static void sp_shape_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags)
479     SPShape const *shape = SP_SHAPE (item);
481     if (shape->curve) {
483         NRRect  cbbox;
485         Geom::Rect geombbox = bounds_exact_transformed(shape->curve->get_pathvector(), transform);
486         cbbox.x0 = geombbox[0][0];
487         cbbox.y0 = geombbox[1][0];
488         cbbox.x1 = geombbox[0][1];
489         cbbox.y1 = geombbox[1][1];
491         if ((SPItem::BBoxType) flags != SPItem::GEOMETRIC_BBOX) {
492             
493             SPStyle* style=SP_OBJECT_STYLE (item);
494             if (!style->stroke.isNone()) {
495                 double const scale = transform.descrim();
496                 if ( fabs(style->stroke_width.computed * scale) > 0.01 ) { // sinon c'est 0=oon veut pas de bord
497                     double const width = MAX(0.125, style->stroke_width.computed * scale);
498                     if ( fabs(cbbox.x1-cbbox.x0) > -0.00001 && fabs(cbbox.y1-cbbox.y0) > -0.00001 ) {
499                         cbbox.x0-=0.5*width;
500                         cbbox.x1+=0.5*width;
501                         cbbox.y0-=0.5*width;
502                         cbbox.y1+=0.5*width;
503                     }
504                 }
505             }
507             // Union with bboxes of the markers, if any
508             if (sp_shape_has_markers (shape)) {
509                 /* TODO: make code prettier: lots of variables can be taken out of the loop! */
510                 Geom::PathVector const & pathv = shape->curve->get_pathvector();
511                 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
512                     if ( shape->marker[SP_MARKER_LOC_START] ) {
513                         SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_START]);
514                         SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_START]));
516                         Geom::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front()));
518                         if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
519                             tr = Geom::Scale(style->stroke_width.computed) * tr;
520                         }
522                         // total marker transform
523                         tr = marker_item->transform * marker->c2p * tr * transform;
525                         // get bbox of the marker with that transform
526                         NRRect marker_bbox;
527                         sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true);
528                         // union it with the shape bbox
529                         nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
530                     }
532                     if ( shape->marker[SP_MARKER_LOC_MID] && (path_it->size_default() > 1) ) {
533                         Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
534                         Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
535                         while (curve_it2 != path_it->end_default())
536                         {
537                             /* Put marker between curve_it1 and curve_it2.
538                              * Loop to end_default (so including closing segment), because when a path is closed,
539                              * there should be a midpoint marker between last segment and closing straight line segment */
541                             SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_MID]);
542                             SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_MID]));
544                             Geom::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
546                             if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
547                                 tr = Geom::Scale(style->stroke_width.computed) * tr;
548                             }
550                             // total marker transform
551                             tr = marker_item->transform * marker->c2p * tr * transform;
553                             // get bbox of the marker with that transform
554                             NRRect marker_bbox;
555                             sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true);
556                             // union it with the shape bbox
557                             nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
559                             ++curve_it1;
560                             ++curve_it2;
561                         }
562                     }
564                     if ( shape->marker[SP_MARKER_LOC_END] ) {
565                         SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_END]);
566                         SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_END]));
568                         /* Get reference to last curve in the path.
569                          * For moveto-only path, this returns the "closing line segment". */
570                         unsigned int index = path_it->size_default();
571                         if (index > 0) {
572                             index--;
573                         }
574                         Geom::Curve const &lastcurve = (*path_it)[index];
576                         Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
578                         if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
579                             tr = Geom::Scale(style->stroke_width.computed) * tr;
580                         }
582                         // total marker transform
583                         tr = marker_item->transform * marker->c2p * tr * transform;
585                         // get bbox of the marker with that transform
586                         NRRect marker_bbox;
587                         sp_item_invoke_bbox (marker_item, &marker_bbox, tr, true);
588                         // union it with the shape bbox
589                         nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
590                     }
591                 }
592             }
593         }
595         // copy our bbox to the variable we're given
596         *bbox = cbbox;
597     }
600 /**
601  * Prepares shape for printing.  Handles printing of comments for printing
602  * debugging, sizes the item to fit into the document width/height,
603  * applies print fill/stroke, sets transforms for markers, and adds
604  * comment labels.
605  */
606 void
607 sp_shape_print (SPItem *item, SPPrintContext *ctx)
609         NRRect pbox, dbox, bbox;
611         SPShape *shape = SP_SHAPE(item);
613         if (!shape->curve) return;
615         gint add_comments = prefs_get_int_attribute_limited ("printing.debug", "add-label-comments", 0, 0, 1);
616         if (add_comments) {
617             gchar * comment = g_strdup_printf("begin '%s'",
618                                               SP_OBJECT(item)->defaultLabel());
619             sp_print_comment(ctx, comment);
620             g_free(comment);
621         }
623     /* fixme: Think (Lauris) */
624     sp_item_invoke_bbox(item, &pbox, NR::identity(), TRUE);
625     dbox.x0 = 0.0;
626     dbox.y0 = 0.0;
627     dbox.x1 = sp_document_width (SP_OBJECT_DOCUMENT (item));
628     dbox.y1 = sp_document_height (SP_OBJECT_DOCUMENT (item));
629     sp_item_bbox_desktop (item, &bbox);
630     Geom::Matrix const i2d(sp_item_i2d_affine(item));
632         SPStyle* style = SP_OBJECT_STYLE (item);
634     if (!style->fill.isNone()) {
635         sp_print_fill (ctx, shape->curve->get_pathvector(), &i2d, style, &pbox, &dbox, &bbox);
636     }
638     if (!style->stroke.isNone()) {
639         sp_print_stroke (ctx, shape->curve->get_pathvector(), &i2d, style, &pbox, &dbox, &bbox);
640     }
642     /* TODO: make code prettier: lots of variables can be taken out of the loop! */
643     Geom::PathVector const & pathv = shape->curve->get_pathvector();
644     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
645         if ( shape->marker[SP_MARKER_LOC_START] ) {
646             SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_START]);
647             SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_START]));
649             Geom::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front()));
651             if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
652                 tr = Geom::Scale(style->stroke_width.computed) * tr;
653             }
655             tr = marker_item->transform * marker->c2p * tr;
657             NR::Matrix old_tr = marker_item->transform;
658             marker_item->transform = tr;
659             sp_item_invoke_print (marker_item, ctx);
660             marker_item->transform = old_tr;
661         }
663         if ( shape->marker[SP_MARKER_LOC_MID] && (path_it->size_default() > 1) ) {
664             Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
665             Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
666             while (curve_it2 != path_it->end_default())
667             {
668                 /* Put marker between curve_it1 and curve_it2.
669                  * Loop to end_default (so including closing segment), because when a path is closed,
670                  * there should be a midpoint marker between last segment and closing straight line segment */
672                 SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_MID]);
673                 SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_MID]));
675                 Geom::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
677                 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
678                     tr = Geom::Scale(style->stroke_width.computed) * tr;
679                 }
681                 tr = marker_item->transform * marker->c2p * tr;
683                 NR::Matrix old_tr = marker_item->transform;
684                 marker_item->transform = tr;
685                 sp_item_invoke_print (marker_item, ctx);
686                 marker_item->transform = old_tr;
688                 ++curve_it1;
689                 ++curve_it2;
690             }
691         }
693         if ( shape->marker[SP_MARKER_LOC_END] ) {
694             SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_END]);
695             SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_END]));
697             /* Get reference to last curve in the path.
698              * For moveto-only path, this returns the "closing line segment". */
699             unsigned int index = path_it->size_default();
700             if (index > 0) {
701                 index--;
702             }
703             Geom::Curve const &lastcurve = (*path_it)[index];
705             Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
707             if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
708                 tr = Geom::Scale(style->stroke_width.computed) * tr;
709             }
711             tr = marker_item->transform * marker->c2p * tr;
713             NR::Matrix old_tr = marker_item->transform;
714             marker_item->transform = tr;
715             sp_item_invoke_print (marker_item, ctx);
716             marker_item->transform = old_tr;
717         }
718     }
720         if (add_comments) {
721             gchar * comment = g_strdup_printf("end '%s'",
722                                               SP_OBJECT(item)->defaultLabel());
723             sp_print_comment(ctx, comment);
724             g_free(comment);
725         }
728 /**
729  * Sets style, path, and paintbox.  Updates marker views, including dimensions.
730  */
731 static NRArenaItem *
732 sp_shape_show (SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int /*flags*/)
734         SPObject *object = SP_OBJECT(item);
735         SPShape *shape = SP_SHAPE(item);
737         NRArenaItem *arenaitem = NRArenaShape::create(arena);
738         NRArenaShape * const s = NR_ARENA_SHAPE(arenaitem);
739         nr_arena_shape_set_style(s, object->style);
740         nr_arena_shape_set_path(s, shape->curve, false);
741         boost::optional<Geom::Rect> paintbox = item->getBounds(Geom::identity());
742         if (paintbox) {
743             s->setPaintBox(*paintbox);
744         }
746         if (sp_shape_has_markers (shape)) {
748             /* Dimension the marker views */
749             if (!arenaitem->key) {
750                 NR_ARENA_ITEM_SET_KEY (arenaitem, sp_item_display_key_new (SP_MARKER_LOC_QTY));
751             }
753             for (int i = 0; i < SP_MARKER_LOC_QTY; i++) {
754                 if (shape->marker[i]) {
755                     sp_marker_show_dimension ((SPMarker *) shape->marker[i],
756                                               NR_ARENA_ITEM_GET_KEY (arenaitem) + i - SP_MARKER_LOC,
757                                               sp_shape_number_of_markers (shape, i));
758                 }
759             }
762             /* Update marker views */
763             sp_shape_update_marker_view (shape, arenaitem);
764         }
766         return arenaitem;
769 /**
770  * Hides/removes marker views from the shape.
771  */
772 static void
773 sp_shape_hide (SPItem *item, unsigned int key)
775         SPShape *shape;
776         SPItemView *v;
777         int i;
779         shape = (SPShape *) item;
781         for (i=0; i<SP_MARKER_LOC_QTY; i++) {
782           if (shape->marker[i]) {
783             for (v = item->display; v != NULL; v = v->next) {
784                 if (key == v->key) {
785               sp_marker_hide ((SPMarker *) shape->marker[i],
786                                     NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
787                 }
788             }
789           }
790         }
792         if (((SPItemClass *) parent_class)->hide) {
793           ((SPItemClass *) parent_class)->hide (item, key);
794         }
797 /**
798 * \param shape Shape.
799 * \return TRUE if the shape has any markers, or FALSE if not.
800 */
801 int
802 sp_shape_has_markers (SPShape const *shape)
804     /* Note, we're ignoring 'marker' settings, which technically should apply for
805        all three settings.  This should be fixed later such that if 'marker' is
806        specified, then all three should appear. */
808     return (
809         shape->curve &&
810         (shape->marker[SP_MARKER_LOC_START] ||
811          shape->marker[SP_MARKER_LOC_MID] ||
812          shape->marker[SP_MARKER_LOC_END])
813         );
817 /**
818 * \param shape Shape.
819 * \param type Marker type (e.g. SP_MARKER_LOC_START)
820 * \return Number of markers that the shape has of this type.
821 */
822 int
823 sp_shape_number_of_markers (SPShape *shape, int type)
825     Geom::PathVector const & pathv = shape->curve->get_pathvector();
827     switch(type) {
828         case SP_MARKER_LOC_START:
829             return shape->marker[SP_MARKER_LOC_START] ? pathv.size() : 0;
831         case SP_MARKER_LOC_MID:
832         {
833             if ( shape->marker[SP_MARKER_LOC_MID] ) {
834             guint n = 0;
835                 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
836                     n += path_it->size();
837                     n += path_it->closed() ? 1 : 0;
838                 }
839                 return n;
840             } else {
841                 return 0;
842             }
843         }
845         case SP_MARKER_LOC_END:
846         {
847             if ( shape->marker[SP_MARKER_LOC_END] ) {
848                 guint n = 0;
849                 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
850                     if (!path_it->empty()) {
851                         n++;
852                     }
853                 }
854                 return n;
855             } else {
856                 return 0;
857             }
858         }
860         default:
861             return 0;
862     }
865 /**
866  * Checks if the given marker is used in the shape, and if so, it
867  * releases it by calling sp_marker_hide.  Also detaches signals
868  * and unrefs the marker from the shape.
869  */
870 static void
871 sp_shape_marker_release (SPObject *marker, SPShape *shape)
873         SPItem *item;
874         int i;
876         item = (SPItem *) shape;
878         for (i = SP_MARKER_LOC_START; i < SP_MARKER_LOC_QTY; i++) {
879           if (marker == shape->marker[i]) {
880             SPItemView *v;
881             /* Hide marker */
882             for (v = item->display; v != NULL; v = v->next) {
883               sp_marker_hide ((SPMarker *) (shape->marker[i]), NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
884               /* fixme: Do we need explicit remove here? (Lauris) */
885               /* nr_arena_item_set_mask (v->arenaitem, NULL); */
886             }
887             /* Detach marker */
888             sp_signal_disconnect_by_data (shape->marker[i], item);
889             shape->marker[i] = sp_object_hunref (shape->marker[i], item);
890           }
891         }
894 /**
895  * No-op.  Exists for handling 'modified' messages
896  */
897 static void
898 sp_shape_marker_modified (SPObject */*marker*/, guint /*flags*/, SPItem */*item*/)
900         /* I think mask does update automagically */
901         /* g_warning ("Item %s mask %s modified", SP_OBJECT_ID (item), SP_OBJECT_ID (mask)); */
904 /**
905  * Adds a new marker to shape object at the location indicated by key.  value 
906  * must be a valid URI reference resolvable from the shape object (i.e., present
907  * in the document <defs>).  If the shape object already has a marker
908  * registered at the given position, it is removed first.  Then the
909  * new marker is hrefed and its signals connected.
910  */
911 void
912 sp_shape_set_marker (SPObject *object, unsigned int key, const gchar *value)
914     SPItem *item = (SPItem *) object;
915     SPShape *shape = (SPShape *) object;
917     if (key < SP_MARKER_LOC_START || key > SP_MARKER_LOC_END) {
918         return;
919     }
921     SPObject *mrk = sp_css_uri_reference_resolve (SP_OBJECT_DOCUMENT (object), value);
922     if (mrk != shape->marker[key]) {
923         if (shape->marker[key]) {
924             SPItemView *v;
926             /* Detach marker */
927             shape->release_connect[key].disconnect();
928             shape->modified_connect[key].disconnect();
930             /* Hide marker */
931             for (v = item->display; v != NULL; v = v->next) {
932                 sp_marker_hide ((SPMarker *) (shape->marker[key]),
933                                 NR_ARENA_ITEM_GET_KEY (v->arenaitem) + key);
934                 /* fixme: Do we need explicit remove here? (Lauris) */
935                 /* nr_arena_item_set_mask (v->arenaitem, NULL); */
936             }
938             /* Unref marker */
939             shape->marker[key] = sp_object_hunref (shape->marker[key], object);
940         }
941         if (SP_IS_MARKER (mrk)) {
942             shape->marker[key] = sp_object_href (mrk, object);
943             shape->release_connect[key] = mrk->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_shape_marker_release), shape));
944             shape->modified_connect[key] = mrk->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_shape_marker_modified), shape));
945         }
946     }
951 /* Shape section */
953 /**
954  * Calls any registered handlers for the set_shape action
955  */
956 void
957 sp_shape_set_shape (SPShape *shape)
959         g_return_if_fail (shape != NULL);
960         g_return_if_fail (SP_IS_SHAPE (shape));
962         if (SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape) {
963           SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape (shape);
964         }
967 /**
968  * Adds a curve to the shape.  If owner is specified, a reference
969  * will be made, otherwise the curve will be copied into the shape.
970  * Any existing curve in the shape will be unreferenced first.
971  * This routine also triggers a request to update the display.
972  */
973 void
974 sp_shape_set_curve (SPShape *shape, SPCurve *curve, unsigned int owner)
976         if (shape->curve) {
977                 shape->curve = shape->curve->unref();
978         }
979         if (curve) {
980                 if (owner) {
981                         shape->curve = curve->ref();
982                 } else {
983                         shape->curve = curve->copy();
984                 }
985         }
986         SP_OBJECT(shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
989 /**
990  * Return duplicate of curve (if any exists) or NULL if there is no curve
991  */
992 SPCurve *
993 sp_shape_get_curve (SPShape *shape)
995         if (shape->curve) {
996                 return shape->curve->copy();
997         }
998         return NULL;
1001 /**
1002  * Same as sp_shape_set_curve but without updating the display
1003  */
1004 void
1005 sp_shape_set_curve_insync (SPShape *shape, SPCurve *curve, unsigned int owner)
1007         if (shape->curve) {
1008                 shape->curve = shape->curve->unref();
1009         }
1010         if (curve) {
1011                 if (owner) {
1012                         shape->curve = curve->ref();
1013                 } else {
1014                         shape->curve = curve->copy();
1015                 }
1016         }
1019 /**
1020  * Return all nodes in a path that are to be considered for snapping
1021  */
1022 static void sp_shape_snappoints(SPItem const *item, SnapPointsIter p)
1024     g_assert(item != NULL);
1025     g_assert(SP_IS_SHAPE(item));
1027     SPShape const *shape = SP_SHAPE(item);
1028     if (shape->curve == NULL) {
1029         return;
1030     }
1032     Geom::PathVector const &pathv = shape->curve->get_pathvector();
1033     if (pathv.empty())
1034         return;
1036     Geom::Matrix const i2d (sp_item_i2d_affine (item));
1038     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
1039         *p = from_2geom(path_it->initialPoint() * i2d);
1041         Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
1042         Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
1043         while (curve_it2 != path_it->end_closed())
1044         {
1045             /* Test whether to add the node between curve_it1 and curve_it2.
1046              * Loop to end_closed (so always including closing segment), the last node to be added
1047              * is the node between the closing segment and the segment before that one. Regardless
1048              * of the path being closed. If the path is closed, the final point was already added by
1049              * adding the initial point. */
1051             Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2);
1053             // Only add cusp nodes. TODO: Shouldn't this be a preference instead?
1054             if (nodetype == Geom::NODE_NONE) {
1055                 *p = from_2geom(curve_it1->finalPoint() * i2d);
1056             }
1057             if (nodetype == Geom::NODE_CUSP) {
1058                 *p = from_2geom(curve_it1->finalPoint() * i2d);
1059             }
1061             ++curve_it1;
1062             ++curve_it2;
1063         }
1064     }
1067 /*
1068   Local Variables:
1069   mode:c++
1070   c-file-style:"stroustrup"
1071   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1072   indent-tabs-mode:nil
1073   fill-column:99
1074   End:
1075 */
1076 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :