Code

d3f357e7688f51e1d6a025aa04eebe1d7738d0ed
[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 "preferences.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, Inkscape::SnapPreferences const *snapprefs);
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     }
175     for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
176         sp_shape_set_marker (object, i, object->style->marker[i].value);
177           }
180 /**
181  * Removes, releases and unrefs all children of object
182  *
183  * This is the inverse of sp_shape_build().  It must be invoked as soon
184  * as the shape is removed from the tree, even if it is still referenced
185  * by other objects.  This routine also disconnects/unrefs markers and
186  * curves attached to it.
187  *
188  * \see sp_object_release()
189  */
190 static void
191 sp_shape_release (SPObject *object)
193         SPItem *item;
194         SPShape *shape;
195         SPItemView *v;
196         int i;
198         item = (SPItem *) object;
199         shape = (SPShape *) object;
201         for (i=SP_MARKER_LOC_START; i<SP_MARKER_LOC_QTY; i++) {
202           if (shape->marker[i]) {
203             for (v = item->display; v != NULL; v = v->next) {
204               sp_marker_hide ((SPMarker *) shape->marker[i], NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
205             }
206       shape->release_connect[i].disconnect();
207       shape->modified_connect[i].disconnect();
208             shape->marker[i] = sp_object_hunref (shape->marker[i], object);
209           }
210         }
211         if (shape->curve) {
212                 shape->curve = shape->curve->unref();
213         }
214     
215         if (((SPObjectClass *) parent_class)->release) {
216           ((SPObjectClass *) parent_class)->release (object);
217         }
222 static void
223 sp_shape_set(SPObject *object, unsigned int key, gchar const *value)
225     if (((SPObjectClass *) parent_class)->set) {
226         ((SPObjectClass *) parent_class)->set(object, key, value);
227     }
230 static Inkscape::XML::Node *
231 sp_shape_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags)
233     if (((SPObjectClass *)(parent_class))->write) {
234         ((SPObjectClass *)(parent_class))->write(object, doc, repr, flags);
235     }
237     return repr;
240 /** 
241  * Updates the shape when its attributes have changed.  Also establishes
242  * marker objects to match the style settings.  
243  */
244 static void
245 sp_shape_update (SPObject *object, SPCtx *ctx, unsigned int flags)
247     SPItem *item = (SPItem *) object;
248     SPShape *shape = (SPShape *) object;
250     if (((SPObjectClass *) (parent_class))->update) {
251         (* ((SPObjectClass *) (parent_class))->update) (object, ctx, flags);
252     }
254     /* This stanza checks that an object's marker style agrees with
255      * the marker objects it has allocated.  sp_shape_set_marker ensures
256      * that the appropriate marker objects are present (or absent) to
257      * match the style.
258      */
259     for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
260         sp_shape_set_marker (object, i, object->style->marker[i].value);
261           }
263     if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
264         SPStyle *style;
265         style = SP_OBJECT_STYLE (object);
266         if (style->stroke_width.unit == SP_CSS_UNIT_PERCENT) {
267             SPItemCtx *ictx = (SPItemCtx *) ctx;
268             double const aw = 1.0 / NR::expansion(ictx->i2vp);
269             style->stroke_width.computed = style->stroke_width.value * aw;
270             for (SPItemView *v = ((SPItem *) (shape))->display; v != NULL; v = v->next) {
271                 nr_arena_shape_set_style ((NRArenaShape *) v->arenaitem, style);
272             }
273         }
274     }
276     if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) {
277         /* This is suboptimal, because changing parent style schedules recalculation */
278         /* But on the other hand - how can we know that parent does not tie style and transform */
279         Geom::OptRect paintbox = SP_ITEM(object)->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
280         for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
281             NRArenaShape * const s = NR_ARENA_SHAPE(v->arenaitem);
282             if (flags & SP_OBJECT_MODIFIED_FLAG) {
283                 nr_arena_shape_set_path(s, shape->curve, (flags & SP_OBJECT_USER_MODIFIED_FLAG_B));
284             }
285             if (paintbox) {
286                 s->setPaintBox(*paintbox);
287             }
288         }
289     }
291     if (sp_shape_has_markers (shape)) {
292         /* Dimension marker views */
293         for (SPItemView *v = item->display; v != NULL; v = v->next) {
294             if (!v->arenaitem->key) {
295                 NR_ARENA_ITEM_SET_KEY (v->arenaitem, sp_item_display_key_new (SP_MARKER_LOC_QTY));
296             }
297             for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
298                 if (shape->marker[i]) {
299                     sp_marker_show_dimension ((SPMarker *) shape->marker[i],
300                                               NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i - SP_MARKER_LOC,
301                                               sp_shape_number_of_markers (shape, i));
302                 }
303             }
304         }
306         /* Update marker views */
307         for (SPItemView *v = item->display; v != NULL; v = v->next) {
308             sp_shape_update_marker_view (shape, v->arenaitem);
309         }
310     }
313 /**
314  * Calculate the transform required to get a marker's path object in the
315  * right place for particular path segment on a shape.
316  *
317  * \see sp_shape_marker_update_marker_view.
318  *
319  * From SVG spec:
320  * The axes of the temporary new user coordinate system are aligned according to the orient attribute on the 'marker'
321  * element and the slope of the curve at the given vertex. (Note: if there is a discontinuity at a vertex, the slope
322  * is the average of the slopes of the two segments of the curve that join at the given vertex. If a slope cannot be
323  * determined, the slope is assumed to be zero.)
324  *
325  * Reference: http://www.w3.org/TR/SVG11/painting.html#MarkerElement, the `orient' attribute.
326  * Reference for behaviour of zero-length segments:
327  * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
328  */
329 Geom::Matrix
330 sp_shape_marker_get_transform(Geom::Curve const & c1, Geom::Curve const & c2)
332     Geom::Point p = c1.pointAt(1);
333     Geom::Curve * c1_reverse = c1.reverse();
334     Geom::Point tang1 = - c1_reverse->unitTangentAt(0);
335     delete c1_reverse;
336     Geom::Point tang2 = c2.unitTangentAt(0);
338     double const angle1 = Geom::atan2(tang1);
339     double const angle2 = Geom::atan2(tang2);
341     double ret_angle;
342     ret_angle = .5 * (angle1 + angle2);
344     if ( fabs( angle2 - angle1 ) > M_PI ) {
345         /* ret_angle is in the middle of the larger of the two sectors between angle1 and
346          * angle2, so flip it by 180degrees to force it to the middle of the smaller sector.
347          *
348          * (Imagine a circle with rays drawn at angle1 and angle2 from the centre of the
349          * circle.  Those two rays divide the circle into two sectors.)
350          */
351         ret_angle += M_PI;
352     }
354     return Geom::Rotate(ret_angle) * Geom::Translate(p);
356 Geom::Matrix
357 sp_shape_marker_get_transform_at_start(Geom::Curve const & c)
359     Geom::Point p = c.pointAt(0);
360     Geom::Matrix ret = Geom::Translate(p);
362     if ( !c.isDegenerate() ) {
363         Geom::Point tang = c.unitTangentAt(0);
364         double const angle = Geom::atan2(tang);
365         ret = Geom::Rotate(angle) * Geom::Translate(p);
366     } else {
367         /* FIXME: the svg spec says to search for a better alternative than zero angle directionality:
368          * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes */
369     }
371     return ret;
373 Geom::Matrix
374 sp_shape_marker_get_transform_at_end(Geom::Curve const & c)
376     Geom::Point p = c.pointAt(1);
377     Geom::Matrix ret = Geom::Translate(p);
379     if ( !c.isDegenerate() ) {
380         Geom::Curve * c_reverse = c.reverse();
381         Geom::Point tang = - c_reverse->unitTangentAt(0);
382         delete c_reverse;
383         double const angle = Geom::atan2(tang);
384         ret = Geom::Rotate(angle) * Geom::Translate(p);
385     } else {
386         /* FIXME: the svg spec says to search for a better alternative than zero angle directionality:
387          * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes */
388     }
390     return ret;
393 /**
394  * Updates the instances (views) of a given marker in a shape.
395  * Marker views have to be scaled already.  The transformation
396  * is retrieved and then shown by calling sp_marker_show_instance.
397  *
398  * TODO: correctly handle the 'marker' attribute.
399  * "Using the marker property from a style sheet is equivalent to using all three (start, mid, end)."
400  * See painting-marker-03-f.svg in SVG 1.1 Full test suite.
401  */
402 static void
403 sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai)
405     SPStyle *style = ((SPObject *) shape)->style;
407     // position arguments to sp_marker_show_instance, basically counts the amount of markers.
408     int start_pos = 0;
409     int mid_pos = 0;
410     int end_pos = 0;
412     Geom::PathVector const & pathv = shape->curve->get_pathvector();
413     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
414         if ( shape->marker[SP_MARKER_LOC_START] ) {
415             Geom::Matrix const m (sp_shape_marker_get_transform_at_start(path_it->front()));
416             sp_marker_show_instance ((SPMarker* ) shape->marker[SP_MARKER_LOC_START], ai,
417                                      NR_ARENA_ITEM_GET_KEY(ai) + SP_MARKER_LOC_START, start_pos, m,
418                                      style->stroke_width.computed);
419              start_pos++;
420         }
422         if ( shape->marker[SP_MARKER_LOC_MID] && (path_it->size_default() > 1) ) {
423             Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
424             Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
425             while (curve_it2 != path_it->end_default())
426             {
427                 /* Put marker between curve_it1 and curve_it2.
428                  * Loop to end_default (so including closing segment), because when a path is closed,
429                  * there should be a midpoint marker between last segment and closing straight line segment
430                  */
431                 Geom::Matrix const m (sp_shape_marker_get_transform(*curve_it1, *curve_it2));
432                 sp_marker_show_instance ((SPMarker* ) shape->marker[SP_MARKER_LOC_MID], ai,
433                                          NR_ARENA_ITEM_GET_KEY(ai) + SP_MARKER_LOC_MID, mid_pos, m,
434                                          style->stroke_width.computed);
435                 mid_pos++;
437                 ++curve_it1;
438                 ++curve_it2;
439             }
440         }
442         if ( shape->marker[SP_MARKER_LOC_END] ) {
443             /* Get reference to last curve in the path.
444              * For moveto-only path, this returns the "closing line segment". */
445             unsigned int index = path_it->size_default();
446             if (index > 0) {
447                 index--;
448             }
449             Geom::Curve const &lastcurve = (*path_it)[index];
451             Geom::Matrix const m = sp_shape_marker_get_transform_at_end(lastcurve);
452             sp_marker_show_instance ((SPMarker* ) shape->marker[SP_MARKER_LOC_END], ai,
453                                      NR_ARENA_ITEM_GET_KEY(ai) + SP_MARKER_LOC_END, end_pos, m,
454                                      style->stroke_width.computed);
455             end_pos++;
456         }
457     }
460 /**
461  * Sets modified flag for all sub-item views.
462  */
463 static void
464 sp_shape_modified (SPObject *object, unsigned int flags)
466         SPShape *shape = SP_SHAPE (object);
468         if (((SPObjectClass *) (parent_class))->modified) {
469           (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
470         }
472         if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
473                 for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
474                         nr_arena_shape_set_style (NR_ARENA_SHAPE (v->arenaitem), object->style);
475                 }
476         }
479 /**
480  * Calculates the bounding box for item, storing it into bbox.
481  * This also includes the bounding boxes of any markers included in the shape.
482  */
483 static void sp_shape_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags)
485     SPShape const *shape = SP_SHAPE (item);
486     if (shape->curve) {
487         Geom::OptRect geombbox = bounds_exact_transformed(shape->curve->get_pathvector(), transform);
488         if (geombbox) {
489             NRRect  cbbox;
490             cbbox.x0 = (*geombbox)[0][0];
491             cbbox.y0 = (*geombbox)[1][0];
492             cbbox.x1 = (*geombbox)[0][1];
493             cbbox.y1 = (*geombbox)[1][1];
495             if ((SPItem::BBoxType) flags != SPItem::GEOMETRIC_BBOX) {
496                 
497                 SPStyle* style=SP_OBJECT_STYLE (item);
498                 if (!style->stroke.isNone()) {
499                     double const scale = transform.descrim();
500                     if ( fabs(style->stroke_width.computed * scale) > 0.01 ) { // sinon c'est 0=oon veut pas de bord
501                         double const width = MAX(0.125, style->stroke_width.computed * scale);
502                         if ( fabs(cbbox.x1-cbbox.x0) > -0.00001 && fabs(cbbox.y1-cbbox.y0) > -0.00001 ) {
503                             cbbox.x0-=0.5*width;
504                             cbbox.x1+=0.5*width;
505                             cbbox.y0-=0.5*width;
506                             cbbox.y1+=0.5*width;
507                         }
508                     }
509                 }
511                 // Union with bboxes of the markers, if any
512                 if (sp_shape_has_markers (shape)) {
513                     /* TODO: make code prettier: lots of variables can be taken out of the loop! */
514                     Geom::PathVector const & pathv = shape->curve->get_pathvector();
515                     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
516                         if ( shape->marker[SP_MARKER_LOC_START] ) {
517                             SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_START]);
518                             SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_START]));
520                             Geom::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front()));
522                             if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
523                                 tr = Geom::Scale(style->stroke_width.computed) * tr;
524                             }
526                             // total marker transform
527                             tr = marker_item->transform * marker->c2p * tr * transform;
529                             // get bbox of the marker with that transform
530                             NRRect marker_bbox;
531                             sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true);
532                             // union it with the shape bbox
533                             nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
534                         }
536                         if ( shape->marker[SP_MARKER_LOC_MID] && (path_it->size_default() > 1) ) {
537                             Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
538                             Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
539                             while (curve_it2 != path_it->end_default())
540                             {
541                                 /* Put marker between curve_it1 and curve_it2.
542                                  * Loop to end_default (so including closing segment), because when a path is closed,
543                                  * there should be a midpoint marker between last segment and closing straight line segment */
545                                 SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_MID]);
546                                 SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_MID]));
548                                 Geom::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
550                                 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
551                                     tr = Geom::Scale(style->stroke_width.computed) * tr;
552                                 }
554                                 // total marker transform
555                                 tr = marker_item->transform * marker->c2p * tr * transform;
557                                 // get bbox of the marker with that transform
558                                 NRRect marker_bbox;
559                                 sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true);
560                                 // union it with the shape bbox
561                                 nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
563                                 ++curve_it1;
564                                 ++curve_it2;
565                             }
566                         }
568                         if ( shape->marker[SP_MARKER_LOC_END] ) {
569                             SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_END]);
570                             SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_END]));
572                             /* Get reference to last curve in the path.
573                              * For moveto-only path, this returns the "closing line segment". */
574                             unsigned int index = path_it->size_default();
575                             if (index > 0) {
576                                 index--;
577                             }
578                             Geom::Curve const &lastcurve = (*path_it)[index];
580                             Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
582                             if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
583                                 tr = Geom::Scale(style->stroke_width.computed) * tr;
584                             }
586                             // total marker transform
587                             tr = marker_item->transform * marker->c2p * tr * transform;
589                             // get bbox of the marker with that transform
590                             NRRect marker_bbox;
591                             sp_item_invoke_bbox (marker_item, &marker_bbox, tr, true);
592                             // union it with the shape bbox
593                             nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
594                         }
595                     }
596                 }
597             }
599             // copy our bbox to the variable we're given
600             *bbox = cbbox;
601         }
602     }
605 /**
606  * Prepares shape for printing.  Handles printing of comments for printing
607  * debugging, sizes the item to fit into the document width/height,
608  * applies print fill/stroke, sets transforms for markers, and adds
609  * comment labels.
610  */
611 void
612 sp_shape_print (SPItem *item, SPPrintContext *ctx)
614         NRRect pbox, dbox, bbox;
616         SPShape *shape = SP_SHAPE(item);
618         if (!shape->curve) return;
620         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
621         gint add_comments = prefs->getBool("/printing/debug/add-label-comments");
622         if (add_comments) {
623             gchar * comment = g_strdup_printf("begin '%s'",
624                                               SP_OBJECT(item)->defaultLabel());
625             sp_print_comment(ctx, comment);
626             g_free(comment);
627         }
629     /* fixme: Think (Lauris) */
630     sp_item_invoke_bbox(item, &pbox, NR::identity(), TRUE);
631     dbox.x0 = 0.0;
632     dbox.y0 = 0.0;
633     dbox.x1 = sp_document_width (SP_OBJECT_DOCUMENT (item));
634     dbox.y1 = sp_document_height (SP_OBJECT_DOCUMENT (item));
635     sp_item_bbox_desktop (item, &bbox);
636     Geom::Matrix const i2d(sp_item_i2d_affine(item));
638         SPStyle* style = SP_OBJECT_STYLE (item);
640     if (!style->fill.isNone()) {
641         sp_print_fill (ctx, shape->curve->get_pathvector(), &i2d, style, &pbox, &dbox, &bbox);
642     }
644     if (!style->stroke.isNone()) {
645         sp_print_stroke (ctx, shape->curve->get_pathvector(), &i2d, style, &pbox, &dbox, &bbox);
646     }
648     /* TODO: make code prettier: lots of variables can be taken out of the loop! */
649     Geom::PathVector const & pathv = shape->curve->get_pathvector();
650     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
651         if ( shape->marker[SP_MARKER_LOC_START] ) {
652             SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_START]);
653             SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_START]));
655             Geom::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front()));
657             if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
658                 tr = Geom::Scale(style->stroke_width.computed) * tr;
659             }
661             tr = marker_item->transform * marker->c2p * tr;
663             NR::Matrix old_tr = marker_item->transform;
664             marker_item->transform = tr;
665             sp_item_invoke_print (marker_item, ctx);
666             marker_item->transform = old_tr;
667         }
669         if ( shape->marker[SP_MARKER_LOC_MID] && (path_it->size_default() > 1) ) {
670             Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
671             Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
672             while (curve_it2 != path_it->end_default())
673             {
674                 /* Put marker between curve_it1 and curve_it2.
675                  * Loop to end_default (so including closing segment), because when a path is closed,
676                  * there should be a midpoint marker between last segment and closing straight line segment */
678                 SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_MID]);
679                 SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_MID]));
681                 Geom::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
683                 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
684                     tr = Geom::Scale(style->stroke_width.computed) * tr;
685                 }
687                 tr = marker_item->transform * marker->c2p * tr;
689                 NR::Matrix old_tr = marker_item->transform;
690                 marker_item->transform = tr;
691                 sp_item_invoke_print (marker_item, ctx);
692                 marker_item->transform = old_tr;
694                 ++curve_it1;
695                 ++curve_it2;
696             }
697         }
699         if ( shape->marker[SP_MARKER_LOC_END] ) {
700             SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_END]);
701             SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_END]));
703             /* Get reference to last curve in the path.
704              * For moveto-only path, this returns the "closing line segment". */
705             unsigned int index = path_it->size_default();
706             if (index > 0) {
707                 index--;
708             }
709             Geom::Curve const &lastcurve = (*path_it)[index];
711             Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
713             if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
714                 tr = Geom::Scale(style->stroke_width.computed) * tr;
715             }
717             tr = marker_item->transform * marker->c2p * tr;
719             NR::Matrix old_tr = marker_item->transform;
720             marker_item->transform = tr;
721             sp_item_invoke_print (marker_item, ctx);
722             marker_item->transform = old_tr;
723         }
724     }
726         if (add_comments) {
727             gchar * comment = g_strdup_printf("end '%s'",
728                                               SP_OBJECT(item)->defaultLabel());
729             sp_print_comment(ctx, comment);
730             g_free(comment);
731         }
734 /**
735  * Sets style, path, and paintbox.  Updates marker views, including dimensions.
736  */
737 static NRArenaItem *
738 sp_shape_show (SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int /*flags*/)
740     SPObject *object = SP_OBJECT(item);
741     SPShape *shape = SP_SHAPE(item);
743     NRArenaItem *arenaitem = NRArenaShape::create(arena);
744     NRArenaShape * const s = NR_ARENA_SHAPE(arenaitem);
745     nr_arena_shape_set_style(s, object->style);
746     nr_arena_shape_set_path(s, shape->curve, false);
747     Geom::OptRect paintbox = item->getBounds(Geom::identity());
748     if (paintbox) {
749         s->setPaintBox(*paintbox);
750     }
752     /* This stanza checks that an object's marker style agrees with
753      * the marker objects it has allocated.  sp_shape_set_marker ensures
754      * that the appropriate marker objects are present (or absent) to
755      * match the style.
756      */
757     for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
758         sp_shape_set_marker (object, i, object->style->marker[i].value);
759           }
761     if (sp_shape_has_markers (shape)) {
763         /* provide key and dimension the marker views */
764         if (!arenaitem->key) {
765             NR_ARENA_ITEM_SET_KEY (arenaitem, sp_item_display_key_new (SP_MARKER_LOC_QTY));
766         }
768         for (int i = 0; i < SP_MARKER_LOC_QTY; i++) {
769             if (shape->marker[i]) {
770                 sp_marker_show_dimension ((SPMarker *) shape->marker[i],
771                                           NR_ARENA_ITEM_GET_KEY (arenaitem) + i - SP_MARKER_LOC,
772                                           sp_shape_number_of_markers (shape, i));
773             }
774         }
776         /* Update marker views */
777         sp_shape_update_marker_view (shape, arenaitem);
778     }
780     return arenaitem;
783 /**
784  * Hides/removes marker views from the shape.
785  */
786 static void
787 sp_shape_hide (SPItem *item, unsigned int key)
789         SPShape *shape;
790         SPItemView *v;
791         int i;
793         shape = (SPShape *) item;
795         for (i=0; i<SP_MARKER_LOC_QTY; i++) {
796           if (shape->marker[i]) {
797             for (v = item->display; v != NULL; v = v->next) {
798                 if (key == v->key) {
799               sp_marker_hide ((SPMarker *) shape->marker[i],
800                                     NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
801                 }
802             }
803           }
804         }
806         if (((SPItemClass *) parent_class)->hide) {
807           ((SPItemClass *) parent_class)->hide (item, key);
808         }
811 /**
812 * \param shape Shape.
813 * \return TRUE if the shape has any markers, or FALSE if not.
814 */
815 int
816 sp_shape_has_markers (SPShape const *shape)
818     /* Note, we're ignoring 'marker' settings, which technically should apply for
819        all three settings.  This should be fixed later such that if 'marker' is
820        specified, then all three should appear. */
822     return (
823         shape->curve &&
824         (shape->marker[SP_MARKER_LOC_START] ||
825          shape->marker[SP_MARKER_LOC_MID] ||
826          shape->marker[SP_MARKER_LOC_END])
827         );
831 /**
832 * \param shape Shape.
833 * \param type Marker type (e.g. SP_MARKER_LOC_START)
834 * \return Number of markers that the shape has of this type.
835 */
836 int
837 sp_shape_number_of_markers (SPShape *shape, int type)
839     Geom::PathVector const & pathv = shape->curve->get_pathvector();
841     switch(type) {
842         case SP_MARKER_LOC_START:
843             return shape->marker[SP_MARKER_LOC_START] ? pathv.size() : 0;
845         case SP_MARKER_LOC_MID:
846         {
847             if ( shape->marker[SP_MARKER_LOC_MID] ) {
848             guint n = 0;
849                 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
850                     n += path_it->size();
851                     n += path_it->closed() ? 1 : 0;
852                 }
853                 return n;
854             } else {
855                 return 0;
856             }
857         }
859         case SP_MARKER_LOC_END:
860         {
861             if ( shape->marker[SP_MARKER_LOC_END] ) {
862                 guint n = 0;
863                 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
864                     if (!path_it->empty()) {
865                         n++;
866                     }
867                 }
868                 return n;
869             } else {
870                 return 0;
871             }
872         }
874         default:
875             return 0;
876     }
879 /**
880  * Checks if the given marker is used in the shape, and if so, it
881  * releases it by calling sp_marker_hide.  Also detaches signals
882  * and unrefs the marker from the shape.
883  */
884 static void
885 sp_shape_marker_release (SPObject *marker, SPShape *shape)
887         SPItem *item;
888         int i;
890         item = (SPItem *) shape;
892         for (i = SP_MARKER_LOC_START; i < SP_MARKER_LOC_QTY; i++) {
893           if (marker == shape->marker[i]) {
894             SPItemView *v;
895             /* Hide marker */
896             for (v = item->display; v != NULL; v = v->next) {
897               sp_marker_hide ((SPMarker *) (shape->marker[i]), NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
898               /* fixme: Do we need explicit remove here? (Lauris) */
899               /* nr_arena_item_set_mask (v->arenaitem, NULL); */
900             }
901             /* Detach marker */
902       shape->release_connect[i].disconnect();
903       shape->modified_connect[i].disconnect();
904             shape->marker[i] = sp_object_hunref (shape->marker[i], item);
905           }
906         }
909 /**
910  * No-op.  Exists for handling 'modified' messages
911  */
912 static void
913 sp_shape_marker_modified (SPObject */*marker*/, guint /*flags*/, SPItem */*item*/)
915         /* I think mask does update automagically */
916         /* g_warning ("Item %s mask %s modified", SP_OBJECT_ID (item), SP_OBJECT_ID (mask)); */
919 /**
920  * Adds a new marker to shape object at the location indicated by key.  value 
921  * must be a valid URI reference resolvable from the shape object (i.e., present
922  * in the document <defs>).  If the shape object already has a marker
923  * registered at the given position, it is removed first.  Then the
924  * new marker is hrefed and its signals connected.
925  */
926 void
927 sp_shape_set_marker (SPObject *object, unsigned int key, const gchar *value)
929     SPItem *item = (SPItem *) object;
930     SPShape *shape = (SPShape *) object;
932     if (key < SP_MARKER_LOC_START || key > SP_MARKER_LOC_END) {
933         return;
934     }
936     SPObject *mrk = sp_css_uri_reference_resolve (SP_OBJECT_DOCUMENT (object), value);
937     if (mrk != shape->marker[key]) {
938         if (shape->marker[key]) {
939             SPItemView *v;
941             /* Detach marker */
942             shape->release_connect[key].disconnect();
943             shape->modified_connect[key].disconnect();
945             /* Hide marker */
946             for (v = item->display; v != NULL; v = v->next) {
947                 sp_marker_hide ((SPMarker *) (shape->marker[key]),
948                                 NR_ARENA_ITEM_GET_KEY (v->arenaitem) + key);
949                 /* fixme: Do we need explicit remove here? (Lauris) */
950                 /* nr_arena_item_set_mask (v->arenaitem, NULL); */
951             }
953             /* Unref marker */
954             shape->marker[key] = sp_object_hunref (shape->marker[key], object);
955         }
956         if (SP_IS_MARKER (mrk)) {
957             shape->marker[key] = sp_object_href (mrk, object);
958             shape->release_connect[key] = mrk->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_shape_marker_release), shape));
959             shape->modified_connect[key] = mrk->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_shape_marker_modified), shape));
960         }
961     }
966 /* Shape section */
968 /**
969  * Calls any registered handlers for the set_shape action
970  */
971 void
972 sp_shape_set_shape (SPShape *shape)
974         g_return_if_fail (shape != NULL);
975         g_return_if_fail (SP_IS_SHAPE (shape));
977         if (SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape) {
978           SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape (shape);
979         }
982 /**
983  * Adds a curve to the shape.  If owner is specified, a reference
984  * will be made, otherwise the curve will be copied into the shape.
985  * Any existing curve in the shape will be unreferenced first.
986  * This routine also triggers a request to update the display.
987  */
988 void
989 sp_shape_set_curve (SPShape *shape, SPCurve *curve, unsigned int owner)
991         if (shape->curve) {
992                 shape->curve = shape->curve->unref();
993         }
994         if (curve) {
995                 if (owner) {
996                         shape->curve = curve->ref();
997                 } else {
998                         shape->curve = curve->copy();
999                 }
1000         }
1001         SP_OBJECT(shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1004 /**
1005  * Return duplicate of curve (if any exists) or NULL if there is no curve
1006  */
1007 SPCurve *
1008 sp_shape_get_curve (SPShape *shape)
1010         if (shape->curve) {
1011                 return shape->curve->copy();
1012         }
1013         return NULL;
1016 /**
1017  * Same as sp_shape_set_curve but without updating the display
1018  */
1019 void
1020 sp_shape_set_curve_insync (SPShape *shape, SPCurve *curve, unsigned int owner)
1022         if (shape->curve) {
1023                 shape->curve = shape->curve->unref();
1024         }
1025         if (curve) {
1026                 if (owner) {
1027                         shape->curve = curve->ref();
1028                 } else {
1029                         shape->curve = curve->copy();
1030                 }
1031         }
1034 /**
1035  * Return all nodes in a path that are to be considered for snapping
1036  */
1037 static void sp_shape_snappoints(SPItem const *item, SnapPointsIter p, Inkscape::SnapPreferences const *snapprefs)
1039     g_assert(item != NULL);
1040     g_assert(SP_IS_SHAPE(item));
1042     SPShape const *shape = SP_SHAPE(item);
1043     if (shape->curve == NULL) {
1044         return;
1045     }
1047     Geom::PathVector const &pathv = shape->curve->get_pathvector();
1048     if (pathv.empty())
1049         return;
1051     Geom::Matrix const i2d (sp_item_i2d_affine (item));
1053     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
1054         *p = from_2geom(path_it->initialPoint() * i2d);
1056         Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
1057         Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
1058         while (curve_it2 != path_it->end_closed())
1059         {
1060             /* Test whether to add the node between curve_it1 and curve_it2.
1061              * Loop to end_closed (so always including closing segment), the last node to be added
1062              * is the node between the closing segment and the segment before that one. Regardless
1063              * of the path being closed. If the path is closed, the final point was already added by
1064              * adding the initial point. */
1066             Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2);
1068             // Depending on the snapping preferences, either add only cusp nodes, or add add both cusp and smooths nodes
1069             if (snapprefs->getSnapSmoothNodes() || nodetype == Geom::NODE_NONE || nodetype == Geom::NODE_CUSP) {
1070                 *p = from_2geom(curve_it1->finalPoint() * i2d);
1071             }
1072             
1073             ++curve_it1;
1074             ++curve_it2;
1075         }
1076     }
1079 /*
1080   Local Variables:
1081   mode:c++
1082   c-file-style:"stroustrup"
1083   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1084   indent-tabs-mode:nil
1085   fill-column:99
1086   End:
1087 */
1088 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :