Code

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 "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     }
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     for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
255         sp_shape_set_marker (object, i, object->style->marker[i].value);
256           }
258     if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
259         SPStyle *style;
260         style = SP_OBJECT_STYLE (object);
261         if (style->stroke_width.unit == SP_CSS_UNIT_PERCENT) {
262             SPItemCtx *ictx = (SPItemCtx *) ctx;
263             double const aw = 1.0 / NR::expansion(ictx->i2vp);
264             style->stroke_width.computed = style->stroke_width.value * aw;
265             for (SPItemView *v = ((SPItem *) (shape))->display; v != NULL; v = v->next) {
266                 nr_arena_shape_set_style ((NRArenaShape *) v->arenaitem, style);
267             }
268         }
269     }
271     if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) {
272         /* This is suboptimal, because changing parent style schedules recalculation */
273         /* But on the other hand - how can we know that parent does not tie style and transform */
274         boost::optional<Geom::Rect> paintbox = SP_ITEM(object)->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
275         for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
276             NRArenaShape * const s = NR_ARENA_SHAPE(v->arenaitem);
277             if (flags & SP_OBJECT_MODIFIED_FLAG) {
278                 nr_arena_shape_set_path(s, shape->curve, (flags & SP_OBJECT_USER_MODIFIED_FLAG_B));
279             }
280             if (paintbox) {
281                 s->setPaintBox(*paintbox);
282             }
283         }
284     }
286     if (sp_shape_has_markers (shape)) {
287         /* Dimension marker views */
288         for (SPItemView *v = item->display; v != NULL; v = v->next) {
289             for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
290                 if (shape->marker[i]) {
291                     sp_marker_show_dimension ((SPMarker *) shape->marker[i],
292                                               NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i - SP_MARKER_LOC,
293                                               sp_shape_number_of_markers (shape, i));
294                 }
295             }
296         }
298         /* Update marker views */
299         for (SPItemView *v = item->display; v != NULL; v = v->next) {
300             sp_shape_update_marker_view (shape, v->arenaitem);
301         }
302     }
305 /**
306  * Calculate the transform required to get a marker's path object in the
307  * right place for particular path segment on a shape.
308  *
309  * \see sp_shape_marker_update_marker_view.
310  *
311  * From SVG spec:
312  * The axes of the temporary new user coordinate system are aligned according to the orient attribute on the 'marker'
313  * element and the slope of the curve at the given vertex. (Note: if there is a discontinuity at a vertex, the slope
314  * is the average of the slopes of the two segments of the curve that join at the given vertex. If a slope cannot be
315  * determined, the slope is assumed to be zero.)
316  *
317  * Reference: http://www.w3.org/TR/SVG11/painting.html#MarkerElement, the `orient' attribute.
318  * Reference for behaviour of zero-length segments:
319  * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
320  */
321 Geom::Matrix
322 sp_shape_marker_get_transform(Geom::Curve const & c1, Geom::Curve const & c2)
324     Geom::Point p = c1.pointAt(1);
325     Geom::Curve * c1_reverse = c1.reverse();
326     Geom::Point tang1 = - c1_reverse->unitTangentAt(0);
327     delete c1_reverse;
328     Geom::Point tang2 = c2.unitTangentAt(0);
330     double const angle1 = Geom::atan2(tang1);
331     double const angle2 = Geom::atan2(tang2);
333     double ret_angle;
334     ret_angle = .5 * (angle1 + angle2);
336     if ( fabs( angle2 - angle1 ) > M_PI ) {
337         /* ret_angle is in the middle of the larger of the two sectors between angle1 and
338          * angle2, so flip it by 180degrees to force it to the middle of the smaller sector.
339          *
340          * (Imagine a circle with rays drawn at angle1 and angle2 from the centre of the
341          * circle.  Those two rays divide the circle into two sectors.)
342          */
343         ret_angle += M_PI;
344     }
346     return Geom::Rotate(ret_angle) * Geom::Translate(p);
348 Geom::Matrix
349 sp_shape_marker_get_transform_at_start(Geom::Curve const & c)
351     Geom::Point p = c.pointAt(0);
352     Geom::Matrix ret = Geom::Translate(p);
354     if ( !c.isDegenerate() ) {
355         Geom::Point tang = c.unitTangentAt(0);
356         double const angle = Geom::atan2(tang);
357         ret = Geom::Rotate(angle) * Geom::Translate(p);
358     } else {
359         /* FIXME: the svg spec says to search for a better alternative than zero angle directionality:
360          * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes */
361     }
363     return ret;
365 Geom::Matrix
366 sp_shape_marker_get_transform_at_end(Geom::Curve const & c)
368     Geom::Point p = c.pointAt(1);
369     Geom::Matrix ret = Geom::Translate(p);
371     if ( !c.isDegenerate() ) {
372         Geom::Curve * c_reverse = c.reverse();
373         Geom::Point tang = - c_reverse->unitTangentAt(0);
374         delete c_reverse;
375         double const angle = Geom::atan2(tang);
376         ret = Geom::Rotate(angle) * Geom::Translate(p);
377     } else {
378         /* FIXME: the svg spec says to search for a better alternative than zero angle directionality:
379          * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes */
380     }
382     return ret;
385 /**
386  * Updates the instances (views) of a given marker in a shape.
387  * Marker views have to be scaled already.  The transformation
388  * is retrieved and then shown by calling sp_marker_show_instance.
389  *
390  * TODO: correctly handle the 'marker' attribute.
391  * "Using the marker property from a style sheet is equivalent to using all three (start, mid, end)."
392  * See painting-marker-03-f.svg in SVG 1.1 Full test suite.
393  */
394 static void
395 sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai)
397     SPStyle *style = ((SPObject *) shape)->style;
399     // position arguments to sp_marker_show_instance, basically counts the amount of markers.
400     int start_pos = 0;
401     int mid_pos = 0;
402     int end_pos = 0;
404     Geom::PathVector const & pathv = shape->curve->get_pathvector();
405     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
406         if ( shape->marker[SP_MARKER_LOC_START] ) {
407             Geom::Matrix const m (sp_shape_marker_get_transform_at_start(path_it->front()));
408             sp_marker_show_instance ((SPMarker* ) shape->marker[SP_MARKER_LOC_START], ai,
409                                      NR_ARENA_ITEM_GET_KEY(ai) + SP_MARKER_LOC_START, start_pos, m,
410                                      style->stroke_width.computed);
411              start_pos++;
412         }
414         if ( shape->marker[SP_MARKER_LOC_MID] && (path_it->size_default() > 1) ) {
415             Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
416             Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
417             while (curve_it2 != path_it->end_default())
418             {
419                 /* Put marker between curve_it1 and curve_it2.
420                  * Loop to end_default (so including closing segment), because when a path is closed,
421                  * there should be a midpoint marker between last segment and closing straight line segment
422                  */
423                 Geom::Matrix const m (sp_shape_marker_get_transform(*curve_it1, *curve_it2));
424                 sp_marker_show_instance ((SPMarker* ) shape->marker[SP_MARKER_LOC_MID], ai,
425                                          NR_ARENA_ITEM_GET_KEY(ai) + SP_MARKER_LOC_MID, mid_pos, m,
426                                          style->stroke_width.computed);
427                 mid_pos++;
429                 ++curve_it1;
430                 ++curve_it2;
431             }
432         }
434         if ( shape->marker[SP_MARKER_LOC_END] ) {
435             /* Get reference to last curve in the path.
436              * For moveto-only path, this returns the "closing line segment". */
437             unsigned int index = path_it->size_default();
438             if (index > 0) {
439                 index--;
440             }
441             Geom::Curve const &lastcurve = (*path_it)[index];
443             Geom::Matrix const m = sp_shape_marker_get_transform_at_end(lastcurve);
444             sp_marker_show_instance ((SPMarker* ) shape->marker[SP_MARKER_LOC_END], ai,
445                                      NR_ARENA_ITEM_GET_KEY(ai) + SP_MARKER_LOC_END, end_pos, m,
446                                      style->stroke_width.computed);
447             end_pos++;
448         }
449     }
452 /**
453  * Sets modified flag for all sub-item views.
454  */
455 static void
456 sp_shape_modified (SPObject *object, unsigned int flags)
458         SPShape *shape = SP_SHAPE (object);
460         if (((SPObjectClass *) (parent_class))->modified) {
461           (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
462         }
464         if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
465                 for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
466                         nr_arena_shape_set_style (NR_ARENA_SHAPE (v->arenaitem), object->style);
467                 }
468         }
471 /**
472  * Calculates the bounding box for item, storing it into bbox.
473  * This also includes the bounding boxes of any markers included in the shape.
474  */
475 static void sp_shape_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags)
477     SPShape const *shape = SP_SHAPE (item);
478     if (shape->curve) {
480         NRRect  cbbox;
482         Geom::Rect geombbox = bounds_exact_transformed(shape->curve->get_pathvector(), transform);
483         cbbox.x0 = geombbox[0][0];
484         cbbox.y0 = geombbox[1][0];
485         cbbox.x1 = geombbox[0][1];
486         cbbox.y1 = geombbox[1][1];
488         if ((SPItem::BBoxType) flags != SPItem::GEOMETRIC_BBOX) {
489             
490             SPStyle* style=SP_OBJECT_STYLE (item);
491             if (!style->stroke.isNone()) {
492                 double const scale = transform.descrim();
493                 if ( fabs(style->stroke_width.computed * scale) > 0.01 ) { // sinon c'est 0=oon veut pas de bord
494                     double const width = MAX(0.125, style->stroke_width.computed * scale);
495                     if ( fabs(cbbox.x1-cbbox.x0) > -0.00001 && fabs(cbbox.y1-cbbox.y0) > -0.00001 ) {
496                         cbbox.x0-=0.5*width;
497                         cbbox.x1+=0.5*width;
498                         cbbox.y0-=0.5*width;
499                         cbbox.y1+=0.5*width;
500                     }
501                 }
502             }
504             // Union with bboxes of the markers, if any
505             if (sp_shape_has_markers (shape)) {
506                 /* TODO: make code prettier: lots of variables can be taken out of the loop! */
507                 Geom::PathVector const & pathv = shape->curve->get_pathvector();
508                 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
509                     if ( shape->marker[SP_MARKER_LOC_START] ) {
510                         SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_START]);
511                         SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_START]));
513                         Geom::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front()));
515                         if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
516                             tr = Geom::Scale(style->stroke_width.computed) * tr;
517                         }
519                         // total marker transform
520                         tr = marker_item->transform * marker->c2p * tr * transform;
522                         // get bbox of the marker with that transform
523                         NRRect marker_bbox;
524                         sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true);
525                         // union it with the shape bbox
526                         nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
527                     }
529                     if ( shape->marker[SP_MARKER_LOC_MID] && (path_it->size_default() > 1) ) {
530                         Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
531                         Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
532                         while (curve_it2 != path_it->end_default())
533                         {
534                             /* Put marker between curve_it1 and curve_it2.
535                              * Loop to end_default (so including closing segment), because when a path is closed,
536                              * there should be a midpoint marker between last segment and closing straight line segment */
538                             SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_MID]);
539                             SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_MID]));
541                             Geom::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
543                             if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
544                                 tr = Geom::Scale(style->stroke_width.computed) * tr;
545                             }
547                             // total marker transform
548                             tr = marker_item->transform * marker->c2p * tr * transform;
550                             // get bbox of the marker with that transform
551                             NRRect marker_bbox;
552                             sp_item_invoke_bbox (marker_item, &marker_bbox, from_2geom(tr), true);
553                             // union it with the shape bbox
554                             nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
556                             ++curve_it1;
557                             ++curve_it2;
558                         }
559                     }
561                     if ( shape->marker[SP_MARKER_LOC_END] ) {
562                         SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_END]);
563                         SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_END]));
565                         /* Get reference to last curve in the path.
566                          * For moveto-only path, this returns the "closing line segment". */
567                         unsigned int index = path_it->size_default();
568                         if (index > 0) {
569                             index--;
570                         }
571                         Geom::Curve const &lastcurve = (*path_it)[index];
573                         Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
575                         if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
576                             tr = Geom::Scale(style->stroke_width.computed) * tr;
577                         }
579                         // total marker transform
580                         tr = marker_item->transform * marker->c2p * tr * transform;
582                         // get bbox of the marker with that transform
583                         NRRect marker_bbox;
584                         sp_item_invoke_bbox (marker_item, &marker_bbox, tr, true);
585                         // union it with the shape bbox
586                         nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
587                     }
588                 }
589             }
590         }
592         // copy our bbox to the variable we're given
593         *bbox = cbbox;
594     }
597 /**
598  * Prepares shape for printing.  Handles printing of comments for printing
599  * debugging, sizes the item to fit into the document width/height,
600  * applies print fill/stroke, sets transforms for markers, and adds
601  * comment labels.
602  */
603 void
604 sp_shape_print (SPItem *item, SPPrintContext *ctx)
606         NRRect pbox, dbox, bbox;
608         SPShape *shape = SP_SHAPE(item);
610         if (!shape->curve) return;
612         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
613         gint add_comments = prefs->getBool("/printing/debug/add-label-comments");
614         if (add_comments) {
615             gchar * comment = g_strdup_printf("begin '%s'",
616                                               SP_OBJECT(item)->defaultLabel());
617             sp_print_comment(ctx, comment);
618             g_free(comment);
619         }
621     /* fixme: Think (Lauris) */
622     sp_item_invoke_bbox(item, &pbox, NR::identity(), TRUE);
623     dbox.x0 = 0.0;
624     dbox.y0 = 0.0;
625     dbox.x1 = sp_document_width (SP_OBJECT_DOCUMENT (item));
626     dbox.y1 = sp_document_height (SP_OBJECT_DOCUMENT (item));
627     sp_item_bbox_desktop (item, &bbox);
628     Geom::Matrix const i2d(sp_item_i2d_affine(item));
630         SPStyle* style = SP_OBJECT_STYLE (item);
632     if (!style->fill.isNone()) {
633         sp_print_fill (ctx, shape->curve->get_pathvector(), &i2d, style, &pbox, &dbox, &bbox);
634     }
636     if (!style->stroke.isNone()) {
637         sp_print_stroke (ctx, shape->curve->get_pathvector(), &i2d, style, &pbox, &dbox, &bbox);
638     }
640     /* TODO: make code prettier: lots of variables can be taken out of the loop! */
641     Geom::PathVector const & pathv = shape->curve->get_pathvector();
642     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
643         if ( shape->marker[SP_MARKER_LOC_START] ) {
644             SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_START]);
645             SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_START]));
647             Geom::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front()));
649             if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
650                 tr = Geom::Scale(style->stroke_width.computed) * tr;
651             }
653             tr = marker_item->transform * marker->c2p * tr;
655             NR::Matrix old_tr = marker_item->transform;
656             marker_item->transform = tr;
657             sp_item_invoke_print (marker_item, ctx);
658             marker_item->transform = old_tr;
659         }
661         if ( shape->marker[SP_MARKER_LOC_MID] && (path_it->size_default() > 1) ) {
662             Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
663             Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
664             while (curve_it2 != path_it->end_default())
665             {
666                 /* Put marker between curve_it1 and curve_it2.
667                  * Loop to end_default (so including closing segment), because when a path is closed,
668                  * there should be a midpoint marker between last segment and closing straight line segment */
670                 SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_MID]);
671                 SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_MID]));
673                 Geom::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
675                 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
676                     tr = Geom::Scale(style->stroke_width.computed) * tr;
677                 }
679                 tr = marker_item->transform * marker->c2p * tr;
681                 NR::Matrix old_tr = marker_item->transform;
682                 marker_item->transform = tr;
683                 sp_item_invoke_print (marker_item, ctx);
684                 marker_item->transform = old_tr;
686                 ++curve_it1;
687                 ++curve_it2;
688             }
689         }
691         if ( shape->marker[SP_MARKER_LOC_END] ) {
692             SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_END]);
693             SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_END]));
695             /* Get reference to last curve in the path.
696              * For moveto-only path, this returns the "closing line segment". */
697             unsigned int index = path_it->size_default();
698             if (index > 0) {
699                 index--;
700             }
701             Geom::Curve const &lastcurve = (*path_it)[index];
703             Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
705             if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
706                 tr = Geom::Scale(style->stroke_width.computed) * tr;
707             }
709             tr = marker_item->transform * marker->c2p * tr;
711             NR::Matrix old_tr = marker_item->transform;
712             marker_item->transform = tr;
713             sp_item_invoke_print (marker_item, ctx);
714             marker_item->transform = old_tr;
715         }
716     }
718         if (add_comments) {
719             gchar * comment = g_strdup_printf("end '%s'",
720                                               SP_OBJECT(item)->defaultLabel());
721             sp_print_comment(ctx, comment);
722             g_free(comment);
723         }
726 /**
727  * Sets style, path, and paintbox.  Updates marker views, including dimensions.
728  */
729 static NRArenaItem *
730 sp_shape_show (SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int /*flags*/)
732     SPObject *object = SP_OBJECT(item);
733     SPShape *shape = SP_SHAPE(item);
735     NRArenaItem *arenaitem = NRArenaShape::create(arena);
736     NRArenaShape * const s = NR_ARENA_SHAPE(arenaitem);
737     nr_arena_shape_set_style(s, object->style);
738     nr_arena_shape_set_path(s, shape->curve, false);
739     boost::optional<Geom::Rect> paintbox = item->getBounds(Geom::identity());
740     if (paintbox) {
741         s->setPaintBox(*paintbox);
742     }
744     /* This stanza checks that an object's marker style agrees with
745      * the marker objects it has allocated.  sp_shape_set_marker ensures
746      * that the appropriate marker objects are present (or absent) to
747      * match the style.
748      */
749     for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
750         sp_shape_set_marker (object, i, object->style->marker[i].value);
751           }
753     if (sp_shape_has_markers (shape)) {
755         /* provide key and dimension the marker views */
756         if (!arenaitem->key) {
757             NR_ARENA_ITEM_SET_KEY (arenaitem, sp_item_display_key_new (SP_MARKER_LOC_QTY));
758         }
760         for (int i = 0; i < SP_MARKER_LOC_QTY; i++) {
761             if (shape->marker[i]) {
762                 sp_marker_show_dimension ((SPMarker *) shape->marker[i],
763                                           NR_ARENA_ITEM_GET_KEY (arenaitem) + i - SP_MARKER_LOC,
764                                           sp_shape_number_of_markers (shape, i));
765             }
766         }
768         /* Update marker views */
769         sp_shape_update_marker_view (shape, arenaitem);
770     }
772     return arenaitem;
775 /**
776  * Hides/removes marker views from the shape.
777  */
778 static void
779 sp_shape_hide (SPItem *item, unsigned int key)
781         SPShape *shape;
782         SPItemView *v;
783         int i;
785         shape = (SPShape *) item;
787         for (i=0; i<SP_MARKER_LOC_QTY; i++) {
788           if (shape->marker[i]) {
789             for (v = item->display; v != NULL; v = v->next) {
790                 if (key == v->key) {
791               sp_marker_hide ((SPMarker *) shape->marker[i],
792                                     NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
793                 }
794             }
795           }
796         }
798         if (((SPItemClass *) parent_class)->hide) {
799           ((SPItemClass *) parent_class)->hide (item, key);
800         }
803 /**
804 * \param shape Shape.
805 * \return TRUE if the shape has any markers, or FALSE if not.
806 */
807 int
808 sp_shape_has_markers (SPShape const *shape)
810     /* Note, we're ignoring 'marker' settings, which technically should apply for
811        all three settings.  This should be fixed later such that if 'marker' is
812        specified, then all three should appear. */
814     return (
815         shape->curve &&
816         (shape->marker[SP_MARKER_LOC_START] ||
817          shape->marker[SP_MARKER_LOC_MID] ||
818          shape->marker[SP_MARKER_LOC_END])
819         );
823 /**
824 * \param shape Shape.
825 * \param type Marker type (e.g. SP_MARKER_LOC_START)
826 * \return Number of markers that the shape has of this type.
827 */
828 int
829 sp_shape_number_of_markers (SPShape *shape, int type)
831     Geom::PathVector const & pathv = shape->curve->get_pathvector();
833     switch(type) {
834         case SP_MARKER_LOC_START:
835             return shape->marker[SP_MARKER_LOC_START] ? pathv.size() : 0;
837         case SP_MARKER_LOC_MID:
838         {
839             if ( shape->marker[SP_MARKER_LOC_MID] ) {
840             guint n = 0;
841                 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
842                     n += path_it->size();
843                     n += path_it->closed() ? 1 : 0;
844                 }
845                 return n;
846             } else {
847                 return 0;
848             }
849         }
851         case SP_MARKER_LOC_END:
852         {
853             if ( shape->marker[SP_MARKER_LOC_END] ) {
854                 guint n = 0;
855                 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
856                     if (!path_it->empty()) {
857                         n++;
858                     }
859                 }
860                 return n;
861             } else {
862                 return 0;
863             }
864         }
866         default:
867             return 0;
868     }
871 /**
872  * Checks if the given marker is used in the shape, and if so, it
873  * releases it by calling sp_marker_hide.  Also detaches signals
874  * and unrefs the marker from the shape.
875  */
876 static void
877 sp_shape_marker_release (SPObject *marker, SPShape *shape)
879         SPItem *item;
880         int i;
882         item = (SPItem *) shape;
884         for (i = SP_MARKER_LOC_START; i < SP_MARKER_LOC_QTY; i++) {
885           if (marker == shape->marker[i]) {
886             SPItemView *v;
887             /* Hide marker */
888             for (v = item->display; v != NULL; v = v->next) {
889               sp_marker_hide ((SPMarker *) (shape->marker[i]), NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
890               /* fixme: Do we need explicit remove here? (Lauris) */
891               /* nr_arena_item_set_mask (v->arenaitem, NULL); */
892             }
893             /* Detach marker */
894             sp_signal_disconnect_by_data (shape->marker[i], item);
895             shape->marker[i] = sp_object_hunref (shape->marker[i], item);
896           }
897         }
900 /**
901  * No-op.  Exists for handling 'modified' messages
902  */
903 static void
904 sp_shape_marker_modified (SPObject */*marker*/, guint /*flags*/, SPItem */*item*/)
906         /* I think mask does update automagically */
907         /* g_warning ("Item %s mask %s modified", SP_OBJECT_ID (item), SP_OBJECT_ID (mask)); */
910 /**
911  * Adds a new marker to shape object at the location indicated by key.  value 
912  * must be a valid URI reference resolvable from the shape object (i.e., present
913  * in the document <defs>).  If the shape object already has a marker
914  * registered at the given position, it is removed first.  Then the
915  * new marker is hrefed and its signals connected.
916  */
917 void
918 sp_shape_set_marker (SPObject *object, unsigned int key, const gchar *value)
920     SPItem *item = (SPItem *) object;
921     SPShape *shape = (SPShape *) object;
923     if (key < SP_MARKER_LOC_START || key > SP_MARKER_LOC_END) {
924         return;
925     }
927     SPObject *mrk = sp_css_uri_reference_resolve (SP_OBJECT_DOCUMENT (object), value);
928     if (mrk != shape->marker[key]) {
929         if (shape->marker[key]) {
930             SPItemView *v;
932             /* Detach marker */
933             shape->release_connect[key].disconnect();
934             shape->modified_connect[key].disconnect();
936             /* Hide marker */
937             for (v = item->display; v != NULL; v = v->next) {
938                 sp_marker_hide ((SPMarker *) (shape->marker[key]),
939                                 NR_ARENA_ITEM_GET_KEY (v->arenaitem) + key);
940                 /* fixme: Do we need explicit remove here? (Lauris) */
941                 /* nr_arena_item_set_mask (v->arenaitem, NULL); */
942             }
944             /* Unref marker */
945             shape->marker[key] = sp_object_hunref (shape->marker[key], object);
946         }
947         if (SP_IS_MARKER (mrk)) {
948             shape->marker[key] = sp_object_href (mrk, object);
949             shape->release_connect[key] = mrk->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_shape_marker_release), shape));
950             shape->modified_connect[key] = mrk->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_shape_marker_modified), shape));
951         }
952     }
957 /* Shape section */
959 /**
960  * Calls any registered handlers for the set_shape action
961  */
962 void
963 sp_shape_set_shape (SPShape *shape)
965         g_return_if_fail (shape != NULL);
966         g_return_if_fail (SP_IS_SHAPE (shape));
968         if (SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape) {
969           SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape (shape);
970         }
973 /**
974  * Adds a curve to the shape.  If owner is specified, a reference
975  * will be made, otherwise the curve will be copied into the shape.
976  * Any existing curve in the shape will be unreferenced first.
977  * This routine also triggers a request to update the display.
978  */
979 void
980 sp_shape_set_curve (SPShape *shape, SPCurve *curve, unsigned int owner)
982         if (shape->curve) {
983                 shape->curve = shape->curve->unref();
984         }
985         if (curve) {
986                 if (owner) {
987                         shape->curve = curve->ref();
988                 } else {
989                         shape->curve = curve->copy();
990                 }
991         }
992         SP_OBJECT(shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
995 /**
996  * Return duplicate of curve (if any exists) or NULL if there is no curve
997  */
998 SPCurve *
999 sp_shape_get_curve (SPShape *shape)
1001         if (shape->curve) {
1002                 return shape->curve->copy();
1003         }
1004         return NULL;
1007 /**
1008  * Same as sp_shape_set_curve but without updating the display
1009  */
1010 void
1011 sp_shape_set_curve_insync (SPShape *shape, SPCurve *curve, unsigned int owner)
1013         if (shape->curve) {
1014                 shape->curve = shape->curve->unref();
1015         }
1016         if (curve) {
1017                 if (owner) {
1018                         shape->curve = curve->ref();
1019                 } else {
1020                         shape->curve = curve->copy();
1021                 }
1022         }
1025 /**
1026  * Return all nodes in a path that are to be considered for snapping
1027  */
1028 static void sp_shape_snappoints(SPItem const *item, SnapPointsIter p, Inkscape::SnapPreferences const *snapprefs)
1030     g_assert(item != NULL);
1031     g_assert(SP_IS_SHAPE(item));
1033     SPShape const *shape = SP_SHAPE(item);
1034     if (shape->curve == NULL) {
1035         return;
1036     }
1038     Geom::PathVector const &pathv = shape->curve->get_pathvector();
1039     if (pathv.empty())
1040         return;
1042     Geom::Matrix const i2d (sp_item_i2d_affine (item));
1044     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
1045         *p = from_2geom(path_it->initialPoint() * i2d);
1047         Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
1048         Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
1049         while (curve_it2 != path_it->end_closed())
1050         {
1051             /* Test whether to add the node between curve_it1 and curve_it2.
1052              * Loop to end_closed (so always including closing segment), the last node to be added
1053              * is the node between the closing segment and the segment before that one. Regardless
1054              * of the path being closed. If the path is closed, the final point was already added by
1055              * adding the initial point. */
1057             Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2);
1059             // Depending on the snapping preferences, either add only cusp nodes, or add add both cusp and smooths nodes
1060             if (snapprefs->getSnapSmoothNodes() || nodetype == Geom::NODE_NONE || nodetype == Geom::NODE_CUSP) {
1061                 *p = from_2geom(curve_it1->finalPoint() * i2d);
1062             }
1063             
1064             ++curve_it1;
1065             ++curve_it2;
1066         }
1067     }
1070 /*
1071   Local Variables:
1072   mode:c++
1073   c-file-style:"stroustrup"
1074   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1075   indent-tabs-mode:nil
1076   fill-column:99
1077   End:
1078 */
1079 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :