Code

Merge 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             if (!v->arenaitem->key) {
290                 NR_ARENA_ITEM_SET_KEY (v->arenaitem, sp_item_display_key_new (SP_MARKER_LOC_QTY));
291             }
292             for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
293                 if (shape->marker[i]) {
294                     sp_marker_show_dimension ((SPMarker *) shape->marker[i],
295                                               NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i - SP_MARKER_LOC,
296                                               sp_shape_number_of_markers (shape, i));
297                 }
298             }
299         }
301         /* Update marker views */
302         for (SPItemView *v = item->display; v != NULL; v = v->next) {
303             sp_shape_update_marker_view (shape, v->arenaitem);
304         }
305     }
308 /**
309  * Calculate the transform required to get a marker's path object in the
310  * right place for particular path segment on a shape.
311  *
312  * \see sp_shape_marker_update_marker_view.
313  *
314  * From SVG spec:
315  * The axes of the temporary new user coordinate system are aligned according to the orient attribute on the 'marker'
316  * element and the slope of the curve at the given vertex. (Note: if there is a discontinuity at a vertex, the slope
317  * is the average of the slopes of the two segments of the curve that join at the given vertex. If a slope cannot be
318  * determined, the slope is assumed to be zero.)
319  *
320  * Reference: http://www.w3.org/TR/SVG11/painting.html#MarkerElement, the `orient' attribute.
321  * Reference for behaviour of zero-length segments:
322  * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
323  */
324 Geom::Matrix
325 sp_shape_marker_get_transform(Geom::Curve const & c1, Geom::Curve const & c2)
327     Geom::Point p = c1.pointAt(1);
328     Geom::Curve * c1_reverse = c1.reverse();
329     Geom::Point tang1 = - c1_reverse->unitTangentAt(0);
330     delete c1_reverse;
331     Geom::Point tang2 = c2.unitTangentAt(0);
333     double const angle1 = Geom::atan2(tang1);
334     double const angle2 = Geom::atan2(tang2);
336     double ret_angle;
337     ret_angle = .5 * (angle1 + angle2);
339     if ( fabs( angle2 - angle1 ) > M_PI ) {
340         /* ret_angle is in the middle of the larger of the two sectors between angle1 and
341          * angle2, so flip it by 180degrees to force it to the middle of the smaller sector.
342          *
343          * (Imagine a circle with rays drawn at angle1 and angle2 from the centre of the
344          * circle.  Those two rays divide the circle into two sectors.)
345          */
346         ret_angle += M_PI;
347     }
349     return Geom::Rotate(ret_angle) * Geom::Translate(p);
351 Geom::Matrix
352 sp_shape_marker_get_transform_at_start(Geom::Curve const & c)
354     Geom::Point p = c.pointAt(0);
355     Geom::Matrix ret = Geom::Translate(p);
357     if ( !c.isDegenerate() ) {
358         Geom::Point tang = c.unitTangentAt(0);
359         double const angle = Geom::atan2(tang);
360         ret = Geom::Rotate(angle) * Geom::Translate(p);
361     } else {
362         /* FIXME: the svg spec says to search for a better alternative than zero angle directionality:
363          * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes */
364     }
366     return ret;
368 Geom::Matrix
369 sp_shape_marker_get_transform_at_end(Geom::Curve const & c)
371     Geom::Point p = c.pointAt(1);
372     Geom::Matrix ret = Geom::Translate(p);
374     if ( !c.isDegenerate() ) {
375         Geom::Curve * c_reverse = c.reverse();
376         Geom::Point tang = - c_reverse->unitTangentAt(0);
377         delete c_reverse;
378         double const angle = Geom::atan2(tang);
379         ret = Geom::Rotate(angle) * Geom::Translate(p);
380     } else {
381         /* FIXME: the svg spec says to search for a better alternative than zero angle directionality:
382          * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes */
383     }
385     return ret;
388 /**
389  * Updates the instances (views) of a given marker in a shape.
390  * Marker views have to be scaled already.  The transformation
391  * is retrieved and then shown by calling sp_marker_show_instance.
392  *
393  * TODO: correctly handle the 'marker' attribute.
394  * "Using the marker property from a style sheet is equivalent to using all three (start, mid, end)."
395  * See painting-marker-03-f.svg in SVG 1.1 Full test suite.
396  */
397 static void
398 sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai)
400     SPStyle *style = ((SPObject *) shape)->style;
402     // position arguments to sp_marker_show_instance, basically counts the amount of markers.
403     int start_pos = 0;
404     int mid_pos = 0;
405     int end_pos = 0;
407     Geom::PathVector const & pathv = shape->curve->get_pathvector();
408     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
409         if ( shape->marker[SP_MARKER_LOC_START] ) {
410             Geom::Matrix const m (sp_shape_marker_get_transform_at_start(path_it->front()));
411             sp_marker_show_instance ((SPMarker* ) shape->marker[SP_MARKER_LOC_START], ai,
412                                      NR_ARENA_ITEM_GET_KEY(ai) + SP_MARKER_LOC_START, start_pos, m,
413                                      style->stroke_width.computed);
414              start_pos++;
415         }
417         if ( shape->marker[SP_MARKER_LOC_MID] && (path_it->size_default() > 1) ) {
418             Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
419             Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
420             while (curve_it2 != path_it->end_default())
421             {
422                 /* Put marker between curve_it1 and curve_it2.
423                  * Loop to end_default (so including closing segment), because when a path is closed,
424                  * there should be a midpoint marker between last segment and closing straight line segment
425                  */
426                 Geom::Matrix const m (sp_shape_marker_get_transform(*curve_it1, *curve_it2));
427                 sp_marker_show_instance ((SPMarker* ) shape->marker[SP_MARKER_LOC_MID], ai,
428                                          NR_ARENA_ITEM_GET_KEY(ai) + SP_MARKER_LOC_MID, mid_pos, m,
429                                          style->stroke_width.computed);
430                 mid_pos++;
432                 ++curve_it1;
433                 ++curve_it2;
434             }
435         }
437         if ( shape->marker[SP_MARKER_LOC_END] ) {
438             /* Get reference to last curve in the path.
439              * For moveto-only path, this returns the "closing line segment". */
440             unsigned int index = path_it->size_default();
441             if (index > 0) {
442                 index--;
443             }
444             Geom::Curve const &lastcurve = (*path_it)[index];
446             Geom::Matrix const m = sp_shape_marker_get_transform_at_end(lastcurve);
447             sp_marker_show_instance ((SPMarker* ) shape->marker[SP_MARKER_LOC_END], ai,
448                                      NR_ARENA_ITEM_GET_KEY(ai) + SP_MARKER_LOC_END, end_pos, m,
449                                      style->stroke_width.computed);
450             end_pos++;
451         }
452     }
455 /**
456  * Sets modified flag for all sub-item views.
457  */
458 static void
459 sp_shape_modified (SPObject *object, unsigned int flags)
461         SPShape *shape = SP_SHAPE (object);
463         if (((SPObjectClass *) (parent_class))->modified) {
464           (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
465         }
467         if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
468                 for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
469                         nr_arena_shape_set_style (NR_ARENA_SHAPE (v->arenaitem), object->style);
470                 }
471         }
474 /**
475  * Calculates the bounding box for item, storing it into bbox.
476  * This also includes the bounding boxes of any markers included in the shape.
477  */
478 static void sp_shape_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags)
480     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         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
616         gint add_comments = prefs->getBool("/printing/debug/add-label-comments");
617         if (add_comments) {
618             gchar * comment = g_strdup_printf("begin '%s'",
619                                               SP_OBJECT(item)->defaultLabel());
620             sp_print_comment(ctx, comment);
621             g_free(comment);
622         }
624     /* fixme: Think (Lauris) */
625     sp_item_invoke_bbox(item, &pbox, NR::identity(), TRUE);
626     dbox.x0 = 0.0;
627     dbox.y0 = 0.0;
628     dbox.x1 = sp_document_width (SP_OBJECT_DOCUMENT (item));
629     dbox.y1 = sp_document_height (SP_OBJECT_DOCUMENT (item));
630     sp_item_bbox_desktop (item, &bbox);
631     Geom::Matrix const i2d(sp_item_i2d_affine(item));
633         SPStyle* style = SP_OBJECT_STYLE (item);
635     if (!style->fill.isNone()) {
636         sp_print_fill (ctx, shape->curve->get_pathvector(), &i2d, style, &pbox, &dbox, &bbox);
637     }
639     if (!style->stroke.isNone()) {
640         sp_print_stroke (ctx, shape->curve->get_pathvector(), &i2d, style, &pbox, &dbox, &bbox);
641     }
643     /* TODO: make code prettier: lots of variables can be taken out of the loop! */
644     Geom::PathVector const & pathv = shape->curve->get_pathvector();
645     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
646         if ( shape->marker[SP_MARKER_LOC_START] ) {
647             SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_START]);
648             SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_START]));
650             Geom::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front()));
652             if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
653                 tr = Geom::Scale(style->stroke_width.computed) * tr;
654             }
656             tr = marker_item->transform * marker->c2p * tr;
658             NR::Matrix old_tr = marker_item->transform;
659             marker_item->transform = tr;
660             sp_item_invoke_print (marker_item, ctx);
661             marker_item->transform = old_tr;
662         }
664         if ( shape->marker[SP_MARKER_LOC_MID] && (path_it->size_default() > 1) ) {
665             Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
666             Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
667             while (curve_it2 != path_it->end_default())
668             {
669                 /* Put marker between curve_it1 and curve_it2.
670                  * Loop to end_default (so including closing segment), because when a path is closed,
671                  * there should be a midpoint marker between last segment and closing straight line segment */
673                 SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_MID]);
674                 SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_MID]));
676                 Geom::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
678                 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
679                     tr = Geom::Scale(style->stroke_width.computed) * tr;
680                 }
682                 tr = marker_item->transform * marker->c2p * tr;
684                 NR::Matrix old_tr = marker_item->transform;
685                 marker_item->transform = tr;
686                 sp_item_invoke_print (marker_item, ctx);
687                 marker_item->transform = old_tr;
689                 ++curve_it1;
690                 ++curve_it2;
691             }
692         }
694         if ( shape->marker[SP_MARKER_LOC_END] ) {
695             SPMarker* marker = SP_MARKER (shape->marker[SP_MARKER_LOC_END]);
696             SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[SP_MARKER_LOC_END]));
698             /* Get reference to last curve in the path.
699              * For moveto-only path, this returns the "closing line segment". */
700             unsigned int index = path_it->size_default();
701             if (index > 0) {
702                 index--;
703             }
704             Geom::Curve const &lastcurve = (*path_it)[index];
706             Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
708             if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
709                 tr = Geom::Scale(style->stroke_width.computed) * tr;
710             }
712             tr = marker_item->transform * marker->c2p * tr;
714             NR::Matrix old_tr = marker_item->transform;
715             marker_item->transform = tr;
716             sp_item_invoke_print (marker_item, ctx);
717             marker_item->transform = old_tr;
718         }
719     }
721         if (add_comments) {
722             gchar * comment = g_strdup_printf("end '%s'",
723                                               SP_OBJECT(item)->defaultLabel());
724             sp_print_comment(ctx, comment);
725             g_free(comment);
726         }
729 /**
730  * Sets style, path, and paintbox.  Updates marker views, including dimensions.
731  */
732 static NRArenaItem *
733 sp_shape_show (SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int /*flags*/)
735     SPObject *object = SP_OBJECT(item);
736     SPShape *shape = SP_SHAPE(item);
738     NRArenaItem *arenaitem = NRArenaShape::create(arena);
739     NRArenaShape * const s = NR_ARENA_SHAPE(arenaitem);
740     nr_arena_shape_set_style(s, object->style);
741     nr_arena_shape_set_path(s, shape->curve, false);
742     boost::optional<Geom::Rect> paintbox = item->getBounds(Geom::identity());
743     if (paintbox) {
744         s->setPaintBox(*paintbox);
745     }
747     /* This stanza checks that an object's marker style agrees with
748      * the marker objects it has allocated.  sp_shape_set_marker ensures
749      * that the appropriate marker objects are present (or absent) to
750      * match the style.
751      */
752     for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
753         sp_shape_set_marker (object, i, object->style->marker[i].value);
754           }
756     if (sp_shape_has_markers (shape)) {
758         /* provide key and dimension the marker views */
759         if (!arenaitem->key) {
760             NR_ARENA_ITEM_SET_KEY (arenaitem, sp_item_display_key_new (SP_MARKER_LOC_QTY));
761         }
763         for (int i = 0; i < SP_MARKER_LOC_QTY; i++) {
764             if (shape->marker[i]) {
765                 sp_marker_show_dimension ((SPMarker *) shape->marker[i],
766                                           NR_ARENA_ITEM_GET_KEY (arenaitem) + i - SP_MARKER_LOC,
767                                           sp_shape_number_of_markers (shape, i));
768             }
769         }
771         /* Update marker views */
772         sp_shape_update_marker_view (shape, arenaitem);
773     }
775     return arenaitem;
778 /**
779  * Hides/removes marker views from the shape.
780  */
781 static void
782 sp_shape_hide (SPItem *item, unsigned int key)
784         SPShape *shape;
785         SPItemView *v;
786         int i;
788         shape = (SPShape *) item;
790         for (i=0; i<SP_MARKER_LOC_QTY; i++) {
791           if (shape->marker[i]) {
792             for (v = item->display; v != NULL; v = v->next) {
793                 if (key == v->key) {
794               sp_marker_hide ((SPMarker *) shape->marker[i],
795                                     NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
796                 }
797             }
798           }
799         }
801         if (((SPItemClass *) parent_class)->hide) {
802           ((SPItemClass *) parent_class)->hide (item, key);
803         }
806 /**
807 * \param shape Shape.
808 * \return TRUE if the shape has any markers, or FALSE if not.
809 */
810 int
811 sp_shape_has_markers (SPShape const *shape)
813     /* Note, we're ignoring 'marker' settings, which technically should apply for
814        all three settings.  This should be fixed later such that if 'marker' is
815        specified, then all three should appear. */
817     return (
818         shape->curve &&
819         (shape->marker[SP_MARKER_LOC_START] ||
820          shape->marker[SP_MARKER_LOC_MID] ||
821          shape->marker[SP_MARKER_LOC_END])
822         );
826 /**
827 * \param shape Shape.
828 * \param type Marker type (e.g. SP_MARKER_LOC_START)
829 * \return Number of markers that the shape has of this type.
830 */
831 int
832 sp_shape_number_of_markers (SPShape *shape, int type)
834     Geom::PathVector const & pathv = shape->curve->get_pathvector();
836     switch(type) {
837         case SP_MARKER_LOC_START:
838             return shape->marker[SP_MARKER_LOC_START] ? pathv.size() : 0;
840         case SP_MARKER_LOC_MID:
841         {
842             if ( shape->marker[SP_MARKER_LOC_MID] ) {
843             guint n = 0;
844                 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
845                     n += path_it->size();
846                     n += path_it->closed() ? 1 : 0;
847                 }
848                 return n;
849             } else {
850                 return 0;
851             }
852         }
854         case SP_MARKER_LOC_END:
855         {
856             if ( shape->marker[SP_MARKER_LOC_END] ) {
857                 guint n = 0;
858                 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
859                     if (!path_it->empty()) {
860                         n++;
861                     }
862                 }
863                 return n;
864             } else {
865                 return 0;
866             }
867         }
869         default:
870             return 0;
871     }
874 /**
875  * Checks if the given marker is used in the shape, and if so, it
876  * releases it by calling sp_marker_hide.  Also detaches signals
877  * and unrefs the marker from the shape.
878  */
879 static void
880 sp_shape_marker_release (SPObject *marker, SPShape *shape)
882         SPItem *item;
883         int i;
885         item = (SPItem *) shape;
887         for (i = SP_MARKER_LOC_START; i < SP_MARKER_LOC_QTY; i++) {
888           if (marker == shape->marker[i]) {
889             SPItemView *v;
890             /* Hide marker */
891             for (v = item->display; v != NULL; v = v->next) {
892               sp_marker_hide ((SPMarker *) (shape->marker[i]), NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
893               /* fixme: Do we need explicit remove here? (Lauris) */
894               /* nr_arena_item_set_mask (v->arenaitem, NULL); */
895             }
896             /* Detach marker */
897             sp_signal_disconnect_by_data (shape->marker[i], item);
898             shape->marker[i] = sp_object_hunref (shape->marker[i], item);
899           }
900         }
903 /**
904  * No-op.  Exists for handling 'modified' messages
905  */
906 static void
907 sp_shape_marker_modified (SPObject */*marker*/, guint /*flags*/, SPItem */*item*/)
909         /* I think mask does update automagically */
910         /* g_warning ("Item %s mask %s modified", SP_OBJECT_ID (item), SP_OBJECT_ID (mask)); */
913 /**
914  * Adds a new marker to shape object at the location indicated by key.  value 
915  * must be a valid URI reference resolvable from the shape object (i.e., present
916  * in the document <defs>).  If the shape object already has a marker
917  * registered at the given position, it is removed first.  Then the
918  * new marker is hrefed and its signals connected.
919  */
920 void
921 sp_shape_set_marker (SPObject *object, unsigned int key, const gchar *value)
923     SPItem *item = (SPItem *) object;
924     SPShape *shape = (SPShape *) object;
926     if (key < SP_MARKER_LOC_START || key > SP_MARKER_LOC_END) {
927         return;
928     }
930     SPObject *mrk = sp_css_uri_reference_resolve (SP_OBJECT_DOCUMENT (object), value);
931     if (mrk != shape->marker[key]) {
932         if (shape->marker[key]) {
933             SPItemView *v;
935             /* Detach marker */
936             shape->release_connect[key].disconnect();
937             shape->modified_connect[key].disconnect();
939             /* Hide marker */
940             for (v = item->display; v != NULL; v = v->next) {
941                 sp_marker_hide ((SPMarker *) (shape->marker[key]),
942                                 NR_ARENA_ITEM_GET_KEY (v->arenaitem) + key);
943                 /* fixme: Do we need explicit remove here? (Lauris) */
944                 /* nr_arena_item_set_mask (v->arenaitem, NULL); */
945             }
947             /* Unref marker */
948             shape->marker[key] = sp_object_hunref (shape->marker[key], object);
949         }
950         if (SP_IS_MARKER (mrk)) {
951             shape->marker[key] = sp_object_href (mrk, object);
952             shape->release_connect[key] = mrk->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_shape_marker_release), shape));
953             shape->modified_connect[key] = mrk->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_shape_marker_modified), shape));
954         }
955     }
960 /* Shape section */
962 /**
963  * Calls any registered handlers for the set_shape action
964  */
965 void
966 sp_shape_set_shape (SPShape *shape)
968         g_return_if_fail (shape != NULL);
969         g_return_if_fail (SP_IS_SHAPE (shape));
971         if (SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape) {
972           SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape (shape);
973         }
976 /**
977  * Adds a curve to the shape.  If owner is specified, a reference
978  * will be made, otherwise the curve will be copied into the shape.
979  * Any existing curve in the shape will be unreferenced first.
980  * This routine also triggers a request to update the display.
981  */
982 void
983 sp_shape_set_curve (SPShape *shape, SPCurve *curve, unsigned int owner)
985         if (shape->curve) {
986                 shape->curve = shape->curve->unref();
987         }
988         if (curve) {
989                 if (owner) {
990                         shape->curve = curve->ref();
991                 } else {
992                         shape->curve = curve->copy();
993                 }
994         }
995         SP_OBJECT(shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
998 /**
999  * Return duplicate of curve (if any exists) or NULL if there is no curve
1000  */
1001 SPCurve *
1002 sp_shape_get_curve (SPShape *shape)
1004         if (shape->curve) {
1005                 return shape->curve->copy();
1006         }
1007         return NULL;
1010 /**
1011  * Same as sp_shape_set_curve but without updating the display
1012  */
1013 void
1014 sp_shape_set_curve_insync (SPShape *shape, SPCurve *curve, unsigned int owner)
1016         if (shape->curve) {
1017                 shape->curve = shape->curve->unref();
1018         }
1019         if (curve) {
1020                 if (owner) {
1021                         shape->curve = curve->ref();
1022                 } else {
1023                         shape->curve = curve->copy();
1024                 }
1025         }
1028 /**
1029  * Return all nodes in a path that are to be considered for snapping
1030  */
1031 static void sp_shape_snappoints(SPItem const *item, SnapPointsIter p, Inkscape::SnapPreferences const *snapprefs)
1033     g_assert(item != NULL);
1034     g_assert(SP_IS_SHAPE(item));
1036     SPShape const *shape = SP_SHAPE(item);
1037     if (shape->curve == NULL) {
1038         return;
1039     }
1041     Geom::PathVector const &pathv = shape->curve->get_pathvector();
1042     if (pathv.empty())
1043         return;
1045     Geom::Matrix const i2d (sp_item_i2d_affine (item));
1047     for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
1048         *p = from_2geom(path_it->initialPoint() * i2d);
1050         Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
1051         Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
1052         while (curve_it2 != path_it->end_closed())
1053         {
1054             /* Test whether to add the node between curve_it1 and curve_it2.
1055              * Loop to end_closed (so always including closing segment), the last node to be added
1056              * is the node between the closing segment and the segment before that one. Regardless
1057              * of the path being closed. If the path is closed, the final point was already added by
1058              * adding the initial point. */
1060             Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2);
1062             // Depending on the snapping preferences, either add only cusp nodes, or add add both cusp and smooths nodes
1063             if (snapprefs->getSnapSmoothNodes() || nodetype == Geom::NODE_NONE || nodetype == Geom::NODE_CUSP) {
1064                 *p = from_2geom(curve_it1->finalPoint() * i2d);
1065             }
1066             
1067             ++curve_it1;
1068             ++curve_it2;
1069         }
1070     }
1073 /*
1074   Local Variables:
1075   mode:c++
1076   c-file-style:"stroustrup"
1077   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1078   indent-tabs-mode:nil
1079   fill-column:99
1080   End:
1081 */
1082 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :