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/n-art-bpath.h>
20 #include <libnr/nr-matrix-fns.h>
21 #include <libnr/nr-matrix-ops.h>
22 #include <libnr/nr-matrix-translate-ops.h>
23 #include <libnr/nr-scale-matrix-ops.h>
25 #include <sigc++/functors/ptr_fun.h>
26 #include <sigc++/adaptors/bind.h>
28 #include "macros.h"
29 #include "display/nr-arena-shape.h"
30 #include "print.h"
31 #include "document.h"
32 #include "style.h"
33 #include "marker.h"
34 #include "sp-path.h"
35 #include "prefs-utils.h"
36 #include "attributes.h"
38 #include "live_effects/lpeobject.h"
39 #include "uri.h"
40 #include "extract-uri.h"
41 #include "uri-references.h"
42 #include "bad-uri-exception.h"
43 #include "xml/repr.h"
45 #include "util/mathfns.h" // for triangle_area()
47 #define noSHAPE_VERBOSE
49 static void sp_shape_class_init (SPShapeClass *klass);
50 static void sp_shape_init (SPShape *shape);
51 static void sp_shape_finalize (GObject *object);
53 static void sp_shape_build (SPObject * object, SPDocument * document, Inkscape::XML::Node * repr);
54 static void sp_shape_release (SPObject *object);
56 static void sp_shape_set(SPObject *object, unsigned key, gchar const *value);
57 static void sp_shape_update (SPObject *object, SPCtx *ctx, unsigned int flags);
58 static void sp_shape_modified (SPObject *object, unsigned int flags);
59 static Inkscape::XML::Node *sp_shape_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
61 static void sp_shape_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags);
62 void sp_shape_print (SPItem * item, SPPrintContext * ctx);
63 static NRArenaItem *sp_shape_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags);
64 static void sp_shape_hide (SPItem *item, unsigned int key);
65 static void sp_shape_snappoints (SPItem const *item, SnapPointsIter p);
67 static void sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai);
69 static SPLPEItemClass *parent_class;
71 /**
72 * Registers the SPShape class with Gdk and returns its type number.
73 */
74 GType
75 sp_shape_get_type (void)
76 {
77 static GType type = 0;
78 if (!type) {
79 GTypeInfo info = {
80 sizeof (SPShapeClass),
81 NULL, NULL,
82 (GClassInitFunc) sp_shape_class_init,
83 NULL, NULL,
84 sizeof (SPShape),
85 16,
86 (GInstanceInitFunc) sp_shape_init,
87 NULL, /* value_table */
88 };
89 type = g_type_register_static (SP_TYPE_LPE_ITEM, "SPShape", &info, (GTypeFlags)0);
90 }
91 return type;
92 }
94 /**
95 * Initializes a SPShapeClass object. Establishes the function pointers to the class'
96 * member routines in the class vtable, and sets pointers to parent classes.
97 */
98 static void
99 sp_shape_class_init (SPShapeClass *klass)
100 {
101 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
102 SPObjectClass *sp_object_class = SP_OBJECT_CLASS(klass);
103 SPItemClass * item_class = SP_ITEM_CLASS(klass);
104 SPLPEItemClass * lpe_item_class = SP_LPE_ITEM_CLASS(klass);
106 parent_class = (SPLPEItemClass *)g_type_class_peek_parent (klass);
108 gobject_class->finalize = sp_shape_finalize;
110 sp_object_class->build = sp_shape_build;
111 sp_object_class->release = sp_shape_release;
112 sp_object_class->set = sp_shape_set;
113 sp_object_class->update = sp_shape_update;
114 sp_object_class->modified = sp_shape_modified;
115 sp_object_class->write = sp_shape_write;
117 item_class->bbox = sp_shape_bbox;
118 item_class->print = sp_shape_print;
119 item_class->show = sp_shape_show;
120 item_class->hide = sp_shape_hide;
121 item_class->snappoints = sp_shape_snappoints;
122 lpe_item_class->update_patheffect = NULL;
124 klass->set_shape = NULL;
125 }
127 /**
128 * Initializes an SPShape object.
129 */
130 static void
131 sp_shape_init (SPShape *shape)
132 {
133 for ( int i = 0 ; i < SP_MARKER_LOC_QTY ; i++ ) {
134 new (&shape->release_connect[i]) sigc::connection();
135 new (&shape->modified_connect[i]) sigc::connection();
136 }
137 }
139 static void
140 sp_shape_finalize (GObject *object)
141 {
142 SPShape *shape=(SPShape *)object;
144 for ( int i = 0 ; i < SP_MARKER_LOC_QTY ; i++ ) {
145 shape->release_connect[i].disconnect();
146 shape->release_connect[i].~connection();
147 shape->modified_connect[i].disconnect();
148 shape->modified_connect[i].~connection();
149 }
151 if (((GObjectClass *) (parent_class))->finalize) {
152 (* ((GObjectClass *) (parent_class))->finalize)(object);
153 }
154 }
156 /**
157 * Virtual build callback for SPMarker.
158 *
159 * This is to be invoked immediately after creation of an SPShape.
160 *
161 * \see sp_object_build()
162 */
163 static void
164 sp_shape_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
165 {
166 if (((SPObjectClass *) (parent_class))->build) {
167 (*((SPObjectClass *) (parent_class))->build) (object, document, repr);
168 }
169 }
171 /**
172 * Removes, releases and unrefs all children of object
173 *
174 * This is the inverse of sp_shape_build(). It must be invoked as soon
175 * as the shape is removed from the tree, even if it is still referenced
176 * by other objects. This routine also disconnects/unrefs markers and
177 * curves attached to it.
178 *
179 * \see sp_object_release()
180 */
181 static void
182 sp_shape_release (SPObject *object)
183 {
184 SPItem *item;
185 SPShape *shape;
186 SPItemView *v;
187 int i;
189 item = (SPItem *) object;
190 shape = (SPShape *) object;
192 for (i=SP_MARKER_LOC_START; i<SP_MARKER_LOC_QTY; i++) {
193 if (shape->marker[i]) {
194 sp_signal_disconnect_by_data (shape->marker[i], object);
195 for (v = item->display; v != NULL; v = v->next) {
196 sp_marker_hide ((SPMarker *) shape->marker[i], NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
197 }
198 shape->marker[i] = sp_object_hunref (shape->marker[i], object);
199 }
200 }
201 if (shape->curve) {
202 shape->curve = shape->curve->unref();
203 }
205 if (((SPObjectClass *) parent_class)->release) {
206 ((SPObjectClass *) parent_class)->release (object);
207 }
208 }
212 static void
213 sp_shape_set(SPObject *object, unsigned int key, gchar const *value)
214 {
215 if (((SPObjectClass *) parent_class)->set) {
216 ((SPObjectClass *) parent_class)->set(object, key, value);
217 }
218 }
220 static Inkscape::XML::Node *
221 sp_shape_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
222 {
223 if (((SPObjectClass *)(parent_class))->write) {
224 ((SPObjectClass *)(parent_class))->write(object, repr, flags);
225 }
227 return repr;
228 }
230 /**
231 * Updates the shape when its attributes have changed. Also establishes
232 * marker objects to match the style settings.
233 */
234 static void
235 sp_shape_update (SPObject *object, SPCtx *ctx, unsigned int flags)
236 {
237 SPItem *item = (SPItem *) object;
238 SPShape *shape = (SPShape *) object;
240 if (((SPObjectClass *) (parent_class))->update) {
241 (* ((SPObjectClass *) (parent_class))->update) (object, ctx, flags);
242 }
244 /* This stanza checks that an object's marker style agrees with
245 * the marker objects it has allocated. sp_shape_set_marker ensures
246 * that the appropriate marker objects are present (or absent) to
247 * match the style.
248 */
249 /* TODO: It would be nice if this could be done at an earlier level */
250 for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
251 sp_shape_set_marker (object, i, object->style->marker[i].value);
252 }
254 if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
255 SPStyle *style;
256 style = SP_OBJECT_STYLE (object);
257 if (style->stroke_width.unit == SP_CSS_UNIT_PERCENT) {
258 SPItemCtx *ictx = (SPItemCtx *) ctx;
259 double const aw = 1.0 / NR::expansion(ictx->i2vp);
260 style->stroke_width.computed = style->stroke_width.value * aw;
261 for (SPItemView *v = ((SPItem *) (shape))->display; v != NULL; v = v->next) {
262 nr_arena_shape_set_style ((NRArenaShape *) v->arenaitem, style);
263 }
264 }
265 }
267 if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) {
268 /* This is suboptimal, because changing parent style schedules recalculation */
269 /* But on the other hand - how can we know that parent does not tie style and transform */
270 NR::Maybe<NR::Rect> paintbox = SP_ITEM(object)->getBounds(NR::identity());
271 for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
272 NRArenaShape * const s = NR_ARENA_SHAPE(v->arenaitem);
273 if (flags & SP_OBJECT_MODIFIED_FLAG) {
274 nr_arena_shape_set_path(s, shape->curve, (flags & SP_OBJECT_USER_MODIFIED_FLAG_B));
275 }
276 if (paintbox) {
277 s->setPaintBox(*paintbox);
278 }
279 }
280 }
282 if (sp_shape_has_markers (shape)) {
284 /* Dimension marker views */
285 for (SPItemView *v = item->display; v != NULL; v = v->next) {
287 if (!v->arenaitem->key) {
288 /* Get enough keys for all, start, mid and end marker types,
289 ** and set this view's arenaitem key to the first of these keys.
290 */
291 NR_ARENA_ITEM_SET_KEY (
292 v->arenaitem,
293 sp_item_display_key_new (SP_MARKER_LOC_QTY)
294 );
295 }
297 for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
298 if (shape->marker[i]) {
299 sp_marker_show_dimension ((SPMarker *) shape->marker[i],
300 NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i - SP_MARKER_LOC,
301 sp_shape_number_of_markers (shape, i));
302 }
303 }
304 }
306 /* Update marker views */
307 for (SPItemView *v = item->display; v != NULL; v = v->next) {
308 sp_shape_update_marker_view (shape, v->arenaitem);
309 }
310 }
311 }
314 /**
315 * Works out whether a marker of a given type is required at a particular
316 * point on a shape.
317 *
318 * \param shape Shape of interest.
319 * \param m Marker type (e.g. SP_MARKER_LOC_START)
320 * \param bp Path segment.
321 * \return 1 if a marker is required here, otherwise 0.
322 */
323 bool
324 sp_shape_marker_required(SPShape const *shape, int const m, NArtBpath *bp)
325 {
326 if (shape->marker[m] == NULL) {
327 return false;
328 }
330 if (bp == SP_CURVE_BPATH(shape->curve))
331 return m == SP_MARKER_LOC_START;
332 else if (bp[1].code == NR_END)
333 return m == SP_MARKER_LOC_END;
334 else
335 return m == SP_MARKER_LOC_MID;
336 }
338 static bool
339 is_moveto(NRPathcode const c)
340 {
341 return c == NR_MOVETO || c == NR_MOVETO_OPEN;
342 }
344 /**
345 * Helper function that advances a subpath's bpath to the first subpath
346 * by checking for moveto segments.
347 *
348 * \pre The bpath[] containing bp begins with a moveto.
349 */
350 static NArtBpath const *
351 first_seg_in_subpath(NArtBpath const *bp)
352 {
353 while (!is_moveto(bp->code)) {
354 --bp;
355 }
356 return bp;
357 }
359 /**
360 * Advances the bpath to the last segment in the subpath.
361 */
362 static NArtBpath const *
363 last_seg_in_subpath(NArtBpath const *bp)
364 {
365 for(;;) {
366 ++bp;
367 switch (bp->code) {
368 case NR_MOVETO:
369 case NR_MOVETO_OPEN:
370 case NR_END:
371 --bp;
372 return bp;
374 default: continue;
375 }
376 }
377 }
380 /* A subpath begins with a moveto and ends immediately before the next moveto or NR_END.
381 * (`moveto' here means either NR_MOVETO or NR_MOVETO_OPEN.) I'm assuming that non-empty
382 * paths always begin with a moveto.
383 *
384 * The control points of the subpath are the control points of the path elements of the subpath.
385 *
386 * As usual, the control points of a moveto or NR_LINETO are {c(3)}, and
387 * the control points of a NR_CURVETO are {c(1), c(2), c(3)}.
388 * (It follows from the definition that NR_END isn't part of a subpath.)
389 *
390 * The initial control point is bpath[bi0].c(3).
391 *
392 * Reference: http://www.w3.org/TR/SVG11/painting.html#MarkerElement, the `orient' attribute.
393 * Reference for behaviour of zero-length segments:
394 * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
395 */
397 static double const no_tangent = 128.0; /* arbitrarily-chosen value outside the range of atan2,
398 * i.e. outside of [-pi, pi]. This value is incremented by
399 * 1 and checked using > to be safe from floating-point
400 * equality comparison madness.*/
402 /**
403 * Helper function to calculate the outgoing tangent of a path
404 * ( atan2(other - p0) )
405 * \pre The bpath[] containing bp0 begins with a moveto.
406 */
407 static double
408 outgoing_tangent(NArtBpath const *bp0)
409 {
410 /* See notes in comment block above. */
412 g_assert(bp0->code != NR_END);
413 NR::Point const &p0 = bp0->c(3);
414 NR::Point other;
415 for (NArtBpath const *bp = bp0;;) {
416 ++bp;
417 switch (bp->code) {
418 case NR_LINETO:
419 other = bp->c(3);
420 if (other != p0) {
421 goto found;
422 }
423 break;
425 case NR_CURVETO:
426 for (unsigned ci = 1; ci <= 3; ++ci) {
427 other = bp->c(ci);
428 if (other != p0) {
429 goto found;
430 }
431 }
432 break;
434 case NR_MOVETO_OPEN:
435 case NR_END:
436 case NR_MOVETO:
437 bp = first_seg_in_subpath(bp0);
438 if (bp == bp0) {
439 /* Gone right around the subpath without finding any different point since the
440 * initial moveto. */
441 return no_tangent + 1;
442 }
443 if (bp->code != NR_MOVETO) {
444 /* Open subpath. */
445 return no_tangent + 1;
446 }
447 other = bp->c(3);
448 if (other != p0) {
449 goto found;
450 }
451 break;
452 }
454 if (bp == bp0) {
455 /* Back where we started, so zero-length subpath. */
456 return no_tangent + 1;
458 /* Note: this test must come after we've looked at element bp, in case bp0 is a curve:
459 * we must look at c(1) and c(2). (E.g. single-curve subpath.)
460 */
461 }
462 }
464 found:
465 return atan2( other - p0 );
466 }
468 /**
469 * Helper function to calculate the incoming tangent of a path
470 * ( atan2(p0 - other) )
471 *
472 * \pre The bpath[] containing bp0 begins with a moveto.
473 */
474 static double
475 incoming_tangent(NArtBpath const *bp0)
476 {
477 /* See notes in comment block before outgoing_tangent. */
479 g_assert(bp0->code != NR_END);
480 NR::Point const &p0 = bp0->c(3);
481 NR::Point other;
482 for (NArtBpath const *bp = bp0;;) {
483 switch (bp->code) {
484 case NR_LINETO:
485 other = bp->c(3);
486 if (other != p0) {
487 goto found;
488 }
489 --bp;
490 break;
492 case NR_CURVETO:
493 for (unsigned ci = 3; ci != 0; --ci) {
494 other = bp->c(ci);
495 if (other != p0) {
496 goto found;
497 }
498 }
499 --bp;
500 break;
502 case NR_MOVETO:
503 case NR_MOVETO_OPEN:
504 other = bp->c(3);
505 if (other != p0) {
506 goto found;
507 }
508 if (bp->code != NR_MOVETO) {
509 /* Open subpath. */
510 return no_tangent + 1;
511 }
512 bp = last_seg_in_subpath(bp0);
513 break;
515 default: /* includes NR_END */
516 g_error("Found invalid path code %u in middle of path.", bp->code);
517 return no_tangent + 1;
518 }
520 if (bp == bp0) {
521 /* Back where we started from: zero-length subpath. */
522 return no_tangent + 1;
523 }
524 }
526 found:
527 return atan2( p0 - other );
528 }
531 /**
532 * Calculate the transform required to get a marker's path object in the
533 * right place for particular path segment on a shape. You should
534 * call sp_shape_marker_required first to see if a marker is required
535 * at this point.
536 *
537 * \see sp_shape_marker_required.
538 *
539 * \param shape Shape which the marker is for.
540 * \param m Marker type (e.g. SP_MARKER_LOC_START)
541 * \param bp Path segment which the arrow is for.
542 * \return Transform matrix.
543 */
544 NR::Matrix
545 sp_shape_marker_get_transform(SPShape const *shape, NArtBpath const *bp)
546 {
547 g_return_val_if_fail(( is_moveto(SP_CURVE_BPATH(shape->curve)[0].code)
548 && ( 0 < shape->curve->end )
549 && ( SP_CURVE_BPATH(shape->curve)[shape->curve->end].code == NR_END ) ),
550 NR::Matrix(NR::translate(bp->c(3))));
551 double const angle1 = incoming_tangent(bp);
552 double const angle2 = outgoing_tangent(bp);
554 double ret_angle;
555 if (angle1 > no_tangent) {
556 /* First vertex of an open subpath. */
557 ret_angle = ( angle2 > no_tangent
558 ? 0.
559 : angle2 );
560 } else if (angle2 > no_tangent) {
561 /* Last vertex of an open subpath. */
562 ret_angle = angle1;
563 } else {
564 ret_angle = .5 * (angle1 + angle2);
566 if ( fabs( angle2 - angle1 ) > M_PI ) {
567 /* ret_angle is in the middle of the larger of the two sectors between angle1 and
568 * angle2, so flip it by 180degrees to force it to the middle of the smaller sector.
569 *
570 * (Imagine a circle with rays drawn at angle1 and angle2 from the centre of the
571 * circle. Those two rays divide the circle into two sectors.)
572 */
573 ret_angle += M_PI;
574 }
575 }
577 return NR::Matrix(NR::rotate(ret_angle)) * NR::translate(bp->c(3));
578 }
580 /**
581 * Updates the instances (views) of a given marker in a shape.
582 * Marker views have to be scaled already. The transformation
583 * is retrieved and then shown by calling sp_marker_show_instance.
584 */
585 static void
586 sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai)
587 {
588 SPStyle *style = ((SPObject *) shape)->style;
590 for (int i = SP_MARKER_LOC_START; i < SP_MARKER_LOC_QTY; i++) {
591 if (shape->marker[i] == NULL) {
592 continue;
593 }
595 int n = 0;
597 for (NArtBpath *bp = SP_CURVE_BPATH(shape->curve); bp->code != NR_END; bp++) {
598 if (sp_shape_marker_required (shape, i, bp)) {
599 NR::Matrix const m(sp_shape_marker_get_transform(shape, bp));
600 sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
601 NR_ARENA_ITEM_GET_KEY(ai) + i, n, m,
602 style->stroke_width.computed);
603 n++;
604 }
605 }
606 }
607 }
609 /**
610 * Sets modified flag for all sub-item views.
611 */
612 static void
613 sp_shape_modified (SPObject *object, unsigned int flags)
614 {
615 SPShape *shape = SP_SHAPE (object);
617 if (((SPObjectClass *) (parent_class))->modified) {
618 (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
619 }
621 if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
622 for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
623 nr_arena_shape_set_style (NR_ARENA_SHAPE (v->arenaitem), object->style);
624 }
625 }
626 }
628 /**
629 * Calculates the bounding box for item, storing it into bbox.
630 * This also includes the bounding boxes of any markers included in the shape.
631 */
632 static void sp_shape_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags)
633 {
634 SPShape const *shape = SP_SHAPE (item);
636 if (shape->curve) {
638 NRRect cbbox;
639 NRBPath bp;
641 bp.path = SP_CURVE_BPATH (shape->curve);
643 cbbox.x0 = cbbox.y0 = NR_HUGE;
644 cbbox.x1 = cbbox.y1 = -NR_HUGE;
646 nr_path_matrix_bbox_union(&bp, transform, &cbbox);
648 if ((SPItem::BBoxType) flags != SPItem::GEOMETRIC_BBOX) {
650 SPStyle* style=SP_OBJECT_STYLE (item);
651 if (!style->stroke.isNone()) {
652 double const scale = expansion(transform);
653 if ( fabs(style->stroke_width.computed * scale) > 0.01 ) { // sinon c'est 0=oon veut pas de bord
654 double const width = MAX(0.125, style->stroke_width.computed * scale);
655 if ( fabs(cbbox.x1-cbbox.x0) > -0.00001 && fabs(cbbox.y1-cbbox.y0) > -0.00001 ) {
656 cbbox.x0-=0.5*width;
657 cbbox.x1+=0.5*width;
658 cbbox.y0-=0.5*width;
659 cbbox.y1+=0.5*width;
660 }
661 }
662 }
664 // Union with bboxes of the markers, if any
665 if (sp_shape_has_markers (shape)) {
666 for (NArtBpath* bp = SP_CURVE_BPATH(shape->curve); bp->code != NR_END; bp++) {
667 for (int m = SP_MARKER_LOC_START; m < SP_MARKER_LOC_QTY; m++) {
668 if (sp_shape_marker_required (shape, m, bp)) {
670 SPMarker* marker = SP_MARKER (shape->marker[m]);
671 SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[m]));
673 NR::Matrix tr(sp_shape_marker_get_transform(shape, bp));
675 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
676 tr = NR::scale(style->stroke_width.computed) * tr;
677 }
679 // total marker transform
680 tr = marker_item->transform * marker->c2p * tr * transform;
682 // get bbox of the marker with that transform
683 NRRect marker_bbox;
684 sp_item_invoke_bbox (marker_item, &marker_bbox, tr, true);
685 // union it with the shape bbox
686 nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
687 }
688 }
689 }
690 }
691 }
693 // copy our bbox to the variable we're given
694 *bbox = cbbox;
695 }
696 }
698 /**
699 * Prepares shape for printing. Handles printing of comments for printing
700 * debugging, sizes the item to fit into the document width/height,
701 * applies print fill/stroke, sets transforms for markers, and adds
702 * comment labels.
703 */
704 void
705 sp_shape_print (SPItem *item, SPPrintContext *ctx)
706 {
707 NRRect pbox, dbox, bbox;
709 SPShape *shape = SP_SHAPE(item);
711 if (!shape->curve) return;
713 gint add_comments = prefs_get_int_attribute_limited ("printing.debug", "add-label-comments", 0, 0, 1);
714 if (add_comments) {
715 gchar * comment = g_strdup_printf("begin '%s'",
716 SP_OBJECT(item)->defaultLabel());
717 sp_print_comment(ctx, comment);
718 g_free(comment);
719 }
721 /* fixme: Think (Lauris) */
722 sp_item_invoke_bbox(item, &pbox, NR::identity(), TRUE);
723 dbox.x0 = 0.0;
724 dbox.y0 = 0.0;
725 dbox.x1 = sp_document_width (SP_OBJECT_DOCUMENT (item));
726 dbox.y1 = sp_document_height (SP_OBJECT_DOCUMENT (item));
727 sp_item_bbox_desktop (item, &bbox);
728 NR::Matrix const i2d = sp_item_i2d_affine(item);
730 SPStyle* style = SP_OBJECT_STYLE (item);
732 if (!style->fill.isNone()) {
733 NRBPath bp;
734 bp.path = SP_CURVE_BPATH(shape->curve);
735 sp_print_fill (ctx, &bp, &i2d, style, &pbox, &dbox, &bbox);
736 }
738 if (!style->stroke.isNone()) {
739 NRBPath bp;
740 bp.path = SP_CURVE_BPATH(shape->curve);
741 sp_print_stroke (ctx, &bp, &i2d, style, &pbox, &dbox, &bbox);
742 }
744 for (NArtBpath* bp = SP_CURVE_BPATH(shape->curve); bp->code != NR_END; bp++) {
745 for (int m = SP_MARKER_LOC_START; m < SP_MARKER_LOC_QTY; m++) {
746 if (sp_shape_marker_required (shape, m, bp)) {
748 SPMarker* marker = SP_MARKER (shape->marker[m]);
749 SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[m]));
751 NR::Matrix tr(sp_shape_marker_get_transform(shape, bp));
753 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
754 tr = NR::scale(style->stroke_width.computed) * tr;
755 }
757 tr = marker_item->transform * marker->c2p * tr;
759 NR::Matrix old_tr = marker_item->transform;
760 marker_item->transform = tr;
761 sp_item_invoke_print (marker_item, ctx);
762 marker_item->transform = old_tr;
763 }
764 }
765 }
767 if (add_comments) {
768 gchar * comment = g_strdup_printf("end '%s'",
769 SP_OBJECT(item)->defaultLabel());
770 sp_print_comment(ctx, comment);
771 g_free(comment);
772 }
773 }
775 /**
776 * Sets style, path, and paintbox. Updates marker views, including dimensions.
777 */
778 static NRArenaItem *
779 sp_shape_show (SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int /*flags*/)
780 {
781 SPObject *object = SP_OBJECT(item);
782 SPShape *shape = SP_SHAPE(item);
784 NRArenaItem *arenaitem = NRArenaShape::create(arena);
785 NRArenaShape * const s = NR_ARENA_SHAPE(arenaitem);
786 nr_arena_shape_set_style(s, object->style);
787 nr_arena_shape_set_path(s, shape->curve, false);
788 NR::Maybe<NR::Rect> paintbox = item->getBounds(NR::identity());
789 if (paintbox) {
790 s->setPaintBox(*paintbox);
791 }
793 if (sp_shape_has_markers (shape)) {
795 /* Dimension the marker views */
796 if (!arenaitem->key) {
797 NR_ARENA_ITEM_SET_KEY (arenaitem, sp_item_display_key_new (SP_MARKER_LOC_QTY));
798 }
800 for (int i = 0; i < SP_MARKER_LOC_QTY; i++) {
801 if (shape->marker[i]) {
802 sp_marker_show_dimension ((SPMarker *) shape->marker[i],
803 NR_ARENA_ITEM_GET_KEY (arenaitem) + i - SP_MARKER_LOC,
804 sp_shape_number_of_markers (shape, i));
805 }
806 }
809 /* Update marker views */
810 sp_shape_update_marker_view (shape, arenaitem);
811 }
813 return arenaitem;
814 }
816 /**
817 * Hides/removes marker views from the shape.
818 */
819 static void
820 sp_shape_hide (SPItem *item, unsigned int key)
821 {
822 SPShape *shape;
823 SPItemView *v;
824 int i;
826 shape = (SPShape *) item;
828 for (i=0; i<SP_MARKER_LOC_QTY; i++) {
829 if (shape->marker[i]) {
830 for (v = item->display; v != NULL; v = v->next) {
831 if (key == v->key) {
832 sp_marker_hide ((SPMarker *) shape->marker[i],
833 NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
834 }
835 }
836 }
837 }
839 if (((SPItemClass *) parent_class)->hide) {
840 ((SPItemClass *) parent_class)->hide (item, key);
841 }
842 }
844 /**
845 * \param shape Shape.
846 * \return TRUE if the shape has any markers, or FALSE if not.
847 */
848 int
849 sp_shape_has_markers (SPShape const *shape)
850 {
851 /* Note, we're ignoring 'marker' settings, which technically should apply for
852 all three settings. This should be fixed later such that if 'marker' is
853 specified, then all three should appear. */
855 return (
856 shape->curve &&
857 (shape->marker[SP_MARKER_LOC_START] ||
858 shape->marker[SP_MARKER_LOC_MID] ||
859 shape->marker[SP_MARKER_LOC_END])
860 );
861 }
864 /**
865 * \param shape Shape.
866 * \param type Marker type (e.g. SP_MARKER_LOC_START)
867 * \return Number of markers that the shape has of this type.
868 */
869 int
870 sp_shape_number_of_markers (SPShape *shape, int type)
871 {
872 int n = 0;
873 for (NArtBpath* bp = SP_CURVE_BPATH(shape->curve); bp->code != NR_END; bp++) {
874 if (sp_shape_marker_required (shape, type, bp)) {
875 n++;
876 }
877 }
879 return n;
880 }
882 /**
883 * Checks if the given marker is used in the shape, and if so, it
884 * releases it by calling sp_marker_hide. Also detaches signals
885 * and unrefs the marker from the shape.
886 */
887 static void
888 sp_shape_marker_release (SPObject *marker, SPShape *shape)
889 {
890 SPItem *item;
891 int i;
893 item = (SPItem *) shape;
895 for (i = SP_MARKER_LOC_START; i < SP_MARKER_LOC_QTY; i++) {
896 if (marker == shape->marker[i]) {
897 SPItemView *v;
898 /* Hide marker */
899 for (v = item->display; v != NULL; v = v->next) {
900 sp_marker_hide ((SPMarker *) (shape->marker[i]), NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
901 /* fixme: Do we need explicit remove here? (Lauris) */
902 /* nr_arena_item_set_mask (v->arenaitem, NULL); */
903 }
904 /* Detach marker */
905 sp_signal_disconnect_by_data (shape->marker[i], item);
906 shape->marker[i] = sp_object_hunref (shape->marker[i], item);
907 }
908 }
909 }
911 /**
912 * No-op. Exists for handling 'modified' messages
913 */
914 static void
915 sp_shape_marker_modified (SPObject */*marker*/, guint /*flags*/, SPItem */*item*/)
916 {
917 /* I think mask does update automagically */
918 /* g_warning ("Item %s mask %s modified", SP_OBJECT_ID (item), SP_OBJECT_ID (mask)); */
919 }
921 /**
922 * Adds a new marker to shape object at the location indicated by key. value
923 * must be a valid URI reference resolvable from the shape object (i.e., present
924 * in the document <defs>). If the shape object already has a marker
925 * registered at the given position, it is removed first. Then the
926 * new marker is hrefed and its signals connected.
927 */
928 void
929 sp_shape_set_marker (SPObject *object, unsigned int key, const gchar *value)
930 {
931 SPItem *item = (SPItem *) object;
932 SPShape *shape = (SPShape *) object;
934 if (key < SP_MARKER_LOC_START || key > SP_MARKER_LOC_END) {
935 return;
936 }
938 SPObject *mrk = sp_css_uri_reference_resolve (SP_OBJECT_DOCUMENT (object), value);
939 if (mrk != shape->marker[key]) {
940 if (shape->marker[key]) {
941 SPItemView *v;
943 /* Detach marker */
944 shape->release_connect[key].disconnect();
945 shape->modified_connect[key].disconnect();
947 /* Hide marker */
948 for (v = item->display; v != NULL; v = v->next) {
949 sp_marker_hide ((SPMarker *) (shape->marker[key]),
950 NR_ARENA_ITEM_GET_KEY (v->arenaitem) + key);
951 /* fixme: Do we need explicit remove here? (Lauris) */
952 /* nr_arena_item_set_mask (v->arenaitem, NULL); */
953 }
955 /* Unref marker */
956 shape->marker[key] = sp_object_hunref (shape->marker[key], object);
957 }
958 if (SP_IS_MARKER (mrk)) {
959 shape->marker[key] = sp_object_href (mrk, object);
960 shape->release_connect[key] = mrk->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_shape_marker_release), shape));
961 shape->modified_connect[key] = mrk->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_shape_marker_modified), shape));
962 }
963 }
964 }
968 /* Shape section */
970 /**
971 * Calls any registered handlers for the set_shape action
972 */
973 void
974 sp_shape_set_shape (SPShape *shape)
975 {
976 g_return_if_fail (shape != NULL);
977 g_return_if_fail (SP_IS_SHAPE (shape));
979 if (SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape) {
980 SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape (shape);
981 }
982 }
984 /**
985 * Adds a curve to the shape. If owner is specified, a reference
986 * will be made, otherwise the curve will be copied into the shape.
987 * Any existing curve in the shape will be unreferenced first.
988 * This routine also triggers a request to update the display.
989 */
990 void
991 sp_shape_set_curve (SPShape *shape, SPCurve *curve, unsigned int owner)
992 {
993 if (shape->curve) {
994 shape->curve = shape->curve->unref();
995 }
996 if (curve) {
997 if (owner) {
998 shape->curve = curve->ref();
999 } else {
1000 shape->curve = curve->copy();
1001 }
1002 }
1003 SP_OBJECT(shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1004 }
1006 /**
1007 * Return duplicate of curve (if any exists) or NULL if there is no curve
1008 */
1009 SPCurve *
1010 sp_shape_get_curve (SPShape *shape)
1011 {
1012 if (shape->curve) {
1013 return shape->curve->copy();
1014 }
1015 return NULL;
1016 }
1018 /**
1019 * Same as sp_shape_set_curve but without updating the display
1020 */
1021 void
1022 sp_shape_set_curve_insync (SPShape *shape, SPCurve *curve, unsigned int owner)
1023 {
1024 if (shape->curve) {
1025 shape->curve = shape->curve->unref();
1026 }
1027 if (curve) {
1028 if (owner) {
1029 shape->curve = curve->ref();
1030 } else {
1031 shape->curve = curve->copy();
1032 }
1033 }
1034 }
1036 /**
1037 * Return all nodes in a path that are to be considered for snapping
1038 */
1039 static void sp_shape_snappoints(SPItem const *item, SnapPointsIter p)
1040 {
1041 g_assert(item != NULL);
1042 g_assert(SP_IS_SHAPE(item));
1044 SPShape const *shape = SP_SHAPE(item);
1045 if (shape->curve == NULL) {
1046 return;
1047 }
1049 NR::Matrix const i2d (sp_item_i2d_affine (item));
1050 NArtBpath const *b = SP_CURVE_BPATH(shape->curve);
1052 // Cycle through the nodes in the concatenated subpaths
1053 while (b->code != NR_END) {
1054 NR::Point pos = b->c(3) * i2d; // this is the current node
1056 // NR_MOVETO Indicates the start of a closed subpath, see nr-path-code.h
1057 // If we're looking at a closed subpath, then we can skip this first
1058 // point of the subpath because it's coincident with the last point.
1059 if (b->code != NR_MOVETO) {
1060 if (b->code == NR_MOVETO_OPEN || b->code == NR_LINETO || b[1].code == NR_LINETO || b[1].code == NR_END) {
1061 // end points of a line segment are always considered for snapping
1062 *p = pos;
1063 } else {
1064 // g_assert(b->code == NR_CURVETO);
1065 NR::Point ppos, npos;
1066 ppos = b->code == NR_CURVETO ? b->c(2) * i2d : pos; // backward handle
1067 npos = b[1].code == NR_CURVETO ? b[1].c(1) * i2d : pos; // forward handle
1068 // Determine whether a node is at a smooth part of the path, by
1069 // calculating a measure for the collinearity of the handles
1070 bool c1 = fabs (Inkscape::Util::triangle_area (pos, ppos, npos)) < 1; // points are (almost) collinear
1071 bool c2 = NR::L2(pos - ppos) < 1e-6 || NR::L2(pos - npos) < 1e-6; // endnode, or a node with a retracted handle
1072 if (!(c1 & !c2)) {
1073 *p = pos; // only return non-smooth nodes ("cusps")
1074 }
1075 }
1076 }
1078 b++;
1079 }
1080 }
1082 /*
1083 Local Variables:
1084 mode:c++
1085 c-file-style:"stroustrup"
1086 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1087 indent-tabs-mode:nil
1088 fill-column:99
1089 End:
1090 */
1091 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :