Code

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