1 #define __SP_SHAPE_C__
3 /*
4 * Base class for shapes, including <path> element
5 *
6 * Author:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 *
9 * Copyright (C) 1999-2002 Lauris Kaplinski
10 * Copyright (C) 2000-2001 Ximian, Inc.
11 * Copyright (C) 2004 John Cliff
12 * Copyright (C) 2007-2008 Johan Engelen
13 *
14 * Released under GNU GPL, read the file 'COPYING' for more information
15 */
17 #ifdef HAVE_CONFIG_H
18 # include "config.h"
19 #endif
22 #include <libnr/n-art-bpath.h>
23 #include <libnr/nr-matrix-fns.h>
24 #include <libnr/nr-matrix-ops.h>
25 #include <libnr/nr-matrix-translate-ops.h>
26 #include <libnr/nr-scale-matrix-ops.h>
28 #include <sigc++/functors/ptr_fun.h>
29 #include <sigc++/adaptors/bind.h>
31 #include "macros.h"
32 #include "display/nr-arena-shape.h"
33 #include "print.h"
34 #include "document.h"
35 #include "style.h"
36 #include "marker.h"
37 #include "sp-path.h"
38 #include "prefs-utils.h"
39 #include "attributes.h"
41 #include "live_effects/effect.h"
42 #include "live_effects/lpeobject.h"
43 #include "live_effects/lpeobject-reference.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::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 void lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPShape *shape);
75 static void lpeobject_ref_modified(SPObject *href, guint flags, SPShape *shape);
77 static SPItemClass *parent_class;
79 /**
80 * Registers the SPShape class with Gdk and returns its type number.
81 */
82 GType
83 sp_shape_get_type (void)
84 {
85 static GType type = 0;
86 if (!type) {
87 GTypeInfo info = {
88 sizeof (SPShapeClass),
89 NULL, NULL,
90 (GClassInitFunc) sp_shape_class_init,
91 NULL, NULL,
92 sizeof (SPShape),
93 16,
94 (GInstanceInitFunc) sp_shape_init,
95 NULL, /* value_table */
96 };
97 type = g_type_register_static (SP_TYPE_ITEM, "SPShape", &info, (GTypeFlags)0);
98 }
99 return type;
100 }
102 /**
103 * Initializes a SPShapeClass object. Establishes the function pointers to the class'
104 * member routines in the class vtable, and sets pointers to parent classes.
105 */
106 static void
107 sp_shape_class_init (SPShapeClass *klass)
108 {
109 GObjectClass *gobject_class;
110 SPObjectClass *sp_object_class;
111 SPItemClass * item_class;
112 SPPathClass * path_class;
114 gobject_class = (GObjectClass *) klass;
115 sp_object_class = (SPObjectClass *) klass;
116 item_class = (SPItemClass *) klass;
117 path_class = (SPPathClass *) klass;
119 parent_class = (SPItemClass *)g_type_class_peek_parent (klass);
121 gobject_class->finalize = sp_shape_finalize;
123 sp_object_class->build = sp_shape_build;
124 sp_object_class->release = sp_shape_release;
125 sp_object_class->set = sp_shape_set;
126 sp_object_class->update = sp_shape_update;
127 sp_object_class->modified = sp_shape_modified;
128 sp_object_class->write = sp_shape_write;
130 item_class->bbox = sp_shape_bbox;
131 item_class->print = sp_shape_print;
132 item_class->show = sp_shape_show;
133 item_class->hide = sp_shape_hide;
134 item_class->snappoints = sp_shape_snappoints;
136 klass->set_shape = NULL;
137 klass->update_patheffect = NULL;
138 }
140 /**
141 * Initializes an SPShape object.
142 */
143 static void
144 sp_shape_init (SPShape *shape)
145 {
146 shape->path_effect_href = NULL;
147 shape->path_effect_ref = new Inkscape::LivePathEffect::LPEObjectReference(SP_OBJECT(shape));
148 new (&shape->lpe_modified_connection) sigc::connection();
150 for ( int i = 0 ; i < SP_MARKER_LOC_QTY ; i++ ) {
151 new (&shape->release_connect[i]) sigc::connection();
152 new (&shape->modified_connect[i]) sigc::connection();
153 }
154 }
156 static void
157 sp_shape_finalize (GObject *object)
158 {
159 SPShape *shape=(SPShape *)object;
161 for ( int i = 0 ; i < SP_MARKER_LOC_QTY ; i++ ) {
162 shape->release_connect[i].disconnect();
163 shape->release_connect[i].~connection();
164 shape->modified_connect[i].disconnect();
165 shape->modified_connect[i].~connection();
166 }
168 if (((GObjectClass *) (parent_class))->finalize) {
169 (* ((GObjectClass *) (parent_class))->finalize)(object);
170 }
171 }
173 /**
174 * Virtual build callback for SPMarker.
175 *
176 * This is to be invoked immediately after creation of an SPShape.
177 *
178 * \see sp_object_build()
179 */
180 static void
181 sp_shape_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
182 {
183 SP_SHAPE(object)->path_effect_ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(lpeobject_ref_changed), SP_SHAPE(object)));
185 sp_object_read_attr(object, "inkscape:path-effect");
187 if (((SPObjectClass *) (parent_class))->build) {
188 (*((SPObjectClass *) (parent_class))->build) (object, document, repr);
189 }
190 }
192 /**
193 * Removes, releases and unrefs all children of object
194 *
195 * This is the inverse of sp_shape_build(). It must be invoked as soon
196 * as the shape is removed from the tree, even if it is still referenced
197 * by other objects. This routine also disconnects/unrefs markers and
198 * curves attached to it.
199 *
200 * \see sp_object_release()
201 */
202 static void
203 sp_shape_release (SPObject *object)
204 {
205 SPItem *item;
206 SPShape *shape;
207 SPItemView *v;
208 int i;
210 item = (SPItem *) object;
211 shape = (SPShape *) object;
213 for (i=SP_MARKER_LOC_START; i<SP_MARKER_LOC_QTY; i++) {
214 if (shape->marker[i]) {
215 sp_signal_disconnect_by_data (shape->marker[i], object);
216 for (v = item->display; v != NULL; v = v->next) {
217 sp_marker_hide ((SPMarker *) shape->marker[i], NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
218 }
219 shape->marker[i] = sp_object_hunref (shape->marker[i], object);
220 }
221 }
222 if (shape->curve) {
223 shape->curve = sp_curve_unref (shape->curve);
224 }
226 if (shape->path_effect_href) {
227 g_free(shape->path_effect_href);
228 }
229 shape->path_effect_ref->detach();
231 shape->lpe_modified_connection.disconnect();
232 shape->lpe_modified_connection.~connection();
234 if (((SPObjectClass *) parent_class)->release) {
235 ((SPObjectClass *) parent_class)->release (object);
236 }
237 }
241 static void
242 sp_shape_set(SPObject *object, unsigned int key, gchar const *value)
243 {
244 SPShape *shape = (SPShape *) object;
246 switch (key) {
247 case SP_ATTR_INKSCAPE_PATH_EFFECT:
248 if ( value && shape->path_effect_href && ( strcmp(value, shape->path_effect_href) == 0 ) ) {
249 /* No change, do nothing. */
250 } else {
251 if (shape->path_effect_href) {
252 g_free(shape->path_effect_href);
253 shape->path_effect_href = NULL;
254 }
255 if (value) {
256 shape->path_effect_href = g_strdup(value);
258 // Now do the attaching, which emits the changed signal.
259 try {
260 shape->path_effect_ref->attach(Inkscape::URI(value));
261 } catch (Inkscape::BadURIException &e) {
262 g_warning("%s", e.what());
263 shape->path_effect_ref->detach();
264 }
265 } else {
266 // Detach, which emits the changed signal.
267 shape->path_effect_ref->detach();
268 }
269 }
270 break;
271 default:
272 if (((SPObjectClass *) parent_class)->set) {
273 ((SPObjectClass *) parent_class)->set(object, key, value);
274 }
275 break;
276 }
277 }
279 static Inkscape::XML::Node *
280 sp_shape_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
281 {
282 SPShape *shape = (SPShape *) object;
284 if ( shape->path_effect_href ) {
285 repr->setAttribute("inkscape:path-effect", shape->path_effect_href);
286 } else {
287 repr->setAttribute("inkscape:path-effect", NULL);
288 }
290 if (((SPObjectClass *)(parent_class))->write) {
291 ((SPObjectClass *)(parent_class))->write(object, repr, flags);
292 }
294 return repr;
295 }
297 /**
298 * Updates the shape when its attributes have changed. Also establishes
299 * marker objects to match the style settings.
300 */
301 static void
302 sp_shape_update (SPObject *object, SPCtx *ctx, unsigned int flags)
303 {
304 SPItem *item = (SPItem *) object;
305 SPShape *shape = (SPShape *) object;
307 if (((SPObjectClass *) (parent_class))->update) {
308 (* ((SPObjectClass *) (parent_class))->update) (object, ctx, flags);
309 }
311 /* This stanza checks that an object's marker style agrees with
312 * the marker objects it has allocated. sp_shape_set_marker ensures
313 * that the appropriate marker objects are present (or absent) to
314 * match the style.
315 */
316 /* TODO: It would be nice if this could be done at an earlier level */
317 for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
318 sp_shape_set_marker (object, i, object->style->marker[i].value);
319 }
321 if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
322 SPStyle *style;
323 style = SP_OBJECT_STYLE (object);
324 if (style->stroke_width.unit == SP_CSS_UNIT_PERCENT) {
325 SPItemCtx *ictx = (SPItemCtx *) ctx;
326 double const aw = 1.0 / NR::expansion(ictx->i2vp);
327 style->stroke_width.computed = style->stroke_width.value * aw;
328 for (SPItemView *v = ((SPItem *) (shape))->display; v != NULL; v = v->next) {
329 nr_arena_shape_set_style ((NRArenaShape *) v->arenaitem, style);
330 }
331 }
332 }
334 if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) {
335 /* This is suboptimal, because changing parent style schedules recalculation */
336 /* But on the other hand - how can we know that parent does not tie style and transform */
337 NR::Maybe<NR::Rect> paintbox = SP_ITEM(object)->getBounds(NR::identity());
338 for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
339 NRArenaShape * const s = NR_ARENA_SHAPE(v->arenaitem);
340 if (flags & SP_OBJECT_MODIFIED_FLAG) {
341 nr_arena_shape_set_path(s, shape->curve, (flags & SP_OBJECT_USER_MODIFIED_FLAG_B));
342 }
343 if (paintbox) {
344 s->setPaintBox(*paintbox);
345 }
346 }
347 }
349 if (sp_shape_has_markers (shape)) {
351 /* Dimension marker views */
352 for (SPItemView *v = item->display; v != NULL; v = v->next) {
354 if (!v->arenaitem->key) {
355 /* Get enough keys for all, start, mid and end marker types,
356 ** and set this view's arenaitem key to the first of these keys.
357 */
358 NR_ARENA_ITEM_SET_KEY (
359 v->arenaitem,
360 sp_item_display_key_new (SP_MARKER_LOC_QTY)
361 );
362 }
364 for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
365 if (shape->marker[i]) {
366 sp_marker_show_dimension ((SPMarker *) shape->marker[i],
367 NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i - SP_MARKER_LOC,
368 sp_shape_number_of_markers (shape, i));
369 }
370 }
371 }
373 /* Update marker views */
374 for (SPItemView *v = item->display; v != NULL; v = v->next) {
375 sp_shape_update_marker_view (shape, v->arenaitem);
376 }
377 }
378 }
381 /**
382 * Works out whether a marker of a given type is required at a particular
383 * point on a shape.
384 *
385 * \param shape Shape of interest.
386 * \param m Marker type (e.g. SP_MARKER_LOC_START)
387 * \param bp Path segment.
388 * \return 1 if a marker is required here, otherwise 0.
389 */
390 bool
391 sp_shape_marker_required(SPShape const *shape, int const m, NArtBpath *bp)
392 {
393 if (shape->marker[m] == NULL) {
394 return false;
395 }
397 if (bp == SP_CURVE_BPATH(shape->curve))
398 return m == SP_MARKER_LOC_START;
399 else if (bp[1].code == NR_END)
400 return m == SP_MARKER_LOC_END;
401 else
402 return m == SP_MARKER_LOC_MID;
403 }
405 static bool
406 is_moveto(NRPathcode const c)
407 {
408 return c == NR_MOVETO || c == NR_MOVETO_OPEN;
409 }
411 /**
412 * Helper function that advances a subpath's bpath to the first subpath
413 * by checking for moveto segments.
414 *
415 * \pre The bpath[] containing bp begins with a moveto.
416 */
417 static NArtBpath const *
418 first_seg_in_subpath(NArtBpath const *bp)
419 {
420 while (!is_moveto(bp->code)) {
421 --bp;
422 }
423 return bp;
424 }
426 /**
427 * Advances the bpath to the last segment in the subpath.
428 */
429 static NArtBpath const *
430 last_seg_in_subpath(NArtBpath const *bp)
431 {
432 for(;;) {
433 ++bp;
434 switch (bp->code) {
435 case NR_MOVETO:
436 case NR_MOVETO_OPEN:
437 case NR_END:
438 --bp;
439 return bp;
441 default: continue;
442 }
443 }
444 }
447 /* A subpath begins with a moveto and ends immediately before the next moveto or NR_END.
448 * (`moveto' here means either NR_MOVETO or NR_MOVETO_OPEN.) I'm assuming that non-empty
449 * paths always begin with a moveto.
450 *
451 * The control points of the subpath are the control points of the path elements of the subpath.
452 *
453 * As usual, the control points of a moveto or NR_LINETO are {c(3)}, and
454 * the control points of a NR_CURVETO are {c(1), c(2), c(3)}.
455 * (It follows from the definition that NR_END isn't part of a subpath.)
456 *
457 * The initial control point is bpath[bi0].c(3).
458 *
459 * Reference: http://www.w3.org/TR/SVG11/painting.html#MarkerElement, the `orient' attribute.
460 * Reference for behaviour of zero-length segments:
461 * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
462 */
464 static double const no_tangent = 128.0; /* arbitrarily-chosen value outside the range of atan2, i.e. outside of [-pi, pi]. */
466 /**
467 * Helper function to calculate the outgoing tangent of a path
468 * ( atan2(other - p0) )
469 * \pre The bpath[] containing bp0 begins with a moveto.
470 */
471 static double
472 outgoing_tangent(NArtBpath const *bp0)
473 {
474 /* See notes in comment block above. */
476 g_assert(bp0->code != NR_END);
477 NR::Point const &p0 = bp0->c(3);
478 NR::Point other;
479 for (NArtBpath const *bp = bp0;;) {
480 ++bp;
481 switch (bp->code) {
482 case NR_LINETO:
483 other = bp->c(3);
484 if (other != p0) {
485 goto found;
486 }
487 break;
489 case NR_CURVETO:
490 for (unsigned ci = 1; ci <= 3; ++ci) {
491 other = bp->c(ci);
492 if (other != p0) {
493 goto found;
494 }
495 }
496 break;
498 case NR_MOVETO_OPEN:
499 case NR_END:
500 case NR_MOVETO:
501 bp = first_seg_in_subpath(bp0);
502 if (bp == bp0) {
503 /* Gone right around the subpath without finding any different point since the
504 * initial moveto. */
505 return no_tangent;
506 }
507 if (bp->code != NR_MOVETO) {
508 /* Open subpath. */
509 return no_tangent;
510 }
511 other = bp->c(3);
512 if (other != p0) {
513 goto found;
514 }
515 break;
516 }
518 if (bp == bp0) {
519 /* Back where we started, so zero-length subpath. */
520 return no_tangent;
522 /* Note: this test must come after we've looked at element bp, in case bp0 is a curve:
523 * we must look at c(1) and c(2). (E.g. single-curve subpath.)
524 */
525 }
526 }
528 found:
529 return atan2( other - p0 );
530 }
532 /**
533 * Helper function to calculate the incoming tangent of a path
534 * ( atan2(p0 - other) )
535 *
536 * \pre The bpath[] containing bp0 begins with a moveto.
537 */
538 static double
539 incoming_tangent(NArtBpath const *bp0)
540 {
541 /* See notes in comment block before outgoing_tangent. */
543 g_assert(bp0->code != NR_END);
544 NR::Point const &p0 = bp0->c(3);
545 NR::Point other;
546 for (NArtBpath const *bp = bp0;;) {
547 switch (bp->code) {
548 case NR_LINETO:
549 other = bp->c(3);
550 if (other != p0) {
551 goto found;
552 }
553 --bp;
554 break;
556 case NR_CURVETO:
557 for (unsigned ci = 3; ci != 0; --ci) {
558 other = bp->c(ci);
559 if (other != p0) {
560 goto found;
561 }
562 }
563 --bp;
564 break;
566 case NR_MOVETO:
567 case NR_MOVETO_OPEN:
568 other = bp->c(3);
569 if (other != p0) {
570 goto found;
571 }
572 if (bp->code != NR_MOVETO) {
573 /* Open subpath. */
574 return no_tangent;
575 }
576 bp = last_seg_in_subpath(bp0);
577 break;
579 default: /* includes NR_END */
580 g_error("Found invalid path code %u in middle of path.", bp->code);
581 return no_tangent;
582 }
584 if (bp == bp0) {
585 /* Back where we started from: zero-length subpath. */
586 return no_tangent;
587 }
588 }
590 found:
591 return atan2( p0 - other );
592 }
595 /**
596 * Calculate the transform required to get a marker's path object in the
597 * right place for particular path segment on a shape. You should
598 * call sp_shape_marker_required first to see if a marker is required
599 * at this point.
600 *
601 * \see sp_shape_marker_required.
602 *
603 * \param shape Shape which the marker is for.
604 * \param m Marker type (e.g. SP_MARKER_LOC_START)
605 * \param bp Path segment which the arrow is for.
606 * \return Transform matrix.
607 */
608 NR::Matrix
609 sp_shape_marker_get_transform(SPShape const *shape, NArtBpath const *bp)
610 {
611 g_return_val_if_fail(( is_moveto(SP_CURVE_BPATH(shape->curve)[0].code)
612 && ( 0 < shape->curve->end )
613 && ( SP_CURVE_BPATH(shape->curve)[shape->curve->end].code == NR_END ) ),
614 NR::Matrix(NR::translate(bp->c(3))));
615 double const angle1 = incoming_tangent(bp);
616 double const angle2 = outgoing_tangent(bp);
618 /* angle1 and angle2 are now each either unset (i.e. still 100 from their initialization) or in
619 [-pi, pi] from atan2. */
620 g_assert((-3.15 < angle1 && angle1 < 3.15) || (angle1 == no_tangent));
621 g_assert((-3.15 < angle2 && angle2 < 3.15) || (angle2 == no_tangent));
623 double ret_angle;
624 if (angle1 == no_tangent) {
625 /* First vertex of an open subpath. */
626 ret_angle = ( angle2 == no_tangent
627 ? 0.
628 : angle2 );
629 } else if (angle2 == no_tangent) {
630 /* Last vertex of an open subpath. */
631 ret_angle = angle1;
632 } else {
633 ret_angle = .5 * (angle1 + angle2);
635 if ( fabs( angle2 - angle1 ) > M_PI ) {
636 /* ret_angle is in the middle of the larger of the two sectors between angle1 and
637 * angle2, so flip it by 180degrees to force it to the middle of the smaller sector.
638 *
639 * (Imagine a circle with rays drawn at angle1 and angle2 from the centre of the
640 * circle. Those two rays divide the circle into two sectors.)
641 */
642 ret_angle += M_PI;
643 }
644 }
646 return NR::Matrix(NR::rotate(ret_angle)) * NR::translate(bp->c(3));
647 }
649 /**
650 * Updates the instances (views) of a given marker in a shape.
651 * Marker views have to be scaled already. The transformation
652 * is retrieved and then shown by calling sp_marker_show_instance.
653 */
654 static void
655 sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai)
656 {
657 SPStyle *style = ((SPObject *) shape)->style;
659 for (int i = SP_MARKER_LOC_START; i < SP_MARKER_LOC_QTY; i++) {
660 if (shape->marker[i] == NULL) {
661 continue;
662 }
664 int n = 0;
666 for (NArtBpath *bp = SP_CURVE_BPATH(shape->curve); bp->code != NR_END; bp++) {
667 if (sp_shape_marker_required (shape, i, bp)) {
668 NR::Matrix const m(sp_shape_marker_get_transform(shape, bp));
669 sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
670 NR_ARENA_ITEM_GET_KEY(ai) + i, n, m,
671 style->stroke_width.computed);
672 n++;
673 }
674 }
675 }
676 }
678 /**
679 * Sets modified flag for all sub-item views.
680 */
681 static void
682 sp_shape_modified (SPObject *object, unsigned int flags)
683 {
684 SPShape *shape = SP_SHAPE (object);
686 if (((SPObjectClass *) (parent_class))->modified) {
687 (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
688 }
690 if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
691 for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
692 nr_arena_shape_set_style (NR_ARENA_SHAPE (v->arenaitem), object->style);
693 }
694 }
695 }
697 /**
698 * Calculates the bounding box for item, storing it into bbox.
699 * This also includes the bounding boxes of any markers included in the shape.
700 */
701 static void sp_shape_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags)
702 {
703 SPShape const *shape = SP_SHAPE (item);
705 if (shape->curve) {
707 NRRect cbbox;
708 NRBPath bp;
710 bp.path = SP_CURVE_BPATH (shape->curve);
712 cbbox.x0 = cbbox.y0 = NR_HUGE;
713 cbbox.x1 = cbbox.y1 = -NR_HUGE;
715 nr_path_matrix_bbox_union(&bp, transform, &cbbox);
717 if ((SPItem::BBoxType) flags != SPItem::GEOMETRIC_BBOX) {
719 SPStyle* style=SP_OBJECT_STYLE (item);
720 if (!style->stroke.isNone()) {
721 double const scale = expansion(transform);
722 if ( fabs(style->stroke_width.computed * scale) > 0.01 ) { // sinon c'est 0=oon veut pas de bord
723 double const width = MAX(0.125, style->stroke_width.computed * scale);
724 if ( fabs(cbbox.x1-cbbox.x0) > -0.00001 && fabs(cbbox.y1-cbbox.y0) > -0.00001 ) {
725 cbbox.x0-=0.5*width;
726 cbbox.x1+=0.5*width;
727 cbbox.y0-=0.5*width;
728 cbbox.y1+=0.5*width;
729 }
730 }
731 }
733 // Union with bboxes of the markers, if any
734 if (sp_shape_has_markers (shape)) {
735 for (NArtBpath* bp = SP_CURVE_BPATH(shape->curve); bp->code != NR_END; bp++) {
736 for (int m = SP_MARKER_LOC_START; m < SP_MARKER_LOC_QTY; m++) {
737 if (sp_shape_marker_required (shape, m, bp)) {
739 SPMarker* marker = SP_MARKER (shape->marker[m]);
740 SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[m]));
742 NR::Matrix tr(sp_shape_marker_get_transform(shape, bp));
744 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
745 tr = NR::scale(style->stroke_width.computed) * tr;
746 }
748 // total marker transform
749 tr = marker_item->transform * marker->c2p * tr * transform;
751 // get bbox of the marker with that transform
752 NRRect marker_bbox;
753 sp_item_invoke_bbox (marker_item, &marker_bbox, tr, true);
754 // union it with the shape bbox
755 nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
756 }
757 }
758 }
759 }
760 }
762 // copy our bbox to the variable we're given
763 *bbox = cbbox;
764 }
765 }
767 /**
768 * Prepares shape for printing. Handles printing of comments for printing
769 * debugging, sizes the item to fit into the document width/height,
770 * applies print fill/stroke, sets transforms for markers, and adds
771 * comment labels.
772 */
773 void
774 sp_shape_print (SPItem *item, SPPrintContext *ctx)
775 {
776 NRRect pbox, dbox, bbox;
778 SPShape *shape = SP_SHAPE(item);
780 if (!shape->curve) return;
782 gint add_comments = prefs_get_int_attribute_limited ("printing.debug", "add-label-comments", 0, 0, 1);
783 if (add_comments) {
784 gchar * comment = g_strdup_printf("begin '%s'",
785 SP_OBJECT(item)->defaultLabel());
786 sp_print_comment(ctx, comment);
787 g_free(comment);
788 }
790 /* fixme: Think (Lauris) */
791 sp_item_invoke_bbox(item, &pbox, NR::identity(), TRUE);
792 dbox.x0 = 0.0;
793 dbox.y0 = 0.0;
794 dbox.x1 = sp_document_width (SP_OBJECT_DOCUMENT (item));
795 dbox.y1 = sp_document_height (SP_OBJECT_DOCUMENT (item));
796 sp_item_bbox_desktop (item, &bbox);
797 NR::Matrix const i2d = sp_item_i2d_affine(item);
799 SPStyle* style = SP_OBJECT_STYLE (item);
801 if (!style->fill.isNone()) {
802 NRBPath bp;
803 bp.path = SP_CURVE_BPATH(shape->curve);
804 sp_print_fill (ctx, &bp, i2d, style, &pbox, &dbox, &bbox);
805 }
807 if (!style->stroke.isNone()) {
808 NRBPath bp;
809 bp.path = SP_CURVE_BPATH(shape->curve);
810 sp_print_stroke (ctx, &bp, i2d, style, &pbox, &dbox, &bbox);
811 }
813 for (NArtBpath* bp = SP_CURVE_BPATH(shape->curve); bp->code != NR_END; bp++) {
814 for (int m = SP_MARKER_LOC_START; m < SP_MARKER_LOC_QTY; m++) {
815 if (sp_shape_marker_required (shape, m, bp)) {
817 SPMarker* marker = SP_MARKER (shape->marker[m]);
818 SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[m]));
820 NR::Matrix tr(sp_shape_marker_get_transform(shape, bp));
822 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
823 tr = NR::scale(style->stroke_width.computed) * tr;
824 }
826 tr = marker_item->transform * marker->c2p * tr;
828 NR::Matrix old_tr = marker_item->transform;
829 marker_item->transform = tr;
830 sp_item_invoke_print (marker_item, ctx);
831 marker_item->transform = old_tr;
832 }
833 }
834 }
836 if (add_comments) {
837 gchar * comment = g_strdup_printf("end '%s'",
838 SP_OBJECT(item)->defaultLabel());
839 sp_print_comment(ctx, comment);
840 g_free(comment);
841 }
842 }
844 /**
845 * Sets style, path, and paintbox. Updates marker views, including dimensions.
846 */
847 static NRArenaItem *
848 sp_shape_show (SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int /*flags*/)
849 {
850 SPObject *object = SP_OBJECT(item);
851 SPShape *shape = SP_SHAPE(item);
853 NRArenaItem *arenaitem = NRArenaShape::create(arena);
854 NRArenaShape * const s = NR_ARENA_SHAPE(arenaitem);
855 nr_arena_shape_set_style(s, object->style);
856 nr_arena_shape_set_path(s, shape->curve, false);
857 NR::Maybe<NR::Rect> paintbox = item->getBounds(NR::identity());
858 if (paintbox) {
859 s->setPaintBox(*paintbox);
860 }
862 if (sp_shape_has_markers (shape)) {
864 /* Dimension the marker views */
865 if (!arenaitem->key) {
866 NR_ARENA_ITEM_SET_KEY (arenaitem, sp_item_display_key_new (SP_MARKER_LOC_QTY));
867 }
869 for (int i = 0; i < SP_MARKER_LOC_QTY; i++) {
870 if (shape->marker[i]) {
871 sp_marker_show_dimension ((SPMarker *) shape->marker[i],
872 NR_ARENA_ITEM_GET_KEY (arenaitem) + i - SP_MARKER_LOC,
873 sp_shape_number_of_markers (shape, i));
874 }
875 }
878 /* Update marker views */
879 sp_shape_update_marker_view (shape, arenaitem);
880 }
882 return arenaitem;
883 }
885 /**
886 * Hides/removes marker views from the shape.
887 */
888 static void
889 sp_shape_hide (SPItem *item, unsigned int key)
890 {
891 SPShape *shape;
892 SPItemView *v;
893 int i;
895 shape = (SPShape *) item;
897 for (i=0; i<SP_MARKER_LOC_QTY; i++) {
898 if (shape->marker[i]) {
899 for (v = item->display; v != NULL; v = v->next) {
900 if (key == v->key) {
901 sp_marker_hide ((SPMarker *) shape->marker[i],
902 NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
903 }
904 }
905 }
906 }
908 if (((SPItemClass *) parent_class)->hide) {
909 ((SPItemClass *) parent_class)->hide (item, key);
910 }
911 }
913 /**
914 * \param shape Shape.
915 * \return TRUE if the shape has any markers, or FALSE if not.
916 */
917 int
918 sp_shape_has_markers (SPShape const *shape)
919 {
920 /* Note, we're ignoring 'marker' settings, which technically should apply for
921 all three settings. This should be fixed later such that if 'marker' is
922 specified, then all three should appear. */
924 return (
925 shape->curve &&
926 (shape->marker[SP_MARKER_LOC_START] ||
927 shape->marker[SP_MARKER_LOC_MID] ||
928 shape->marker[SP_MARKER_LOC_END])
929 );
930 }
933 /**
934 * \param shape Shape.
935 * \param type Marker type (e.g. SP_MARKER_LOC_START)
936 * \return Number of markers that the shape has of this type.
937 */
938 int
939 sp_shape_number_of_markers (SPShape *shape, int type)
940 {
941 int n = 0;
942 for (NArtBpath* bp = SP_CURVE_BPATH(shape->curve); bp->code != NR_END; bp++) {
943 if (sp_shape_marker_required (shape, type, bp)) {
944 n++;
945 }
946 }
948 return n;
949 }
951 /**
952 * Checks if the given marker is used in the shape, and if so, it
953 * releases it by calling sp_marker_hide. Also detaches signals
954 * and unrefs the marker from the shape.
955 */
956 static void
957 sp_shape_marker_release (SPObject *marker, SPShape *shape)
958 {
959 SPItem *item;
960 int i;
962 item = (SPItem *) shape;
964 for (i = SP_MARKER_LOC_START; i < SP_MARKER_LOC_QTY; i++) {
965 if (marker == shape->marker[i]) {
966 SPItemView *v;
967 /* Hide marker */
968 for (v = item->display; v != NULL; v = v->next) {
969 sp_marker_hide ((SPMarker *) (shape->marker[i]), NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
970 /* fixme: Do we need explicit remove here? (Lauris) */
971 /* nr_arena_item_set_mask (v->arenaitem, NULL); */
972 }
973 /* Detach marker */
974 sp_signal_disconnect_by_data (shape->marker[i], item);
975 shape->marker[i] = sp_object_hunref (shape->marker[i], item);
976 }
977 }
978 }
980 /**
981 * No-op. Exists for handling 'modified' messages
982 */
983 static void
984 sp_shape_marker_modified (SPObject */*marker*/, guint /*flags*/, SPItem */*item*/)
985 {
986 /* I think mask does update automagically */
987 /* g_warning ("Item %s mask %s modified", SP_OBJECT_ID (item), SP_OBJECT_ID (mask)); */
988 }
990 /**
991 * Adds a new marker to shape object at the location indicated by key. value
992 * must be a valid URI reference resolvable from the shape object (i.e., present
993 * in the document <defs>). If the shape object already has a marker
994 * registered at the given position, it is removed first. Then the
995 * new marker is hrefed and its signals connected.
996 */
997 void
998 sp_shape_set_marker (SPObject *object, unsigned int key, const gchar *value)
999 {
1000 SPItem *item = (SPItem *) object;
1001 SPShape *shape = (SPShape *) object;
1003 if (key < SP_MARKER_LOC_START || key > SP_MARKER_LOC_END) {
1004 return;
1005 }
1007 SPObject *mrk = sp_css_uri_reference_resolve (SP_OBJECT_DOCUMENT (object), value);
1008 if (mrk != shape->marker[key]) {
1009 if (shape->marker[key]) {
1010 SPItemView *v;
1012 /* Detach marker */
1013 shape->release_connect[key].disconnect();
1014 shape->modified_connect[key].disconnect();
1016 /* Hide marker */
1017 for (v = item->display; v != NULL; v = v->next) {
1018 sp_marker_hide ((SPMarker *) (shape->marker[key]),
1019 NR_ARENA_ITEM_GET_KEY (v->arenaitem) + key);
1020 /* fixme: Do we need explicit remove here? (Lauris) */
1021 /* nr_arena_item_set_mask (v->arenaitem, NULL); */
1022 }
1024 /* Unref marker */
1025 shape->marker[key] = sp_object_hunref (shape->marker[key], object);
1026 }
1027 if (SP_IS_MARKER (mrk)) {
1028 shape->marker[key] = sp_object_href (mrk, object);
1029 shape->release_connect[key] = mrk->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_shape_marker_release), shape));
1030 shape->modified_connect[key] = mrk->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_shape_marker_modified), shape));
1031 }
1032 }
1033 }
1037 /* Shape section */
1039 /**
1040 * Calls any registered handlers for the set_shape action
1041 */
1042 void
1043 sp_shape_set_shape (SPShape *shape)
1044 {
1045 g_return_if_fail (shape != NULL);
1046 g_return_if_fail (SP_IS_SHAPE (shape));
1048 if (SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape) {
1049 SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape (shape);
1050 }
1051 }
1053 /**
1054 * Adds a curve to the shape. If owner is specified, a reference
1055 * will be made, otherwise the curve will be copied into the shape.
1056 * Any existing curve in the shape will be unreferenced first.
1057 * This routine also triggers a request to update the display.
1058 */
1059 void
1060 sp_shape_set_curve (SPShape *shape, SPCurve *curve, unsigned int owner)
1061 {
1062 if (shape->curve) {
1063 shape->curve = sp_curve_unref (shape->curve);
1064 }
1065 if (curve) {
1066 if (owner) {
1067 shape->curve = sp_curve_ref (curve);
1068 } else {
1069 shape->curve = sp_curve_copy (curve);
1070 }
1071 }
1072 SP_OBJECT(shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1073 }
1075 /**
1076 * Return duplicate of curve (if any exists) or NULL if there is no curve
1077 */
1078 SPCurve *
1079 sp_shape_get_curve (SPShape *shape)
1080 {
1081 if (shape->curve) {
1082 return sp_curve_copy (shape->curve);
1083 }
1084 return NULL;
1085 }
1087 /* NOT FOR GENERAL PUBLIC UNTIL SORTED OUT (Lauris) */
1088 void
1089 sp_shape_set_curve_insync (SPShape *shape, SPCurve *curve, unsigned int owner)
1090 {
1091 if (shape->curve) {
1092 shape->curve = sp_curve_unref (shape->curve);
1093 }
1094 if (curve) {
1095 if (owner) {
1096 shape->curve = sp_curve_ref (curve);
1097 } else {
1098 shape->curve = sp_curve_copy (curve);
1099 }
1100 }
1101 }
1103 /**
1104 * Return all nodes in a path that are to be considered for snapping
1105 */
1106 static void sp_shape_snappoints(SPItem const *item, SnapPointsIter p)
1107 {
1108 g_assert(item != NULL);
1109 g_assert(SP_IS_SHAPE(item));
1111 SPShape const *shape = SP_SHAPE(item);
1112 if (shape->curve == NULL) {
1113 return;
1114 }
1116 NR::Matrix const i2d (sp_item_i2d_affine (item));
1117 NArtBpath const *b = SP_CURVE_BPATH(shape->curve);
1119 // Cycle through the nodes in the concatenated subpaths
1120 while (b->code != NR_END) {
1121 NR::Point pos = b->c(3) * i2d; // this is the current node
1123 // NR_MOVETO Indicates the start of a closed subpath, see nr-path-code.h
1124 // If we're looking at a closed subpath, then we can skip this first
1125 // point of the subpath because it's coincident with the last point.
1126 if (b->code != NR_MOVETO) {
1127 if (b->code == NR_MOVETO_OPEN || b->code == NR_LINETO || b[1].code == NR_LINETO || b[1].code == NR_END) {
1128 // end points of a line segment are always considered for snapping
1129 *p = pos;
1130 } else {
1131 // g_assert(b->code == NR_CURVETO);
1132 NR::Point ppos, npos;
1133 ppos = b->code == NR_CURVETO ? b->c(2) * i2d : pos; // backward handle
1134 npos = b[1].code == NR_CURVETO ? b[1].c(1) * i2d : pos; // forward handle
1135 // Determine whether a node is at a smooth part of the path, by
1136 // calculating a measure for the collinearity of the handles
1137 bool c1 = fabs (Inkscape::Util::triangle_area (pos, ppos, npos)) < 1; // points are (almost) collinear
1138 bool c2 = NR::L2(pos - ppos) < 1e-6 || NR::L2(pos - npos) < 1e-6; // endnode, or a node with a retracted handle
1139 if (!(c1 & !c2)) {
1140 *p = pos; // only return non-smooth nodes ("cusps")
1141 }
1142 }
1143 }
1145 b++;
1146 }
1147 }
1150 LivePathEffectObject *
1151 sp_shape_get_livepatheffectobject(SPShape *shape) {
1152 if (!shape) return NULL;
1154 if (sp_shape_has_path_effect(shape)) {
1155 return shape->path_effect_ref->lpeobject;
1156 } else {
1157 return NULL;
1158 }
1159 }
1161 Inkscape::LivePathEffect::Effect *
1162 sp_shape_get_livepatheffect(SPShape *shape) {
1163 if (!shape) return NULL;
1165 LivePathEffectObject * lpeobj = sp_shape_get_livepatheffectobject(shape);
1166 if (lpeobj)
1167 return lpeobj->lpe;
1168 else
1169 return NULL;
1170 }
1172 /**
1173 * Calls any registered handlers for the update_patheffect action
1174 */
1175 void
1176 sp_shape_update_patheffect (SPShape *shape, bool write)
1177 {
1178 #ifdef SHAPE_VERBOSE
1179 g_message("sp_shape_update_patheffect: %p\n", shape);
1180 #endif
1181 g_return_if_fail (shape != NULL);
1182 g_return_if_fail (SP_IS_SHAPE (shape));
1184 if (SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->update_patheffect) {
1185 SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->update_patheffect (shape, write);
1186 }
1187 }
1189 void sp_shape_perform_path_effect(SPCurve *curve, SPShape *shape) {
1190 if (!shape) return;
1191 if (!curve) return;
1193 LivePathEffectObject *lpeobj = sp_shape_get_livepatheffectobject(shape);
1194 if (lpeobj && lpeobj->lpe) {
1195 lpeobj->lpe->doEffect(curve);
1196 }
1197 }
1199 /**
1200 * Gets called when (re)attached to another lpeobject.
1201 */
1202 static void
1203 lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPShape *shape)
1204 {
1205 if (old_ref) {
1206 sp_signal_disconnect_by_data(old_ref, shape);
1207 }
1208 if ( IS_LIVEPATHEFFECT(ref) && ref != shape )
1209 {
1210 shape->lpe_modified_connection.disconnect();
1211 shape->lpe_modified_connection = ref->connectModified(sigc::bind(sigc::ptr_fun(&lpeobject_ref_modified), shape));
1212 lpeobject_ref_modified(ref, 0, shape);
1213 }
1214 }
1216 /**
1217 * Gets called when lpeobject repr contents change: i.e. parameter change.
1218 */
1219 static void
1220 lpeobject_ref_modified(SPObject */*href*/, guint /*flags*/, SPShape *shape)
1221 {
1222 sp_shape_update_patheffect (shape, true);
1223 }
1225 void sp_shape_set_path_effect(SPShape *shape, gchar *value)
1226 {
1227 if (!value) {
1228 sp_shape_remove_path_effect(shape);
1229 } else {
1230 SP_OBJECT_REPR(shape)->setAttribute("inkscape:path-effect", value);
1231 }
1232 }
1234 void sp_shape_set_path_effect(SPShape *shape, LivePathEffectObject * new_lpeobj)
1235 {
1236 const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id");
1237 gchar *hrefstr = g_strdup_printf("#%s", repr_id);
1238 sp_shape_set_path_effect(shape, hrefstr);
1239 g_free(hrefstr);
1240 }
1242 void sp_shape_remove_path_effect(SPShape *shape)
1243 {
1244 Inkscape::XML::Node *repr = SP_OBJECT_REPR(shape);
1245 repr->setAttribute("inkscape:path-effect", NULL);
1246 if (SP_IS_PATH(shape)) {
1247 repr->setAttribute("d", repr->attribute("inkscape:original-d"));
1248 repr->setAttribute("inkscape:original-d", NULL);
1249 }
1250 }
1252 bool sp_shape_has_path_effect(SPShape *shape)
1253 {
1254 return (shape->path_effect_href != NULL);
1255 }
1257 void sp_shape_edit_next_param_oncanvas(SPShape *shape, SPDesktop *dt)
1258 {
1259 LivePathEffectObject *lpeobj = sp_shape_get_livepatheffectobject(shape);
1260 if (lpeobj && lpeobj->lpe) {
1261 lpeobj->lpe->editNextParamOncanvas(SP_ITEM(shape), dt);
1262 }
1263 }
1265 /*
1266 Local Variables:
1267 mode:c++
1268 c-file-style:"stroustrup"
1269 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1270 indent-tabs-mode:nil
1271 fill-column:99
1272 End:
1273 */
1274 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :