1 /*
2 * Base class for shapes, including <path> element
3 *
4 * Author:
5 * Lauris Kaplinski <lauris@kaplinski.com>
6 * Abhishek Sharma
7 *
8 * Copyright (C) 1999-2002 Lauris Kaplinski
9 * Copyright (C) 2000-2001 Ximian, Inc.
10 * Copyright (C) 2004 John Cliff
11 * Copyright (C) 2007-2008 Johan Engelen
12 * Copyright (C) 2010 Jon A. Cruz <jon@joncruz.org>
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
21 #include <libnr/nr-matrix-fns.h>
22 #include <libnr/nr-matrix-ops.h>
23 #include <libnr/nr-matrix-translate-ops.h>
24 #include <libnr/nr-scale-matrix-ops.h>
25 #include <2geom/rect.h>
26 #include <2geom/transforms.h>
27 #include <2geom/pathvector.h>
28 #include <2geom/path-intersection.h>
29 #include <2geom/exception.h>
30 #include "helper/geom.h"
31 #include "helper/geom-nodetype.h"
33 #include <sigc++/functors/ptr_fun.h>
34 #include <sigc++/adaptors/bind.h>
36 #include "macros.h"
37 #include "display/nr-arena-shape.h"
38 #include "display/curve.h"
39 #include "print.h"
40 #include "document.h"
41 #include "style.h"
42 #include "marker.h"
43 #include "sp-path.h"
44 #include "preferences.h"
45 #include "attributes.h"
47 #include "live_effects/lpeobject.h"
48 #include "uri.h"
49 #include "extract-uri.h"
50 #include "uri-references.h"
51 #include "bad-uri-exception.h"
52 #include "xml/repr.h"
54 #include "util/mathfns.h" // for triangle_area()
56 #include "splivarot.h" // for bounding box calculation
58 #define noSHAPE_VERBOSE
60 void sp_shape_print (SPItem * item, SPPrintContext * ctx);
62 SPLPEItemClass * SPShapeClass::parent_class = 0;
64 /**
65 * Registers the SPShape class with Gdk and returns its type number.
66 */
67 GType SPShape::getType(void)
68 {
69 static GType type = 0;
70 if (!type) {
71 GTypeInfo info = {
72 sizeof (SPShapeClass),
73 NULL, NULL,
74 (GClassInitFunc) SPShapeClass::sp_shape_class_init,
75 NULL, NULL,
76 sizeof (SPShape),
77 16,
78 (GInstanceInitFunc) sp_shape_init,
79 NULL, /* value_table */
80 };
81 type = g_type_register_static (SP_TYPE_LPE_ITEM, "SPShape", &info, (GTypeFlags)0);
82 }
83 return type;
84 }
86 /**
87 * Initializes a SPShapeClass object. Establishes the function pointers to the class'
88 * member routines in the class vtable, and sets pointers to parent classes.
89 */
90 void SPShapeClass::sp_shape_class_init(SPShapeClass *klass)
91 {
92 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
93 SPObjectClass *sp_object_class = SP_OBJECT_CLASS(klass);
94 SPItemClass * item_class = SP_ITEM_CLASS(klass);
95 SPLPEItemClass * lpe_item_class = SP_LPE_ITEM_CLASS(klass);
97 parent_class = (SPLPEItemClass *)g_type_class_peek_parent (klass);
99 gobject_class->finalize = SPShape::sp_shape_finalize;
101 sp_object_class->build = SPShape::sp_shape_build;
102 sp_object_class->release = SPShape::sp_shape_release;
103 sp_object_class->set = SPShape::sp_shape_set;
104 sp_object_class->update = SPShape::sp_shape_update;
105 sp_object_class->modified = SPShape::sp_shape_modified;
106 sp_object_class->write = SPShape::sp_shape_write;
108 item_class->bbox = SPShape::sp_shape_bbox;
109 item_class->print = sp_shape_print;
110 item_class->show = SPShape::sp_shape_show;
111 item_class->hide = SPShape::sp_shape_hide;
112 item_class->snappoints = SPShape::sp_shape_snappoints;
113 lpe_item_class->update_patheffect = NULL;
115 klass->set_shape = NULL;
116 }
118 /**
119 * Initializes an SPShape object.
120 */
121 void SPShape::sp_shape_init(SPShape *shape)
122 {
123 for ( int i = 0 ; i < SP_MARKER_LOC_QTY ; i++ ) {
124 new (&shape->release_connect[i]) sigc::connection();
125 new (&shape->modified_connect[i]) sigc::connection();
126 shape->marker[i] = NULL;
127 }
128 shape->curve = NULL;
129 }
131 void SPShape::sp_shape_finalize(GObject *object)
132 {
133 SPShape *shape=(SPShape *)object;
135 for ( int i = 0 ; i < SP_MARKER_LOC_QTY ; i++ ) {
136 shape->release_connect[i].disconnect();
137 shape->release_connect[i].~connection();
138 shape->modified_connect[i].disconnect();
139 shape->modified_connect[i].~connection();
140 }
142 if (((GObjectClass *) (SPShapeClass::parent_class))->finalize) {
143 (* ((GObjectClass *) (SPShapeClass::parent_class))->finalize)(object);
144 }
145 }
147 /**
148 * Virtual build callback for SPMarker.
149 *
150 * This is to be invoked immediately after creation of an SPShape.
151 *
152 * \see sp_object_build()
153 */
154 void SPShape::sp_shape_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
155 {
156 if (((SPObjectClass *) (SPShapeClass::parent_class))->build) {
157 (*((SPObjectClass *) (SPShapeClass::parent_class))->build) (object, document, repr);
158 }
160 for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
161 sp_shape_set_marker (object, i, object->style->marker[i].value);
162 }
163 }
165 /**
166 * Removes, releases and unrefs all children of object
167 *
168 * This is the inverse of sp_shape_build(). It must be invoked as soon
169 * as the shape is removed from the tree, even if it is still referenced
170 * by other objects. This routine also disconnects/unrefs markers and
171 * curves attached to it.
172 *
173 * \see sp_object_release()
174 */
175 void SPShape::sp_shape_release(SPObject *object)
176 {
177 SPItem *item;
178 SPShape *shape;
179 SPItemView *v;
180 int i;
182 item = (SPItem *) object;
183 shape = (SPShape *) object;
185 for (i = 0; i < SP_MARKER_LOC_QTY; i++) {
186 if (shape->marker[i]) {
187 for (v = item->display; v != NULL; v = v->next) {
188 sp_marker_hide ((SPMarker *) shape->marker[i], NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
189 }
190 shape->release_connect[i].disconnect();
191 shape->modified_connect[i].disconnect();
192 shape->marker[i] = sp_object_hunref (shape->marker[i], object);
193 }
194 }
195 if (shape->curve) {
196 shape->curve = shape->curve->unref();
197 }
199 if (((SPObjectClass *) SPShapeClass::parent_class)->release) {
200 ((SPObjectClass *) SPShapeClass::parent_class)->release (object);
201 }
202 }
206 void SPShape::sp_shape_set(SPObject *object, unsigned int key, gchar const *value)
207 {
208 if (((SPObjectClass *) SPShapeClass::parent_class)->set) {
209 ((SPObjectClass *) SPShapeClass::parent_class)->set(object, key, value);
210 }
211 }
213 Inkscape::XML::Node * SPShape::sp_shape_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags)
214 {
215 if (((SPObjectClass *)(SPShapeClass::parent_class))->write) {
216 ((SPObjectClass *)(SPShapeClass::parent_class))->write(object, doc, repr, flags);
217 }
219 return repr;
220 }
222 /**
223 * Updates the shape when its attributes have changed. Also establishes
224 * marker objects to match the style settings.
225 */
226 void SPShape::sp_shape_update(SPObject *object, SPCtx *ctx, unsigned int flags)
227 {
228 SPItem *item = (SPItem *) object;
229 SPShape *shape = (SPShape *) object;
231 if (((SPObjectClass *) (SPShapeClass::parent_class))->update) {
232 (* ((SPObjectClass *) (SPShapeClass::parent_class))->update) (object, ctx, flags);
233 }
235 /* This stanza checks that an object's marker style agrees with
236 * the marker objects it has allocated. sp_shape_set_marker ensures
237 * that the appropriate marker objects are present (or absent) to
238 * match the style.
239 */
240 for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
241 sp_shape_set_marker (object, i, object->style->marker[i].value);
242 }
244 if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
245 SPStyle *style;
246 style = SP_OBJECT_STYLE (object);
247 if (style->stroke_width.unit == SP_CSS_UNIT_PERCENT) {
248 SPItemCtx *ictx = (SPItemCtx *) ctx;
249 double const aw = 1.0 / NR::expansion(ictx->i2vp);
250 style->stroke_width.computed = style->stroke_width.value * aw;
251 for (SPItemView *v = ((SPItem *) (shape))->display; v != NULL; v = v->next) {
252 nr_arena_shape_set_style ((NRArenaShape *) v->arenaitem, style);
253 }
254 }
255 }
257 if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) {
258 /* This is suboptimal, because changing parent style schedules recalculation */
259 /* But on the other hand - how can we know that parent does not tie style and transform */
260 Geom::OptRect paintbox = SP_ITEM(object)->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
261 for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
262 NRArenaShape * const s = NR_ARENA_SHAPE(v->arenaitem);
263 if (flags & SP_OBJECT_MODIFIED_FLAG) {
264 nr_arena_shape_set_path(s, shape->curve, (flags & SP_OBJECT_USER_MODIFIED_FLAG_B));
265 }
266 if (paintbox) {
267 s->setPaintBox(*paintbox);
268 }
269 }
270 }
272 if (shape->hasMarkers ()) {
273 /* Dimension marker views */
274 for (SPItemView *v = item->display; v != NULL; v = v->next) {
275 if (!v->arenaitem->key) {
276 NR_ARENA_ITEM_SET_KEY (v->arenaitem, SPItem::display_key_new (SP_MARKER_LOC_QTY));
277 }
278 for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
279 if (shape->marker[i]) {
280 sp_marker_show_dimension ((SPMarker *) shape->marker[i],
281 NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i,
282 shape->numberOfMarkers (i));
283 }
284 }
285 }
287 /* Update marker views */
288 for (SPItemView *v = item->display; v != NULL; v = v->next) {
289 sp_shape_update_marker_view (shape, v->arenaitem);
290 }
291 }
292 }
294 /**
295 * Calculate the transform required to get a marker's path object in the
296 * right place for particular path segment on a shape.
297 *
298 * \see sp_shape_marker_update_marker_view.
299 *
300 * From SVG spec:
301 * The axes of the temporary new user coordinate system are aligned according to the orient attribute on the 'marker'
302 * element and the slope of the curve at the given vertex. (Note: if there is a discontinuity at a vertex, the slope
303 * is the average of the slopes of the two segments of the curve that join at the given vertex. If a slope cannot be
304 * determined, the slope is assumed to be zero.)
305 *
306 * Reference: http://www.w3.org/TR/SVG11/painting.html#MarkerElement, the `orient' attribute.
307 * Reference for behaviour of zero-length segments:
308 * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
309 */
310 Geom::Matrix sp_shape_marker_get_transform(Geom::Curve const & c1, Geom::Curve const & c2)
311 {
312 Geom::Point p = c1.pointAt(1);
313 Geom::Curve * c1_reverse = c1.reverse();
314 Geom::Point tang1 = - c1_reverse->unitTangentAt(0);
315 delete c1_reverse;
316 Geom::Point tang2 = c2.unitTangentAt(0);
318 double const angle1 = Geom::atan2(tang1);
319 double const angle2 = Geom::atan2(tang2);
321 double ret_angle;
322 ret_angle = .5 * (angle1 + angle2);
324 if ( fabs( angle2 - angle1 ) > M_PI ) {
325 /* ret_angle is in the middle of the larger of the two sectors between angle1 and
326 * angle2, so flip it by 180degrees to force it to the middle of the smaller sector.
327 *
328 * (Imagine a circle with rays drawn at angle1 and angle2 from the centre of the
329 * circle. Those two rays divide the circle into two sectors.)
330 */
331 ret_angle += M_PI;
332 }
334 return Geom::Rotate(ret_angle) * Geom::Translate(p);
335 }
337 Geom::Matrix sp_shape_marker_get_transform_at_start(Geom::Curve const & c)
338 {
339 Geom::Point p = c.pointAt(0);
340 Geom::Matrix ret = Geom::Translate(p);
342 if ( !c.isDegenerate() ) {
343 Geom::Point tang = c.unitTangentAt(0);
344 double const angle = Geom::atan2(tang);
345 ret = Geom::Rotate(angle) * Geom::Translate(p);
346 } else {
347 /* FIXME: the svg spec says to search for a better alternative than zero angle directionality:
348 * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes */
349 }
351 return ret;
352 }
354 Geom::Matrix sp_shape_marker_get_transform_at_end(Geom::Curve const & c)
355 {
356 Geom::Point p = c.pointAt(1);
357 Geom::Matrix ret = Geom::Translate(p);
359 if ( !c.isDegenerate() ) {
360 Geom::Curve * c_reverse = c.reverse();
361 Geom::Point tang = - c_reverse->unitTangentAt(0);
362 delete c_reverse;
363 double const angle = Geom::atan2(tang);
364 ret = Geom::Rotate(angle) * Geom::Translate(p);
365 } else {
366 /* FIXME: the svg spec says to search for a better alternative than zero angle directionality:
367 * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes */
368 }
370 return ret;
371 }
373 /**
374 * Updates the instances (views) of a given marker in a shape.
375 * Marker views have to be scaled already. The transformation
376 * is retrieved and then shown by calling sp_marker_show_instance.
377 *
378 * @todo figure out what to do when both 'marker' and for instance 'marker-end' are set.
379 */
380 void SPShape::sp_shape_update_marker_view(SPShape *shape, NRArenaItem *ai)
381 {
382 SPStyle *style = ((SPObject *) shape)->style;
384 // position arguments to sp_marker_show_instance, basically counts the amount of markers.
385 int counter[4] = {0};
387 if (!shape->curve) return;
388 Geom::PathVector const & pathv = shape->curve->get_pathvector();
389 if (pathv.empty()) return;
391 // the first vertex should get a start marker, the last an end marker, and all the others a mid marker
392 // see bug 456148
394 // START marker
395 {
396 Geom::Matrix const m (sp_shape_marker_get_transform_at_start(pathv.begin()->front()));
397 for (int i = 0; i < 2; i++) { // SP_MARKER_LOC and SP_MARKER_LOC_START
398 if ( shape->marker[i] ) {
399 sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
400 NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
401 style->stroke_width.computed);
402 counter[i]++;
403 }
404 }
405 }
407 // MID marker
408 if (shape->marker[SP_MARKER_LOC_MID] || shape->marker[SP_MARKER_LOC]) {
409 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
410 // START position
411 if ( path_it != pathv.begin()
412 && ! ((path_it == (pathv.end()-1)) && (path_it->size_default() == 0)) ) // if this is the last path and it is a moveto-only, don't draw mid marker there
413 {
414 Geom::Matrix const m (sp_shape_marker_get_transform_at_start(path_it->front()));
415 for (int i = 0; i < 3; i += 2) { // SP_MARKER_LOC and SP_MARKER_LOC_MID
416 if ( shape->marker[i] ) {
417 sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
418 NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
419 style->stroke_width.computed);
420 counter[i]++;
421 }
422 }
423 }
424 // MID position
425 if ( path_it->size_default() > 1) {
426 Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve
427 Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); // outgoing curve
428 while (curve_it2 != path_it->end_default())
429 {
430 /* Put marker between curve_it1 and curve_it2.
431 * Loop to end_default (so including closing segment), because when a path is closed,
432 * there should be a midpoint marker between last segment and closing straight line segment
433 */
434 Geom::Matrix const m (sp_shape_marker_get_transform(*curve_it1, *curve_it2));
435 for (int i = 0; i < 3; i += 2) { // SP_MARKER_LOC and SP_MARKER_LOC_MID
436 if (shape->marker[i]) {
437 sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
438 NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
439 style->stroke_width.computed);
440 counter[i]++;
441 }
442 }
444 ++curve_it1;
445 ++curve_it2;
446 }
447 }
448 // END position
449 if ( path_it != (pathv.end()-1) && !path_it->empty()) {
450 Geom::Curve const &lastcurve = path_it->back_default();
451 Geom::Matrix const m = sp_shape_marker_get_transform_at_end(lastcurve);
452 for (int i = 0; i < 3; i += 2) { // SP_MARKER_LOC and SP_MARKER_LOC_MID
453 if (shape->marker[i]) {
454 sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
455 NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
456 style->stroke_width.computed);
457 counter[i]++;
458 }
459 }
460 }
461 }
462 }
464 // END marker
465 if ( shape->marker[SP_MARKER_LOC_END] || shape->marker[SP_MARKER_LOC] ) {
466 /* Get reference to last curve in the path.
467 * For moveto-only path, this returns the "closing line segment". */
468 Geom::Path const &path_last = pathv.back();
469 unsigned int index = path_last.size_default();
470 if (index > 0) {
471 index--;
472 }
473 Geom::Curve const &lastcurve = path_last[index];
474 Geom::Matrix const m = sp_shape_marker_get_transform_at_end(lastcurve);
476 for (int i = 0; i < 4; i += 3) { // SP_MARKER_LOC and SP_MARKER_LOC_END
477 if (shape->marker[i]) {
478 sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
479 NR_ARENA_ITEM_GET_KEY(ai) + i, counter[i], m,
480 style->stroke_width.computed);
481 counter[i]++;
482 }
483 }
484 }
485 }
487 /**
488 * Sets modified flag for all sub-item views.
489 */
490 void SPShape::sp_shape_modified(SPObject *object, unsigned int flags)
491 {
492 SPShape *shape = SP_SHAPE (object);
494 if (((SPObjectClass *) (SPShapeClass::parent_class))->modified) {
495 (* ((SPObjectClass *) (SPShapeClass::parent_class))->modified) (object, flags);
496 }
498 if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
499 for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
500 nr_arena_shape_set_style (NR_ARENA_SHAPE (v->arenaitem), object->style);
501 }
502 }
503 }
505 /**
506 * Calculates the bounding box for item, storing it into bbox.
507 * This also includes the bounding boxes of any markers included in the shape.
508 */
509 void SPShape::sp_shape_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags)
510 {
511 SPShape const *shape = SP_SHAPE (item);
512 if (shape->curve) {
513 Geom::OptRect geombbox = bounds_exact_transformed(shape->curve->get_pathvector(), transform);
514 if (geombbox) {
515 NRRect cbbox;
516 cbbox.x0 = (*geombbox)[0][0];
517 cbbox.y0 = (*geombbox)[1][0];
518 cbbox.x1 = (*geombbox)[0][1];
519 cbbox.y1 = (*geombbox)[1][1];
521 switch ((SPItem::BBoxType) flags) {
522 case SPItem::GEOMETRIC_BBOX: {
523 // do nothing
524 break;
525 }
526 case SPItem::RENDERING_BBOX: {
527 // convert the stroke to a path and calculate that path's geometric bbox
528 SPStyle* style=SP_OBJECT_STYLE (item);
529 if (!style->stroke.isNone()) {
530 Geom::PathVector *pathv = item_outline(item);
531 if (pathv) {
532 Geom::OptRect geomstrokebbox = bounds_exact_transformed(*pathv, transform);
533 if (geomstrokebbox) {
534 NRRect strokebbox;
535 strokebbox.x0 = (*geomstrokebbox)[0][0];
536 strokebbox.y0 = (*geomstrokebbox)[1][0];
537 strokebbox.x1 = (*geomstrokebbox)[0][1];
538 strokebbox.y1 = (*geomstrokebbox)[1][1];
539 nr_rect_d_union (&cbbox, &cbbox, &strokebbox);
540 }
541 delete pathv;
542 }
543 }
544 break;
545 }
546 default:
547 case SPItem::APPROXIMATE_BBOX: {
548 SPStyle* style=SP_OBJECT_STYLE (item);
549 if (!style->stroke.isNone()) {
550 double const scale = transform.descrim();
551 if ( fabs(style->stroke_width.computed * scale) > 0.01 ) { // sinon c'est 0=oon veut pas de bord
552 double const width = MAX(0.125, style->stroke_width.computed * scale);
553 if ( fabs(cbbox.x1-cbbox.x0) > -0.00001 && fabs(cbbox.y1-cbbox.y0) > -0.00001 ) {
554 cbbox.x0-=0.5*width;
555 cbbox.x1+=0.5*width;
556 cbbox.y0-=0.5*width;
557 cbbox.y1+=0.5*width;
558 }
559 }
560 }
562 // Union with bboxes of the markers, if any
563 if ( shape->hasMarkers() && !shape->curve->get_pathvector().empty() ) {
564 /** \todo make code prettier! */
565 Geom::PathVector const & pathv = shape->curve->get_pathvector();
566 // START marker
567 for (unsigned i = 0; i < 2; i++) { // SP_MARKER_LOC and SP_MARKER_LOC_START
568 if ( shape->marker[i] ) {
569 SPMarker* marker = SP_MARKER (shape->marker[i]);
570 SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
572 if (marker_item) {
573 Geom::Matrix tr(sp_shape_marker_get_transform_at_start(pathv.begin()->front()));
574 if (!marker->orient_auto) {
575 Geom::Point transl = tr.translation();
576 tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
577 }
578 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
579 tr = Geom::Scale(style->stroke_width.computed) * tr;
580 }
582 // total marker transform
583 tr = marker_item->transform * marker->c2p * tr * transform;
585 // get bbox of the marker with that transform
586 NRRect marker_bbox;
587 marker_item->invoke_bbox ( &marker_bbox, from_2geom(tr), true);
588 // union it with the shape bbox
589 nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
590 }
591 }
592 }
593 // MID marker
594 for (unsigned i = 0; i < 3; i += 2) { // SP_MARKER_LOC and SP_MARKER_LOC_MID
595 SPMarker* marker = SP_MARKER (shape->marker[i]);
596 if ( !shape->marker[i] ) continue;
597 SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
598 if ( !marker_item ) continue;
600 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
601 // START position
602 if ( path_it != pathv.begin()
603 && ! ((path_it == (pathv.end()-1)) && (path_it->size_default() == 0)) ) // if this is the last path and it is a moveto-only, there is no mid marker there
604 {
605 Geom::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front()));
606 if (!marker->orient_auto) {
607 Geom::Point transl = tr.translation();
608 tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
609 }
610 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
611 tr = Geom::Scale(style->stroke_width.computed) * tr;
612 }
613 tr = marker_item->transform * marker->c2p * tr * transform;
614 NRRect marker_bbox;
615 marker_item->invoke_bbox ( &marker_bbox, from_2geom(tr), true);
616 nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
617 }
618 // MID position
619 if ( path_it->size_default() > 1) {
620 Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve
621 Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); // outgoing curve
622 while (curve_it2 != path_it->end_default())
623 {
624 /* Put marker between curve_it1 and curve_it2.
625 * Loop to end_default (so including closing segment), because when a path is closed,
626 * there should be a midpoint marker between last segment and closing straight line segment */
628 SPMarker* marker = SP_MARKER (shape->marker[i]);
629 SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
631 if (marker_item) {
632 Geom::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
633 if (!marker->orient_auto) {
634 Geom::Point transl = tr.translation();
635 tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
636 }
637 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
638 tr = Geom::Scale(style->stroke_width.computed) * tr;
639 }
640 tr = marker_item->transform * marker->c2p * tr * transform;
641 NRRect marker_bbox;
642 marker_item->invoke_bbox ( &marker_bbox, from_2geom(tr), true);
643 nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
644 }
646 ++curve_it1;
647 ++curve_it2;
648 }
649 }
650 // END position
651 if ( path_it != (pathv.end()-1) && !path_it->empty()) {
652 Geom::Curve const &lastcurve = path_it->back_default();
653 Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
654 if (!marker->orient_auto) {
655 Geom::Point transl = tr.translation();
656 tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
657 }
658 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
659 tr = Geom::Scale(style->stroke_width.computed) * tr;
660 }
661 tr = marker_item->transform * marker->c2p * tr * transform;
662 NRRect marker_bbox;
663 marker_item->invoke_bbox ( &marker_bbox, tr, true);
664 nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
665 }
666 }
667 }
668 // END marker
669 for (unsigned i = 0; i < 4; i += 3) { // SP_MARKER_LOC and SP_MARKER_LOC_END
670 if ( shape->marker[i] ) {
671 SPMarker* marker = SP_MARKER (shape->marker[i]);
672 SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
674 if (marker_item) {
675 /* Get reference to last curve in the path.
676 * For moveto-only path, this returns the "closing line segment". */
677 Geom::Path const &path_last = pathv.back();
678 unsigned int index = path_last.size_default();
679 if (index > 0) {
680 index--;
681 }
682 Geom::Curve const &lastcurve = path_last[index];
684 Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
685 if (!marker->orient_auto) {
686 Geom::Point transl = tr.translation();
687 tr = Geom::Rotate::from_degrees(marker->orient) * Geom::Translate(transl);
688 }
689 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
690 tr = Geom::Scale(style->stroke_width.computed) * tr;
691 }
693 // total marker transform
694 tr = marker_item->transform * marker->c2p * tr * transform;
696 // get bbox of the marker with that transform
697 NRRect marker_bbox;
698 marker_item->invoke_bbox ( &marker_bbox, tr, true);
699 // union it with the shape bbox
700 nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
701 }
702 }
703 }
704 }
705 break;
706 } // end case approximate bbox type
707 } // end switch bboxtype
709 // copy our bbox to the variable we're given
710 *bbox = cbbox;
711 }
712 }
713 }
715 static void
716 sp_shape_print_invoke_marker_printing(SPObject* obj, Geom::Matrix tr, SPStyle* style, SPPrintContext *ctx) {
717 SPMarker *marker = SP_MARKER(obj);
718 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
719 tr = Geom::Scale(style->stroke_width.computed) * tr;
720 }
722 SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker));
723 tr = marker_item->transform * marker->c2p * tr;
725 Geom::Matrix old_tr = marker_item->transform;
726 marker_item->transform = tr;
727 marker_item->invoke_print (ctx);
728 marker_item->transform = old_tr;
729 }
730 /**
731 * Prepares shape for printing. Handles printing of comments for printing
732 * debugging, sizes the item to fit into the document width/height,
733 * applies print fill/stroke, sets transforms for markers, and adds
734 * comment labels.
735 */
736 void
737 sp_shape_print (SPItem *item, SPPrintContext *ctx)
738 {
739 NRRect pbox, dbox, bbox;
741 SPShape *shape = SP_SHAPE(item);
743 if (!shape->curve) return;
745 Geom::PathVector const & pathv = shape->curve->get_pathvector();
746 if (pathv.empty()) return;
748 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
749 gint add_comments = prefs->getBool("/printing/debug/add-label-comments");
750 if (add_comments) {
751 gchar * comment = g_strdup_printf("begin '%s'",
752 SP_OBJECT(item)->defaultLabel());
753 sp_print_comment(ctx, comment);
754 g_free(comment);
755 }
757 /* fixme: Think (Lauris) */
758 item->invoke_bbox( &pbox, Geom::identity(), TRUE);
759 dbox.x0 = 0.0;
760 dbox.y0 = 0.0;
761 dbox.x1 = SP_OBJECT_DOCUMENT (item)->getWidth ();
762 dbox.y1 = SP_OBJECT_DOCUMENT (item)->getHeight ();
763 item->getBboxDesktop (&bbox);
764 Geom::Matrix const i2d(item->i2d_affine());
766 SPStyle* style = SP_OBJECT_STYLE (item);
768 if (!style->fill.isNone()) {
769 sp_print_fill (ctx, pathv, &i2d, style, &pbox, &dbox, &bbox);
770 }
772 if (!style->stroke.isNone()) {
773 sp_print_stroke (ctx, pathv, &i2d, style, &pbox, &dbox, &bbox);
774 }
776 /** \todo make code prettier */
777 // START marker
778 for (int i = 0; i < 2; i++) { // SP_MARKER_LOC and SP_MARKER_LOC_START
779 if ( shape->marker[i] ) {
780 Geom::Matrix tr(sp_shape_marker_get_transform_at_start(pathv.begin()->front()));
781 sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
782 }
783 }
784 // MID marker
785 for (int i = 0; i < 3; i += 2) { // SP_MARKER_LOC and SP_MARKER_LOC_MID
786 if (shape->marker[i]) {
787 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
788 // START position
789 if ( path_it != pathv.begin()
790 && ! ((path_it == (pathv.end()-1)) && (path_it->size_default() == 0)) ) // if this is the last path and it is a moveto-only, there is no mid marker there
791 {
792 Geom::Matrix tr(sp_shape_marker_get_transform_at_start(path_it->front()));
793 sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
794 }
795 // MID position
796 if ( path_it->size_default() > 1) {
797 Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve
798 Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); // outgoing curve
799 while (curve_it2 != path_it->end_default())
800 {
801 /* Put marker between curve_it1 and curve_it2.
802 * Loop to end_default (so including closing segment), because when a path is closed,
803 * there should be a midpoint marker between last segment and closing straight line segment */
804 Geom::Matrix tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
806 sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
808 ++curve_it1;
809 ++curve_it2;
810 }
811 }
812 if ( path_it != (pathv.end()-1) && !path_it->empty()) {
813 Geom::Curve const &lastcurve = path_it->back_default();
814 Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
815 sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
816 }
817 }
818 }
819 }
820 // END marker
821 if ( shape->marker[SP_MARKER_LOC_END] || shape->marker[SP_MARKER_LOC]) {
822 /* Get reference to last curve in the path.
823 * For moveto-only path, this returns the "closing line segment". */
824 Geom::Path const &path_last = pathv.back();
825 unsigned int index = path_last.size_default();
826 if (index > 0) {
827 index--;
828 }
829 Geom::Curve const &lastcurve = path_last[index];
831 Geom::Matrix tr = sp_shape_marker_get_transform_at_end(lastcurve);
833 for (int i = 0; i < 4; i += 3) { // SP_MARKER_LOC and SP_MARKER_LOC_END
834 if (shape->marker[i]) {
835 sp_shape_print_invoke_marker_printing(shape->marker[i], tr, style, ctx);
836 }
837 }
838 }
840 if (add_comments) {
841 gchar * comment = g_strdup_printf("end '%s'",
842 SP_OBJECT(item)->defaultLabel());
843 sp_print_comment(ctx, comment);
844 g_free(comment);
845 }
846 }
848 /**
849 * Sets style, path, and paintbox. Updates marker views, including dimensions.
850 */
851 NRArenaItem * SPShape::sp_shape_show(SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int /*flags*/)
852 {
853 SPObject *object = SP_OBJECT(item);
854 SPShape *shape = SP_SHAPE(item);
856 NRArenaItem *arenaitem = NRArenaShape::create(arena);
857 NRArenaShape * const s = NR_ARENA_SHAPE(arenaitem);
858 nr_arena_shape_set_style(s, object->style);
859 nr_arena_shape_set_path(s, shape->curve, false);
860 Geom::OptRect paintbox = item->getBounds(Geom::identity());
861 if (paintbox) {
862 s->setPaintBox(*paintbox);
863 }
865 /* This stanza checks that an object's marker style agrees with
866 * the marker objects it has allocated. sp_shape_set_marker ensures
867 * that the appropriate marker objects are present (or absent) to
868 * match the style.
869 */
870 for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
871 sp_shape_set_marker (object, i, object->style->marker[i].value);
872 }
874 if (shape->hasMarkers ()) {
876 /* provide key and dimension the marker views */
877 if (!arenaitem->key) {
878 NR_ARENA_ITEM_SET_KEY (arenaitem, SPItem::display_key_new (SP_MARKER_LOC_QTY));
879 }
881 for (int i = 0; i < SP_MARKER_LOC_QTY; i++) {
882 if (shape->marker[i]) {
883 sp_marker_show_dimension ((SPMarker *) shape->marker[i],
884 NR_ARENA_ITEM_GET_KEY (arenaitem) + i,
885 shape->numberOfMarkers (i));
886 }
887 }
889 /* Update marker views */
890 sp_shape_update_marker_view (shape, arenaitem);
891 }
893 return arenaitem;
894 }
896 /**
897 * Hides/removes marker views from the shape.
898 */
899 void SPShape::sp_shape_hide(SPItem *item, unsigned int key)
900 {
901 SPShape *shape;
902 SPItemView *v;
903 int i;
905 shape = (SPShape *) item;
907 for (i=0; i<SP_MARKER_LOC_QTY; i++) {
908 if (shape->marker[i]) {
909 for (v = item->display; v != NULL; v = v->next) {
910 if (key == v->key) {
911 sp_marker_hide ((SPMarker *) shape->marker[i],
912 NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
913 }
914 }
915 }
916 }
918 if (((SPItemClass *) SPShapeClass::parent_class)->hide) {
919 ((SPItemClass *) SPShapeClass::parent_class)->hide (item, key);
920 }
921 }
923 /**
924 * \param shape Shape.
925 * \return TRUE if the shape has any markers, or FALSE if not.
926 */
927 int SPShape::hasMarkers() const
928 {
929 /* Note, we're ignoring 'marker' settings, which technically should apply for
930 all three settings. This should be fixed later such that if 'marker' is
931 specified, then all three should appear. */
933 return (
934 this->curve &&
935 (this->marker[SP_MARKER_LOC] ||
936 this->marker[SP_MARKER_LOC_START] ||
937 this->marker[SP_MARKER_LOC_MID] ||
938 this->marker[SP_MARKER_LOC_END])
939 );
940 }
943 /**
944 * \param shape Shape.
945 * \param type Marker type (e.g. SP_MARKER_LOC_START)
946 * \return Number of markers that the shape has of this type.
947 */
948 int SPShape::numberOfMarkers(int type)
949 {
950 Geom::PathVector const & pathv = this->curve->get_pathvector();
951 if (pathv.size() == 0) {
952 return 0;
953 }
955 switch(type) {
956 case SP_MARKER_LOC:
957 {
958 if ( this->marker[SP_MARKER_LOC] ) {
959 guint n = 0;
960 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
961 n += path_it->size_default() + 1;
962 }
963 return n;
964 } else {
965 return 0;
966 }
967 }
968 case SP_MARKER_LOC_START:
969 // there is only a start marker on the first path of a pathvector
970 return this->marker[SP_MARKER_LOC_START] ? 1 : 0;
972 case SP_MARKER_LOC_MID:
973 {
974 if ( this->marker[SP_MARKER_LOC_MID] ) {
975 guint n = 0;
976 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
977 n += path_it->size_default() + 1;
978 }
979 return n - 2; // minus the start and end marker.
980 } else {
981 return 0;
982 }
983 }
985 case SP_MARKER_LOC_END:
986 {
987 // there is only an end marker on the last path of a pathvector
988 return this->marker[SP_MARKER_LOC_END] ? 1 : 0;
989 }
991 default:
992 return 0;
993 }
994 }
996 /**
997 * Checks if the given marker is used in the shape, and if so, it
998 * releases it by calling sp_marker_hide. Also detaches signals
999 * and unrefs the marker from the shape.
1000 */
1001 static void
1002 sp_shape_marker_release (SPObject *marker, SPShape *shape)
1003 {
1004 SPItem *item;
1005 int i;
1007 item = (SPItem *) shape;
1009 for (i = 0; i < SP_MARKER_LOC_QTY; i++) {
1010 if (marker == shape->marker[i]) {
1011 SPItemView *v;
1012 /* Hide marker */
1013 for (v = item->display; v != NULL; v = v->next) {
1014 sp_marker_hide ((SPMarker *) (shape->marker[i]), NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
1015 /* fixme: Do we need explicit remove here? (Lauris) */
1016 /* nr_arena_item_set_mask (v->arenaitem, NULL); */
1017 }
1018 /* Detach marker */
1019 shape->release_connect[i].disconnect();
1020 shape->modified_connect[i].disconnect();
1021 shape->marker[i] = sp_object_hunref (shape->marker[i], item);
1022 }
1023 }
1024 }
1026 /**
1027 * No-op. Exists for handling 'modified' messages
1028 */
1029 static void
1030 sp_shape_marker_modified (SPObject */*marker*/, guint /*flags*/, SPItem */*item*/)
1031 {
1032 /* I think mask does update automagically */
1033 /* g_warning ("Item %s mask %s modified", item->getId(), mask->getId()); */
1034 }
1036 /**
1037 * Adds a new marker to shape object at the location indicated by key. value
1038 * must be a valid URI reference resolvable from the shape object (i.e., present
1039 * in the document <defs>). If the shape object already has a marker
1040 * registered at the given position, it is removed first. Then the
1041 * new marker is hrefed and its signals connected.
1042 */
1043 void
1044 sp_shape_set_marker (SPObject *object, unsigned int key, const gchar *value)
1045 {
1046 SPItem *item = (SPItem *) object;
1047 SPShape *shape = (SPShape *) object;
1049 if (key > SP_MARKER_LOC_END) {
1050 return;
1051 }
1053 SPObject *mrk = sp_css_uri_reference_resolve (SP_OBJECT_DOCUMENT (object), value);
1054 if (mrk != shape->marker[key]) {
1055 if (shape->marker[key]) {
1056 SPItemView *v;
1058 /* Detach marker */
1059 shape->release_connect[key].disconnect();
1060 shape->modified_connect[key].disconnect();
1062 /* Hide marker */
1063 for (v = item->display; v != NULL; v = v->next) {
1064 sp_marker_hide ((SPMarker *) (shape->marker[key]),
1065 NR_ARENA_ITEM_GET_KEY (v->arenaitem) + key);
1066 /* fixme: Do we need explicit remove here? (Lauris) */
1067 /* nr_arena_item_set_mask (v->arenaitem, NULL); */
1068 }
1070 /* Unref marker */
1071 shape->marker[key] = sp_object_hunref (shape->marker[key], object);
1072 }
1073 if (SP_IS_MARKER (mrk)) {
1074 shape->marker[key] = sp_object_href (mrk, object);
1075 shape->release_connect[key] = mrk->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_shape_marker_release), shape));
1076 shape->modified_connect[key] = mrk->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_shape_marker_modified), shape));
1077 }
1078 }
1079 }
1083 /* Shape section */
1085 /**
1086 * Calls any registered handlers for the set_shape action
1087 */
1088 void SPShape::setShape()
1089 {
1090 if (SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (this))->set_shape) {
1091 SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (this))->set_shape (this);
1092 }
1093 }
1095 /**
1096 * Adds a curve to the shape. If owner is specified, a reference
1097 * will be made, otherwise the curve will be copied into the shape.
1098 * Any existing curve in the shape will be unreferenced first.
1099 * This routine also triggers a request to update the display.
1100 */
1101 void SPShape::setCurve(SPCurve *curve, unsigned int owner)
1102 {
1103 if (this->curve) {
1104 this->curve = this->curve->unref();
1105 }
1106 if (curve) {
1107 if (owner) {
1108 this->curve = curve->ref();
1109 } else {
1110 this->curve = curve->copy();
1111 }
1112 }
1113 SP_OBJECT(this)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1114 }
1116 /**
1117 * Return duplicate of curve (if any exists) or NULL if there is no curve
1118 */
1119 SPCurve * SPShape::getCurve()
1120 {
1121 if (this->curve) {
1122 return this->curve->copy();
1123 }
1124 return NULL;
1125 }
1127 /**
1128 * Same as sp_shape_set_curve but without updating the display
1129 */
1130 void SPShape::setCurveInsync(SPCurve *curve, unsigned int owner)
1131 {
1132 if (this->curve) {
1133 this->curve = this->curve->unref();
1134 }
1135 if (curve) {
1136 if (owner) {
1137 this->curve = curve->ref();
1138 } else {
1139 this->curve = curve->copy();
1140 }
1141 }
1142 }
1144 /**
1145 * Return all nodes in a path that are to be considered for snapping
1146 */
1147 void SPShape::sp_shape_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs)
1148 {
1149 g_assert(item != NULL);
1150 g_assert(SP_IS_SHAPE(item));
1152 SPShape const *shape = SP_SHAPE(item);
1153 if (shape->curve == NULL) {
1154 return;
1155 }
1157 // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes
1158 if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) {
1159 return;
1160 }
1162 Geom::PathVector const &pathv = shape->curve->get_pathvector();
1163 if (pathv.empty())
1164 return;
1166 Geom::Matrix const i2d (item->i2d_affine ());
1168 if (snapprefs->getSnapObjectMidpoints()) {
1169 Geom::OptRect bbox = item->getBounds(item->i2d_affine());
1170 if (bbox) {
1171 p.push_back(Inkscape::SnapCandidatePoint(bbox->midpoint(), Inkscape::SNAPSOURCE_OBJECT_MIDPOINT, Inkscape::SNAPTARGET_OBJECT_MIDPOINT));
1172 }
1173 }
1175 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
1176 if (snapprefs->getSnapToItemNode()) {
1177 // Add the first point of the path
1178 p.push_back(Inkscape::SnapCandidatePoint(path_it->initialPoint() * i2d, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP));
1179 }
1181 Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve
1182 Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); // outgoing curve
1183 while (curve_it1 != path_it->end_default())
1184 {
1185 // For each path: consider midpoints of line segments for snapping
1186 if (snapprefs->getSnapLineMidpoints()) { // only do this when we're snapping nodes (enforces strict snapping)
1187 if (Geom::LineSegment const* line_segment = dynamic_cast<Geom::LineSegment const*>(&(*curve_it1))) {
1188 p.push_back(Inkscape::SnapCandidatePoint(Geom::middle_point(*line_segment) * i2d, Inkscape::SNAPSOURCE_LINE_MIDPOINT, Inkscape::SNAPTARGET_LINE_MIDPOINT));
1189 }
1190 }
1192 if (curve_it2 == path_it->end_default()) { // Test will only pass for the last iteration of the while loop
1193 if (snapprefs->getSnapToItemNode() && !path_it->closed()) {
1194 // Add the last point of the path, but only for open paths
1195 // (for closed paths the first and last point will coincide)
1196 p.push_back(Inkscape::SnapCandidatePoint((*curve_it1).finalPoint() * i2d, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP));
1197 }
1198 } else {
1199 /* Test whether to add the node between curve_it1 and curve_it2.
1200 * Loop to end_default (so only iterating through the stroked part); */
1202 Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2);
1204 bool c1 = snapprefs->getSnapToItemNode() && (nodetype == Geom::NODE_CUSP || nodetype == Geom::NODE_NONE);
1205 bool c2 = snapprefs->getSnapSmoothNodes() && (nodetype == Geom::NODE_SMOOTH || nodetype == Geom::NODE_SYMM);
1207 if (c1 || c2) {
1208 Inkscape::SnapSourceType sst;
1209 Inkscape::SnapTargetType stt;
1210 switch (nodetype) {
1211 case Geom::NODE_CUSP:
1212 sst = Inkscape::SNAPSOURCE_NODE_CUSP;
1213 stt = Inkscape::SNAPTARGET_NODE_CUSP;
1214 break;
1215 case Geom::NODE_SMOOTH:
1216 case Geom::NODE_SYMM:
1217 sst = Inkscape::SNAPSOURCE_NODE_SMOOTH;
1218 stt = Inkscape::SNAPTARGET_NODE_SMOOTH;
1219 break;
1220 default:
1221 sst = Inkscape::SNAPSOURCE_UNDEFINED;
1222 stt = Inkscape::SNAPTARGET_UNDEFINED;
1223 break;
1224 }
1225 p.push_back(Inkscape::SnapCandidatePoint(curve_it1->finalPoint() * i2d, sst, stt));
1226 }
1227 }
1229 ++curve_it1;
1230 ++curve_it2;
1231 }
1233 // Find the internal intersections of each path and consider these for snapping
1234 // (using "Method 1" as described in Inkscape::ObjectSnapper::_collectNodes())
1235 if (snapprefs->getSnapIntersectionCS()) {
1236 Geom::Crossings cs;
1237 try {
1238 cs = self_crossings(*path_it);
1239 if (cs.size() > 0) { // There might be multiple intersections...
1240 for (Geom::Crossings::const_iterator i = cs.begin(); i != cs.end(); i++) {
1241 Geom::Point p_ix = (*path_it).pointAt((*i).ta);
1242 p.push_back(Inkscape::SnapCandidatePoint(p_ix * i2d, Inkscape::SNAPSOURCE_PATH_INTERSECTION, Inkscape::SNAPTARGET_PATH_INTERSECTION));
1243 }
1244 }
1245 } catch (Geom::RangeError &e) {
1246 // do nothing
1247 // The exception could be Geom::InfiniteSolutions: then no snappoints should be added
1248 }
1250 }
1251 }
1253 }
1255 /*
1256 Local Variables:
1257 mode:c++
1258 c-file-style:"stroustrup"
1259 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1260 indent-tabs-mode:nil
1261 fill-column:99
1262 End:
1263 */
1264 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :