1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Base class for shapes, including <path> element
4 *
5 * Author:
6 * Lauris Kaplinski <lauris@kaplinski.com>
7 * Abhishek Sharma
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 * Copyright (C) 2010 Jon A. Cruz <jon@joncruz.org>
14 *
15 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
16 */
17
18 #include <2geom/rect.h>
19 #include <2geom/transforms.h>
20 #include <2geom/pathvector.h>
21 #include <2geom/path-intersection.h>
22 #include "helper/geom.h"
23 #include "helper/geom-nodetype.h"
24
25 #include <sigc++/functors/ptr_fun.h>
26 #include <sigc++/adaptors/bind.h>
27
28 #include "display/drawing-shape.h"
29 #include "display/curve.h"
30 #include "print.h"
31 #include "document.h"
32 #include "style.h"
33 #include "sp-marker.h"
34 #include "sp-root.h"
35 #include "sp-path.h"
36 #include "preferences.h"
37 #include "attributes.h"
38 #include "path/path-outline.h" // For bound box calculation
39
40 #include "svg/svg.h"
41 #include "svg/path-string.h"
42 #include "live_effects/lpeobject.h"
43
44 #include "helper/mathfns.h" // for triangle_area()
45
46 #define noSHAPE_VERBOSE
47
48 static void sp_shape_update_marker_view (SPShape *shape, Inkscape::DrawingItem *ai);
49
SPShape()50 SPShape::SPShape() : SPLPEItem() {
51 for (auto & i : this->_marker) {
52 i = nullptr;
53 }
54
55 this->_curve = nullptr;
56 this->_curve_before_lpe = nullptr;
57 }
58
~SPShape()59 SPShape::~SPShape() {
60 for ( int i = 0 ; i < SP_MARKER_LOC_QTY ; i++ ) {
61 this->_release_connect[i].disconnect();
62 this->_modified_connect[i].disconnect();
63 }
64 }
65
build(SPDocument * document,Inkscape::XML::Node * repr)66 void SPShape::build(SPDocument *document, Inkscape::XML::Node *repr) {
67 SPLPEItem::build(document, repr);
68
69 for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
70 sp_shape_set_marker (this, i, this->style->marker_ptrs[i]->value());
71 }
72 }
73
74
75 /**
76 * Removes, releases and unrefs all children of object
77 *
78 * This is the inverse of sp_shape_build(). It must be invoked as soon
79 * as the shape is removed from the tree, even if it is still referenced
80 * by other objects. This routine also disconnects/unrefs markers and
81 * curves attached to it.
82 *
83 * \see SPObject::release()
84 */
release()85 void SPShape::release() {
86 for (int i = 0; i < SP_MARKER_LOC_QTY; i++) {
87 if (this->_marker[i]) {
88
89 for (SPItemView *v = this->display; v != nullptr; v = v->next) {
90 sp_marker_hide(_marker[i], v->arenaitem->key() + i);
91 }
92
93 this->_release_connect[i].disconnect();
94 this->_modified_connect[i].disconnect();
95 _marker[i]->unhrefObject(this);
96 _marker[i] = nullptr;
97 }
98 }
99
100 _curve.reset();
101
102 _curve_before_lpe.reset();
103
104 SPLPEItem::release();
105 }
106
set(SPAttr key,const gchar * value)107 void SPShape::set(SPAttr key, const gchar* value) {
108 SPLPEItem::set(key, value);
109 }
110
111
write(Inkscape::XML::Document * xml_doc,Inkscape::XML::Node * repr,guint flags)112 Inkscape::XML::Node* SPShape::write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) {
113 SPLPEItem::write(xml_doc, repr, flags);
114 return repr;
115 }
116
update(SPCtx * ctx,guint flags)117 void SPShape::update(SPCtx* ctx, guint flags) {
118 // Any update can change the bounding box,
119 // so the cached version can no longer be used.
120 // But the idle checker usually is just moving the objects around.
121 bbox_vis_cache_is_valid = false;
122 bbox_geom_cache_is_valid = false;
123
124 // std::cout << "SPShape::update(): " << (getId()?getId():"null") << std::endl;
125 SPLPEItem::update(ctx, flags);
126
127 /* This stanza checks that an object's marker style agrees with
128 * the marker objects it has allocated. sp_shape_set_marker ensures
129 * that the appropriate marker objects are present (or absent) to
130 * match the style.
131 */
132 for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
133 sp_shape_set_marker (this, i, this->style->marker_ptrs[i]->value());
134 }
135
136 if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
137 if (this->style->stroke_width.unit == SP_CSS_UNIT_PERCENT) {
138 SPItemCtx *ictx = (SPItemCtx *) ctx;
139 double const aw = 1.0 / ictx->i2vp.descrim();
140 this->style->stroke_width.computed = this->style->stroke_width.value * aw;
141
142 for (SPItemView *v = ((SPItem *) (this))->display; v != nullptr; v = v->next) {
143 Inkscape::DrawingShape *sh = dynamic_cast<Inkscape::DrawingShape *>(v->arenaitem);
144 if (hasMarkers()) {
145 this->context_style = this->style;
146 sh->setStyle(this->style, this->context_style);
147 // Done at end:
148 // sh->setChildrenStyle(this->context_style); //Resolve 'context-xxx' in children.
149 } else if (this->parent) {
150 this->context_style = this->parent->context_style;
151 sh->setStyle(this->style, this->context_style);
152 }
153 }
154 }
155 }
156
157 if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) {
158 /* This is suboptimal, because changing parent style schedules recalculation */
159 /* But on the other hand - how can we know that parent does not tie style and transform */
160 for (SPItemView *v = this->display; v != nullptr; v = v->next) {
161 Inkscape::DrawingShape *sh = dynamic_cast<Inkscape::DrawingShape *>(v->arenaitem);
162
163 if (flags & SP_OBJECT_MODIFIED_FLAG) {
164 sh->setPath(this->_curve.get());
165 }
166 }
167 }
168
169 if (this->hasMarkers ()) {
170
171 /* Dimension marker views */
172 for (SPItemView *v = this->display; v != nullptr; v = v->next) {
173 if (!v->arenaitem->key()) {
174 v->arenaitem->setKey(SPItem::display_key_new (SP_MARKER_LOC_QTY));
175 }
176
177 for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
178 if (_marker[i]) {
179 sp_marker_show_dimension(_marker[i],
180 v->arenaitem->key() + i,
181 numberOfMarkers(i));
182 }
183 }
184 }
185
186 /* Update marker views */
187 for (SPItemView *v = this->display; v != nullptr; v = v->next) {
188 sp_shape_update_marker_view (this, v->arenaitem);
189 }
190
191 // Marker selector needs this here or marker previews are not rendered.
192 for (SPItemView *v = this->display; v != nullptr; v = v->next) {
193 Inkscape::DrawingShape *sh = dynamic_cast<Inkscape::DrawingShape *>(v->arenaitem);
194
195 sh->setChildrenStyle(this->context_style); // Resolve 'context-xxx' in children.
196 }
197 }
198
199 /* Update stroke/dashes for relative units. */
200 if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
201
202 SPItemCtx const *ictx = reinterpret_cast<SPItemCtx const *>(ctx);
203
204 double const w = ictx->viewport.width();
205 double const h = ictx->viewport.height();
206 double const d = sqrt(w*w + h*h) * M_SQRT1_2; // diagonal per SVG spec
207 double const em = style->font_size.computed;
208 double const ex = 0.5 * em; // fixme: get x height from pango or libnrtype.
209
210 if (style->stroke_width.unit == SP_CSS_UNIT_EM) {
211 style->stroke_width.computed = style->stroke_width.value * em;
212 }
213 else if (style->stroke_width.unit == SP_CSS_UNIT_EX) {
214 style->stroke_width.computed = style->stroke_width.value * ex;
215 }
216 else if (style->stroke_width.unit == SP_CSS_UNIT_PERCENT) {
217 style->stroke_width.computed = style->stroke_width.value * d;
218 }
219
220 if (style->stroke_dasharray.values.size() != 0) {
221 for (auto&& i: style->stroke_dasharray.values) {
222 if (i.unit == SP_CSS_UNIT_EM) i.computed = i.value * em;
223 else if (i.unit == SP_CSS_UNIT_EX) i.computed = i.value * ex;
224 else if (i.unit == SP_CSS_UNIT_PERCENT) i.computed = i.value * d;
225 }
226 }
227
228 if (style->stroke_dashoffset.unit == SP_CSS_UNIT_EM) {
229 style->stroke_dashoffset.computed = style->stroke_dashoffset.value * em;
230 }
231 else if (style->stroke_dashoffset.unit == SP_CSS_UNIT_EX) {
232 style->stroke_dashoffset.computed = style->stroke_dashoffset.value * ex;
233 }
234 else if (style->stroke_dashoffset.unit == SP_CSS_UNIT_PERCENT) {
235 style->stroke_dashoffset.computed = style->stroke_dashoffset.value * d;
236 }
237 }
238 }
239
240 /**
241 * Calculate the transform required to get a marker's path object in the
242 * right place for particular path segment on a shape.
243 *
244 * \see sp_shape_marker_update_marker_view.
245 *
246 * From SVG spec:
247 * The axes of the temporary new user coordinate system are aligned according to the orient attribute on the 'marker'
248 * element and the slope of the curve at the given vertex. (Note: if there is a discontinuity at a vertex, the slope
249 * is the average of the slopes of the two segments of the curve that join at the given vertex. If a slope cannot be
250 * determined, the slope is assumed to be zero.)
251 *
252 * Reference: http://www.w3.org/TR/SVG11/painting.html#MarkerElement, the `orient' attribute.
253 * Reference for behaviour of zero-length segments:
254 * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
255 */
sp_shape_marker_get_transform(Geom::Curve const & c1,Geom::Curve const & c2)256 Geom::Affine sp_shape_marker_get_transform(Geom::Curve const & c1, Geom::Curve const & c2)
257 {
258 Geom::Point p = c1.pointAt(1);
259 Geom::Curve * c1_reverse = c1.reverse();
260 Geom::Point tang1 = - c1_reverse->unitTangentAt(0);
261 delete c1_reverse;
262 Geom::Point tang2 = c2.unitTangentAt(0);
263
264 double const angle1 = Geom::atan2(tang1);
265 double const angle2 = Geom::atan2(tang2);
266
267 double ret_angle = .5 * (angle1 + angle2);
268
269 if ( fabs( angle2 - angle1 ) > M_PI ) {
270 /* ret_angle is in the middle of the larger of the two sectors between angle1 and
271 * angle2, so flip it by 180degrees to force it to the middle of the smaller sector.
272 *
273 * (Imagine a circle with rays drawn at angle1 and angle2 from the centre of the
274 * circle. Those two rays divide the circle into two sectors.)
275 */
276 ret_angle += M_PI;
277 }
278
279 return Geom::Rotate(ret_angle) * Geom::Translate(p);
280 }
281
sp_shape_marker_get_transform_at_start(Geom::Curve const & c)282 Geom::Affine sp_shape_marker_get_transform_at_start(Geom::Curve const & c)
283 {
284 Geom::Point p = c.pointAt(0);
285 Geom::Affine ret = Geom::Translate(p);
286
287 if ( !c.isDegenerate() ) {
288 Geom::Point tang = c.unitTangentAt(0);
289 double const angle = Geom::atan2(tang);
290 ret = Geom::Rotate(angle) * Geom::Translate(p);
291 } else {
292 /* FIXME: the svg spec says to search for a better alternative than zero angle directionality:
293 * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes */
294 }
295
296 return ret;
297 }
298
sp_shape_marker_get_transform_at_end(Geom::Curve const & c)299 Geom::Affine sp_shape_marker_get_transform_at_end(Geom::Curve const & c)
300 {
301 Geom::Point p = c.pointAt(1);
302 Geom::Affine ret = Geom::Translate(p);
303
304 if ( !c.isDegenerate() ) {
305 Geom::Curve * c_reverse = c.reverse();
306 Geom::Point tang = - c_reverse->unitTangentAt(0);
307 delete c_reverse;
308 double const angle = Geom::atan2(tang);
309 ret = Geom::Rotate(angle) * Geom::Translate(p);
310 } else {
311 /* FIXME: the svg spec says to search for a better alternative than zero angle directionality:
312 * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes */
313 }
314
315 return ret;
316 }
317
318 /**
319 * Updates the instances (views) of a given marker in a shape.
320 * Marker views have to be scaled already. The transformation
321 * is retrieved and then shown by calling sp_marker_show_instance.
322 *
323 * @todo figure out what to do when both 'marker' and for instance 'marker-end' are set.
324 */
325 static void
sp_shape_update_marker_view(SPShape * shape,Inkscape::DrawingItem * ai)326 sp_shape_update_marker_view(SPShape *shape, Inkscape::DrawingItem *ai)
327 {
328 // position arguments to sp_marker_show_instance, basically counts the amount of markers.
329 int counter[4] = {0};
330
331 if (!shape->curve())
332 return;
333
334 Geom::PathVector const &pathv = shape->curve()->get_pathvector();
335 if (pathv.empty()) return;
336
337 // the first vertex should get a start marker, the last an end marker, and all the others a mid marker
338 // see bug 456148
339
340 // START marker
341 {
342 Geom::Affine const m (sp_shape_marker_get_transform_at_start(pathv.begin()->front()));
343 for (int i = 0; i < 2; i++) { // SP_MARKER_LOC and SP_MARKER_LOC_START
344 if ( shape->_marker[i] ) {
345 Geom::Affine m_auto = m;
346 // Reverse start marker if necessary.
347 if (shape->_marker[i]->orient_mode == MARKER_ORIENT_AUTO_START_REVERSE) {
348 m_auto = Geom::Rotate::from_degrees( 180.0 ) * m;
349 }
350 sp_marker_show_instance(shape->_marker[i], ai,
351 ai->key() + i, counter[i], m_auto,
352 shape->style->stroke_width.computed);
353 counter[i]++;
354 }
355 }
356 }
357
358 // MID marker
359 if (shape->_marker[SP_MARKER_LOC_MID] || shape->_marker[SP_MARKER_LOC]) {
360 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
361 // START position
362 if ( path_it != pathv.begin()
363 && ! ((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
364 {
365 Geom::Affine const m (sp_shape_marker_get_transform_at_start(path_it->front()));
366 for (int i = 0; i < 3; i += 2) { // SP_MARKER_LOC and SP_MARKER_LOC_MID
367 if ( shape->_marker[i] ) {
368 sp_marker_show_instance(shape->_marker[i], ai,
369 ai->key() + i, counter[i], m,
370 shape->style->stroke_width.computed);
371 counter[i]++;
372 }
373 }
374 }
375 // MID position
376 if ( path_it->size_default() > 1) {
377 Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve
378 Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); // outgoing curve
379 while (curve_it2 != path_it->end_default())
380 {
381 /* Put marker between curve_it1 and curve_it2.
382 * Loop to end_default (so including closing segment), because when a path is closed,
383 * there should be a midpoint marker between last segment and closing straight line segment
384 */
385 Geom::Affine const m (sp_shape_marker_get_transform(*curve_it1, *curve_it2));
386 for (int i = 0; i < 3; i += 2) { // SP_MARKER_LOC and SP_MARKER_LOC_MID
387 if (shape->_marker[i]) {
388 sp_marker_show_instance(shape->_marker[i], ai,
389 ai->key() + i, counter[i], m,
390 shape->style->stroke_width.computed);
391 counter[i]++;
392 }
393 }
394
395 ++curve_it1;
396 ++curve_it2;
397 }
398 }
399 // END position
400 if ( path_it != (pathv.end()-1) && !path_it->empty()) {
401 Geom::Curve const &lastcurve = path_it->back_default();
402 Geom::Affine const m = sp_shape_marker_get_transform_at_end(lastcurve);
403 for (int i = 0; i < 3; i += 2) { // SP_MARKER_LOC and SP_MARKER_LOC_MID
404 if (shape->_marker[i]) {
405 sp_marker_show_instance(shape->_marker[i], ai,
406 ai->key() + i, counter[i], m,
407 shape->style->stroke_width.computed);
408 counter[i]++;
409 }
410 }
411 }
412 }
413 }
414
415 // END marker
416 if ( shape->_marker[SP_MARKER_LOC_END] || shape->_marker[SP_MARKER_LOC] ) {
417 /* Get reference to last curve in the path.
418 * For moveto-only path, this returns the "closing line segment". */
419 Geom::Path const &path_last = pathv.back();
420 unsigned int index = path_last.size_default();
421 if (index > 0) {
422 index--;
423 }
424 Geom::Curve const &lastcurve = path_last[index];
425 Geom::Affine const m = sp_shape_marker_get_transform_at_end(lastcurve);
426
427 for (int i = 0; i < 4; i += 3) { // SP_MARKER_LOC and SP_MARKER_LOC_END
428 if (shape->_marker[i]) {
429 sp_marker_show_instance(shape->_marker[i], ai,
430 ai->key() + i, counter[i], m,
431 shape->style->stroke_width.computed);
432 counter[i]++;
433 }
434 }
435 }
436 }
437
modified(unsigned int flags)438 void SPShape::modified(unsigned int flags) {
439 // std::cout << "SPShape::modified(): " << (getId()?getId():"null") << std::endl;
440 SPLPEItem::modified(flags);
441
442 if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
443 for (SPItemView *v = this->display; v != nullptr; v = v->next) {
444 Inkscape::DrawingShape *sh = dynamic_cast<Inkscape::DrawingShape *>(v->arenaitem);
445 if (hasMarkers()) {
446 this->context_style = this->style;
447 sh->setStyle(this->style, this->context_style);
448 // Note: marker selector preview does not trigger SP_OBJECT_STYLE_MODIFIED_FLAG so
449 // this is not called when marker previews are generated, however there is code in
450 // SPShape::update() that calls this routine so we don't worry about it here.
451 sh->setChildrenStyle(this->context_style); // Resolve 'context-xxx' in children.
452 } else if (this->parent) {
453 this->context_style = this->parent->context_style;
454 sh->setStyle(this->style, this->context_style);
455 }
456 }
457 }
458
459 if (flags & SP_OBJECT_MODIFIED_FLAG && style->filter.set) {
460 if (auto filter = style->getFilter()) {
461 filter->update_filter_all_regions();
462 }
463 }
464
465 if (!_curve) {
466 sp_lpe_item_update_patheffect(this, true, false);
467 }
468 }
469
bbox(Geom::Affine const & transform,SPItem::BBoxType bboxtype) const470 Geom::OptRect SPShape::bbox(Geom::Affine const &transform, SPItem::BBoxType bboxtype) const {
471 // If the object is clipped, the update funcation that invalidates
472 // the cache doesn't get called if the object is moved, so we need
473 // to compare the transformations as well.
474
475 if (bboxtype == SPItem::VISUAL_BBOX) {
476 bbox_vis_cache =
477 either_bbox(transform, bboxtype, bbox_vis_cache_is_valid, bbox_vis_cache, bbox_vis_cache_transform);
478 if (bbox_vis_cache) {
479 bbox_vis_cache_transform = transform;
480 bbox_vis_cache_is_valid = true;
481 }
482 return bbox_vis_cache;
483 } else {
484 bbox_geom_cache =
485 either_bbox(transform, bboxtype, bbox_geom_cache_is_valid, bbox_geom_cache, bbox_geom_cache_transform);
486 if (bbox_geom_cache) {
487 bbox_geom_cache_transform = transform;
488 bbox_geom_cache_is_valid = true;
489 }
490 return bbox_geom_cache;
491 }
492 }
493
either_bbox(Geom::Affine const & transform,SPItem::BBoxType bboxtype,bool cache_is_valid,Geom::OptRect bbox_cache,Geom::Affine const & transform_cache) const494 Geom::OptRect SPShape::either_bbox(Geom::Affine const &transform, SPItem::BBoxType bboxtype, bool cache_is_valid,
495 Geom::OptRect bbox_cache, Geom::Affine const &transform_cache) const
496 {
497
498 Geom::OptRect bbox;
499
500 // Return the cache if possible.
501 auto delta = transform_cache.inverse() * transform;
502 if (cache_is_valid && bbox_cache && delta.isTranslation()) {
503
504 // Don't re-adjust the cache if we haven't moved
505 if (!delta.isNonzeroTranslation()) {
506 return bbox_cache;
507 }
508 // delta is pure translation so it's safe to use it as is
509 return *bbox_cache * delta;
510 }
511
512 if (!this->_curve || this->_curve->get_pathvector().empty()) {
513 return bbox;
514 }
515
516 bbox = bounds_exact_transformed(this->_curve->get_pathvector(), transform);
517
518 if (!bbox) {
519 return bbox;
520 }
521
522 if (bboxtype == SPItem::VISUAL_BBOX) {
523 // convert the stroke to a path and calculate that path's geometric bbox
524
525 if (!this->style->stroke.isNone() && !this->style->stroke_extensions.hairline) {
526 Geom::PathVector *pathv = item_to_outline(this, true); // calculate bbox_only
527
528 if (pathv) {
529 bbox |= bounds_exact_transformed(*pathv, transform);
530 delete pathv;
531 }
532 }
533
534 // Union with bboxes of the markers, if any
535 if ( this->hasMarkers() && !this->_curve->get_pathvector().empty() ) {
536 /** \todo make code prettier! */
537 Geom::PathVector const & pathv = this->_curve->get_pathvector();
538 // START marker
539 for (unsigned i = 0; i < 2; i++) { // SP_MARKER_LOC and SP_MARKER_LOC_START
540 if ( this->_marker[i] ) {
541 SPItem* marker_item = sp_item_first_item_child( _marker[i] );
542
543 if (marker_item) {
544 Geom::Affine tr(sp_shape_marker_get_transform_at_start(pathv.begin()->front()));
545
546 if (_marker[i]->orient_mode == MARKER_ORIENT_AUTO_START_REVERSE) {
547 // Reverse start marker if necessary
548 tr = Geom::Rotate::from_degrees( 180.0 ) * tr;
549 } else if (_marker[i]->orient_mode == MARKER_ORIENT_ANGLE) {
550 Geom::Point transl = tr.translation();
551 tr = Geom::Rotate::from_degrees(_marker[i]->orient.computed) * Geom::Translate(transl);
552 }
553
554 if (_marker[i]->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
555 tr = Geom::Scale(this->style->stroke_width.computed) * tr;
556 }
557
558 // total marker transform
559 tr = marker_item->transform * _marker[i]->c2p * tr * transform;
560
561 // get bbox of the marker with that transform
562 bbox |= marker_item->visualBounds(tr);
563 }
564 }
565 }
566
567 // MID marker
568 for (unsigned i = 0; i < 3; i += 2) { // SP_MARKER_LOC and SP_MARKER_LOC_MID
569 if ( !this->_marker[i] ) {
570 continue;
571 }
572
573 SPMarker* marker = _marker[i];
574 SPItem* marker_item = sp_item_first_item_child( marker );
575
576 if ( !marker_item ) {
577 continue;
578 }
579
580 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
581 // START position
582 if ( path_it != pathv.begin()
583 && ! ((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
584 {
585 Geom::Affine tr(sp_shape_marker_get_transform_at_start(path_it->front()));
586
587 if (marker->orient_mode == MARKER_ORIENT_ANGLE) {
588 Geom::Point transl = tr.translation();
589 tr = Geom::Rotate::from_degrees(marker->orient.computed) * Geom::Translate(transl);
590 }
591
592 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
593 tr = Geom::Scale(this->style->stroke_width.computed) * tr;
594 }
595
596 tr = marker_item->transform * marker->c2p * tr * transform;
597 bbox |= marker_item->visualBounds(tr);
598 }
599
600 // MID position
601 if ( path_it->size_default() > 1) {
602 Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve
603 Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); // outgoing curve
604
605 while (curve_it2 != path_it->end_default())
606 {
607 /* Put marker between curve_it1 and curve_it2.
608 * Loop to end_default (so including closing segment), because when a path is closed,
609 * there should be a midpoint marker between last segment and closing straight line segment */
610
611 SPMarker* marker = _marker[i];
612 SPItem* marker_item = sp_item_first_item_child( marker );
613
614 if (marker_item) {
615 Geom::Affine tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
616
617 if (marker->orient_mode == MARKER_ORIENT_ANGLE) {
618 Geom::Point transl = tr.translation();
619 tr = Geom::Rotate::from_degrees(marker->orient.computed) * Geom::Translate(transl);
620 }
621
622 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
623 tr = Geom::Scale(this->style->stroke_width.computed) * tr;
624 }
625
626 tr = marker_item->transform * marker->c2p * tr * transform;
627 bbox |= marker_item->visualBounds(tr);
628 }
629
630 ++curve_it1;
631 ++curve_it2;
632 }
633 }
634
635 // END position
636 if ( path_it != (pathv.end()-1) && !path_it->empty()) {
637 Geom::Curve const &lastcurve = path_it->back_default();
638 Geom::Affine tr = sp_shape_marker_get_transform_at_end(lastcurve);
639
640 if (marker->orient_mode == MARKER_ORIENT_ANGLE) {
641 Geom::Point transl = tr.translation();
642 tr = Geom::Rotate::from_degrees(marker->orient.computed) * Geom::Translate(transl);
643 }
644
645 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
646 tr = Geom::Scale(this->style->stroke_width.computed) * tr;
647 }
648
649 tr = marker_item->transform * marker->c2p * tr * transform;
650 bbox |= marker_item->visualBounds(tr);
651 }
652 }
653 }
654
655 // END marker
656 for (unsigned i = 0; i < 4; i += 3) { // SP_MARKER_LOC and SP_MARKER_LOC_END
657 if ( _marker[i] ) {
658 SPMarker* marker = _marker[i];
659 SPItem* marker_item = sp_item_first_item_child( marker );
660
661 if (marker_item) {
662 /* Get reference to last curve in the path.
663 * For moveto-only path, this returns the "closing line segment". */
664 Geom::Path const &path_last = pathv.back();
665 unsigned int index = path_last.size_default();
666
667 if (index > 0) {
668 index--;
669 }
670
671 Geom::Curve const &lastcurve = path_last[index];
672
673 Geom::Affine tr = sp_shape_marker_get_transform_at_end(lastcurve);
674
675 if (marker->orient_mode == MARKER_ORIENT_ANGLE) {
676 Geom::Point transl = tr.translation();
677 tr = Geom::Rotate::from_degrees(marker->orient.computed) * Geom::Translate(transl);
678 }
679
680 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
681 tr = Geom::Scale(this->style->stroke_width.computed) * tr;
682 }
683
684 // total marker transform
685 tr = marker_item->transform * marker->c2p * tr * transform;
686
687 // get bbox of the marker with that transform
688 bbox |= marker_item->visualBounds(tr);
689 }
690 }
691 }
692 }
693 }
694
695
696 return bbox;
697 }
698
699 static void
sp_shape_print_invoke_marker_printing(SPObject * obj,Geom::Affine tr,SPStyle const * style,SPPrintContext * ctx)700 sp_shape_print_invoke_marker_printing(SPObject *obj, Geom::Affine tr, SPStyle const *style, SPPrintContext *ctx)
701 {
702 SPMarker *marker = SP_MARKER(obj);
703 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
704 tr = Geom::Scale(style->stroke_width.computed) * tr;
705 }
706
707 SPItem* marker_item = sp_item_first_item_child( marker );
708 if (marker_item) {
709 tr = marker_item->transform * marker->c2p * tr;
710
711 Geom::Affine old_tr = marker_item->transform;
712 marker_item->transform = tr;
713 marker_item->invoke_print (ctx);
714 marker_item->transform = old_tr;
715 }
716 }
717
print(SPPrintContext * ctx)718 void SPShape::print(SPPrintContext* ctx) {
719 if (!this->_curve) {
720 return;
721 }
722
723 Geom::PathVector const & pathv = this->_curve->get_pathvector();
724
725 if (pathv.empty()) {
726 return;
727 }
728
729 /* fixme: Think (Lauris) */
730 Geom::OptRect pbox, dbox, bbox;
731 pbox = this->geometricBounds();
732 bbox = this->desktopVisualBounds();
733 dbox = Geom::Rect::from_xywh(Geom::Point(0,0), this->document->getDimensions());
734
735 Geom::Affine const i2dt(this->i2dt_affine());
736
737 SPStyle* style = this->style;
738
739 if (!style->fill.isNone()) {
740 ctx->fill (pathv, i2dt, style, pbox, dbox, bbox);
741 }
742
743 if (!style->stroke.isNone()) {
744 ctx->stroke (pathv, i2dt, style, pbox, dbox, bbox);
745 }
746
747 /** \todo make code prettier */
748 // START marker
749 for (int i = 0; i < 2; i++) { // SP_MARKER_LOC and SP_MARKER_LOC_START
750 if ( this->_marker[i] ) {
751 Geom::Affine tr(sp_shape_marker_get_transform_at_start(pathv.begin()->front()));
752 sp_shape_print_invoke_marker_printing(this->_marker[i], tr, style, ctx);
753 }
754 }
755
756 // MID marker
757 for (int i = 0; i < 3; i += 2) { // SP_MARKER_LOC and SP_MARKER_LOC_MID
758 if (this->_marker[i]) {
759 for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
760 // START position
761 if ( path_it != pathv.begin()
762 && ! ((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
763 {
764 Geom::Affine tr(sp_shape_marker_get_transform_at_start(path_it->front()));
765 sp_shape_print_invoke_marker_printing(this->_marker[i], tr, style, ctx);
766 }
767
768 // MID position
769 if ( path_it->size_default() > 1) {
770 Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve
771 Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); // outgoing curve
772
773 while (curve_it2 != path_it->end_default())
774 {
775 /* Put marker between curve_it1 and curve_it2.
776 * Loop to end_default (so including closing segment), because when a path is closed,
777 * there should be a midpoint marker between last segment and closing straight line segment */
778 Geom::Affine tr(sp_shape_marker_get_transform(*curve_it1, *curve_it2));
779
780 sp_shape_print_invoke_marker_printing(this->_marker[i], tr, style, ctx);
781
782 ++curve_it1;
783 ++curve_it2;
784 }
785 }
786
787 if ( path_it != (pathv.end()-1) && !path_it->empty()) {
788 Geom::Curve const &lastcurve = path_it->back_default();
789 Geom::Affine tr = sp_shape_marker_get_transform_at_end(lastcurve);
790 sp_shape_print_invoke_marker_printing(this->_marker[i], tr, style, ctx);
791 }
792 }
793 }
794 }
795
796 // END marker
797 if ( this->_marker[SP_MARKER_LOC_END] || this->_marker[SP_MARKER_LOC]) {
798 /* Get reference to last curve in the path.
799 * For moveto-only path, this returns the "closing line segment". */
800 Geom::Path const &path_last = pathv.back();
801 unsigned int index = path_last.size_default();
802
803 if (index > 0) {
804 index--;
805 }
806
807 Geom::Curve const &lastcurve = path_last[index];
808
809 Geom::Affine tr = sp_shape_marker_get_transform_at_end(lastcurve);
810
811 for (int i = 0; i < 4; i += 3) { // SP_MARKER_LOC and SP_MARKER_LOC_END
812 if (this->_marker[i]) {
813 sp_shape_print_invoke_marker_printing(this->_marker[i], tr, style, ctx);
814 }
815 }
816 }
817 }
818
update_patheffect(bool write)819 void SPShape::update_patheffect(bool write)
820 {
821 if (auto c_lpe = SPCurve::copy(curveForEdit())) {
822 /* if a path has an lpeitem applied, then reset the curve to the _curve_before_lpe.
823 * This is very important for LPEs to work properly! (the bbox might be recalculated depending on the curve in shape)*/
824 this->setCurveInsync(c_lpe.get());
825 SPRoot *root = this->document->getRoot();
826 if (!sp_version_inside_range(root->version.inkscape, 0, 1, 0, 92)) {
827 this->resetClipPathAndMaskLPE();
828 }
829
830 bool success = false;
831 if (hasPathEffect() && pathEffectsEnabled()) {
832 success = this->performPathEffect(c_lpe.get(), SP_SHAPE(this));
833 if (success) {
834 this->setCurveInsync(c_lpe.get());
835 this->applyToClipPath(this);
836 this->applyToMask(this);
837 }
838 }
839 if (write && success) {
840 Inkscape::XML::Node *repr = this->getRepr();
841 if (c_lpe != nullptr) {
842 repr->setAttribute("d", sp_svg_write_path(c_lpe->get_pathvector()));
843 } else {
844 repr->removeAttribute("d");
845 }
846 }
847 this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
848 }
849 }
850
show(Inkscape::Drawing & drawing,unsigned int,unsigned int)851 Inkscape::DrawingItem* SPShape::show(Inkscape::Drawing &drawing, unsigned int /*key*/, unsigned int /*flags*/) {
852 // std::cout << "SPShape::show(): " << (getId()?getId():"null") << std::endl;
853 Inkscape::DrawingShape *s = new Inkscape::DrawingShape(drawing);
854
855 bool has_markers = this->hasMarkers();
856
857 s->setPath(this->_curve.get());
858
859 /* This stanza checks that an object's marker style agrees with
860 * the marker objects it has allocated. sp_shape_set_marker ensures
861 * that the appropriate marker objects are present (or absent) to
862 * match the style.
863 */
864 for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
865 sp_shape_set_marker (this, i, this->style->marker_ptrs[i]->value());
866 }
867
868 if (has_markers) {
869 /* provide key and dimension the marker views */
870 if (!s->key()) {
871 s->setKey(SPItem::display_key_new (SP_MARKER_LOC_QTY));
872 }
873
874 for (int i = 0; i < SP_MARKER_LOC_QTY; i++) {
875 if (_marker[i]) {
876 sp_marker_show_dimension(_marker[i],
877 s->key() + i,
878 numberOfMarkers(i));
879 }
880 }
881
882 /* Update marker views */
883 sp_shape_update_marker_view (this, s);
884
885 this->context_style = this->style;
886 s->setStyle(this->style, this->context_style);
887 s->setChildrenStyle(this->context_style); // Resolve 'context-xxx' in children.
888 } else if (this->parent) {
889 this->context_style = this->parent->context_style;
890 s->setStyle(this->style, this->context_style);
891 }
892 return s;
893 }
894
895 /**
896 * Sets style, path, and paintbox. Updates marker views, including dimensions.
897 */
hide(unsigned int key)898 void SPShape::hide(unsigned int key) {
899 for (int i = 0; i < SP_MARKER_LOC_QTY; ++i) {
900 if (_marker[i]) {
901 for (SPItemView* v = display; v != nullptr; v = v->next) {
902 if (key == v->key) {
903 sp_marker_hide(_marker[i], v->arenaitem->key() + i);
904 }
905 }
906 }
907 }
908
909 //SPLPEItem::onHide(key);
910 }
911
912 /**
913 * \param shape Shape.
914 * \return TRUE if the shape has any markers, or FALSE if not.
915 */
hasMarkers() const916 int SPShape::hasMarkers() const
917 {
918 /* Note, we're ignoring 'marker' settings, which technically should apply for
919 all three settings. This should be fixed later such that if 'marker' is
920 specified, then all three should appear. */
921
922 // Ignore markers for objects which are inside markers themselves.
923 for (SPObject *parent = this->parent; parent != nullptr; parent = parent->parent) {
924 if (dynamic_cast<SPMarker *>(parent)) {
925 return 0;
926 }
927 }
928
929 return (
930 this->_curve &&
931 (this->_marker[SP_MARKER_LOC] ||
932 this->_marker[SP_MARKER_LOC_START] ||
933 this->_marker[SP_MARKER_LOC_MID] ||
934 this->_marker[SP_MARKER_LOC_END])
935 );
936 }
937
938
939 /**
940 * \param shape Shape.
941 * \param type Marker type (e.g. SP_MARKER_LOC_START)
942 * \return Number of markers that the shape has of this type.
943 */
numberOfMarkers(int type) const944 int SPShape::numberOfMarkers(int type) const {
945 Geom::PathVector const & pathv = this->_curve->get_pathvector();
946
947 if (pathv.size() == 0) {
948 return 0;
949 }
950 switch(type) {
951
952 case SP_MARKER_LOC:
953 {
954 if ( this->_marker[SP_MARKER_LOC] ) {
955 guint n = 0;
956 for(const auto & path_it : pathv) {
957 n += path_it.size_default() + 1;
958 }
959 return n;
960 } else {
961 return 0;
962 }
963 }
964 case SP_MARKER_LOC_START:
965 // there is only a start marker on the first path of a pathvector
966 return this->_marker[SP_MARKER_LOC_START] ? 1 : 0;
967
968 case SP_MARKER_LOC_MID:
969 {
970 if ( this->_marker[SP_MARKER_LOC_MID] ) {
971 guint n = 0;
972 for(const auto & path_it : pathv) {
973 n += path_it.size_default() + 1;
974 }
975 n = (n > 1) ? (n - 2) : 0; // Minus the start and end marker, but never negative.
976 // A path or polyline may have only one point.
977 return n;
978 } else {
979 return 0;
980 }
981 }
982
983 case SP_MARKER_LOC_END:
984 {
985 // there is only an end marker on the last path of a pathvector
986 return this->_marker[SP_MARKER_LOC_END] ? 1 : 0;
987 }
988
989 default:
990 return 0;
991 }
992 }
993
994 /**
995 * Checks if the given marker is used in the shape, and if so, it
996 * releases it by calling sp_marker_hide. Also detaches signals
997 * and unrefs the marker from the shape.
998 */
999 static void
sp_shape_marker_release(SPObject * marker,SPShape * shape)1000 sp_shape_marker_release (SPObject *marker, SPShape *shape)
1001 {
1002 SPItem *item = dynamic_cast<SPItem *>(shape);
1003 g_return_if_fail(item != nullptr);
1004
1005 for (int i = 0; i < SP_MARKER_LOC_QTY; i++) {
1006 if (marker == shape->_marker[i]) {
1007 SPItemView *v;
1008 /* Hide marker */
1009 for (v = item->display; v != nullptr; v = v->next) {
1010 sp_marker_hide(shape->_marker[i], v->arenaitem->key() + i);
1011 }
1012 /* Detach marker */
1013 shape->_release_connect[i].disconnect();
1014 shape->_modified_connect[i].disconnect();
1015 shape->_marker[i]->unhrefObject(item);
1016 shape->_marker[i] = nullptr;
1017 }
1018 }
1019 }
1020
1021 /**
1022 * No-op. Exists for handling 'modified' messages
1023 */
1024 static void
sp_shape_marker_modified(SPObject *,guint,SPItem *)1025 sp_shape_marker_modified (SPObject */*marker*/, guint /*flags*/, SPItem */*item*/)
1026 {
1027 /* I think mask does update automagically */
1028 /* g_warning ("Item %s mask %s modified", item->getId(), mask->getId()); */
1029 }
1030
1031 /**
1032 * Adds a new marker to shape object at the location indicated by key. value
1033 * must be a valid URI reference resolvable from the shape object (i.e., present
1034 * in the document <defs>). If the shape object already has a marker
1035 * registered at the given position, it is removed first. Then the
1036 * new marker is hrefed and its signals connected.
1037 */
1038 void
sp_shape_set_marker(SPObject * object,unsigned int key,const gchar * value)1039 sp_shape_set_marker (SPObject *object, unsigned int key, const gchar *value)
1040 {
1041 SPShape *shape = dynamic_cast<SPShape *>(object);
1042 g_return_if_fail(shape != nullptr);
1043
1044 if (key > SP_MARKER_LOC_END) {
1045 return;
1046 }
1047
1048 SPObject *mrk = sp_css_uri_reference_resolve(object->document, value);
1049 SPMarker *marker = dynamic_cast<SPMarker *>(mrk);
1050 if (marker != shape->_marker[key]) {
1051 if (shape->_marker[key]) {
1052 SPItemView *v;
1053
1054 /* Detach marker */
1055 shape->_release_connect[key].disconnect();
1056 shape->_modified_connect[key].disconnect();
1057
1058 /* Hide marker */
1059 for (v = shape->display; v != nullptr; v = v->next) {
1060 sp_marker_hide(shape->_marker[key],
1061 v->arenaitem->key() + key);
1062 }
1063
1064 /* Unref marker */
1065 shape->_marker[key]->unhrefObject(object);
1066 shape->_marker[key] = nullptr;
1067 }
1068 if (marker) {
1069 shape->_marker[key] = marker;
1070 shape->_marker[key]->hrefObject(object);
1071 shape->_release_connect[key] = marker->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_shape_marker_release), shape));
1072 shape->_modified_connect[key] = marker->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_shape_marker_modified), shape));
1073 }
1074 }
1075 }
1076
1077 // CPPIFY: make pure virtual
set_shape()1078 void SPShape::set_shape() {
1079 //throw;
1080 }
1081
1082 /* Shape section */
1083
_setCurve(std::unique_ptr<SPCurve> && new_curve,bool update_display)1084 void SPShape::_setCurve(std::unique_ptr<SPCurve> &&new_curve, bool update_display)
1085 {
1086 _curve = std::move(new_curve);
1087
1088 if (update_display) {
1089 requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1090 }
1091 }
_setCurve(SPCurve const * new_curve,bool update_display)1092 void SPShape::_setCurve(SPCurve const *new_curve, bool update_display)
1093 {
1094 _setCurve(SPCurve::copy(new_curve), update_display);
1095 }
1096
1097 /**
1098 * Adds a curve to the shape.
1099 * Any existing curve in the shape will be unreferenced first.
1100 * This routine also triggers a request to update the display.
1101 */
setCurve(std::unique_ptr<SPCurve> && new_curve)1102 void SPShape::setCurve(std::unique_ptr<SPCurve> &&new_curve)
1103 {
1104 _setCurve(std::move(new_curve), true);
1105 }
setCurve(SPCurve const * new_curve)1106 void SPShape::setCurve(SPCurve const *new_curve)
1107 {
1108 _setCurve(new_curve, true);
1109 }
1110
1111 /**
1112 * Sets _curve_before_lpe to a copy of `new_curve`
1113 */
setCurveBeforeLPE(std::unique_ptr<SPCurve> && new_curve)1114 void SPShape::setCurveBeforeLPE(std::unique_ptr<SPCurve> &&new_curve)
1115 {
1116 _curve_before_lpe = std::move(new_curve);
1117 }
setCurveBeforeLPE(SPCurve const * new_curve)1118 void SPShape::setCurveBeforeLPE(SPCurve const *new_curve)
1119 {
1120 setCurveBeforeLPE(SPCurve::copy(new_curve));
1121 }
1122
1123 /**
1124 * Same as setCurve() but without updating the display
1125 */
setCurveInsync(std::unique_ptr<SPCurve> && new_curve)1126 void SPShape::setCurveInsync(std::unique_ptr<SPCurve> &&new_curve)
1127 {
1128 _setCurve(std::move(new_curve), false);
1129 }
setCurveInsync(SPCurve const * new_curve)1130 void SPShape::setCurveInsync(SPCurve const *new_curve)
1131 {
1132 _setCurve(new_curve, false);
1133 }
1134
1135 /**
1136 * Return a borrowed pointer to the curve (if any exists) or NULL if there is no curve
1137 */
curve()1138 SPCurve *SPShape::curve()
1139 {
1140 return _curve.get();
1141 }
curve() const1142 SPCurve const *SPShape::curve() const
1143 {
1144 return _curve.get();
1145 }
1146
1147 /**
1148 * Return a borrowed pointer of the curve *before* LPE (if any exists) or NULL if there is no curve
1149 */
curveBeforeLPE() const1150 SPCurve const *SPShape::curveBeforeLPE() const
1151 {
1152 return _curve_before_lpe.get();
1153 }
1154
1155 /**
1156 * Return a borrowed pointer of the curve for edit
1157 */
curveForEdit() const1158 SPCurve const *SPShape::curveForEdit() const
1159 {
1160 if (_curve_before_lpe) {
1161 return _curve_before_lpe.get();
1162 }
1163 return curve();
1164 }
1165
snappoints(std::vector<Inkscape::SnapCandidatePoint> & p,Inkscape::SnapPreferences const * snapprefs) const1166 void SPShape::snappoints(std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs) const {
1167 if (this->_curve == nullptr) {
1168 return;
1169 }
1170
1171 Geom::PathVector const &pathv = this->_curve->get_pathvector();
1172
1173 if (pathv.empty()) {
1174 return;
1175 }
1176
1177 Geom::Affine const i2dt (this->i2dt_affine ());
1178
1179 if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_OBJECT_MIDPOINT)) {
1180 Geom::OptRect bbox = this->desktopVisualBounds();
1181
1182 if (bbox) {
1183 p.emplace_back(bbox->midpoint(), Inkscape::SNAPSOURCE_OBJECT_MIDPOINT, Inkscape::SNAPTARGET_OBJECT_MIDPOINT);
1184 }
1185 }
1186
1187 for(const auto & path_it : pathv) {
1188 if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_NODE_CUSP)) {
1189 // Add the first point of the path
1190 p.emplace_back(path_it.initialPoint() * i2dt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP);
1191 }
1192
1193 Geom::Path::const_iterator curve_it1 = path_it.begin(); // incoming curve
1194 Geom::Path::const_iterator curve_it2 = ++(path_it.begin()); // outgoing curve
1195
1196 while (curve_it1 != path_it.end_default())
1197 {
1198 // For each path: consider midpoints of line segments for snapping
1199 if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_LINE_MIDPOINT)) {
1200 if (Geom::LineSegment const* line_segment = dynamic_cast<Geom::LineSegment const*>(&(*curve_it1))) {
1201 p.emplace_back(Geom::middle_point(*line_segment) * i2dt, Inkscape::SNAPSOURCE_LINE_MIDPOINT, Inkscape::SNAPTARGET_LINE_MIDPOINT);
1202 }
1203 }
1204
1205 if (curve_it2 == path_it.end_default()) { // Test will only pass for the last iteration of the while loop
1206 if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_NODE_CUSP) && !path_it.closed()) {
1207 // Add the last point of the path, but only for open paths
1208 // (for closed paths the first and last point will coincide)
1209 p.emplace_back((*curve_it1).finalPoint() * i2dt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP);
1210 }
1211 } else {
1212 /* Test whether to add the node between curve_it1 and curve_it2.
1213 * Loop to end_default (so only iterating through the stroked part); */
1214
1215 Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2);
1216
1217 bool c1 = snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_NODE_CUSP) && (nodetype == Geom::NODE_CUSP || nodetype == Geom::NODE_NONE);
1218 bool c2 = snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_NODE_SMOOTH) && (nodetype == Geom::NODE_SMOOTH || nodetype == Geom::NODE_SYMM);
1219
1220 if (c1 || c2) {
1221 Inkscape::SnapSourceType sst;
1222 Inkscape::SnapTargetType stt;
1223
1224 switch (nodetype) {
1225 case Geom::NODE_CUSP:
1226 sst = Inkscape::SNAPSOURCE_NODE_CUSP;
1227 stt = Inkscape::SNAPTARGET_NODE_CUSP;
1228 break;
1229 case Geom::NODE_SMOOTH:
1230 case Geom::NODE_SYMM:
1231 sst = Inkscape::SNAPSOURCE_NODE_SMOOTH;
1232 stt = Inkscape::SNAPTARGET_NODE_SMOOTH;
1233 break;
1234 default:
1235 sst = Inkscape::SNAPSOURCE_UNDEFINED;
1236 stt = Inkscape::SNAPTARGET_UNDEFINED;
1237 break;
1238 }
1239
1240 p.emplace_back(curve_it1->finalPoint() * i2dt, sst, stt);
1241 }
1242 }
1243
1244 ++curve_it1;
1245 ++curve_it2;
1246 }
1247
1248 // Find the internal intersections of each path and consider these for snapping
1249 // (using "Method 1" as described in Inkscape::ObjectSnapper::_collectNodes())
1250 if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_PATH_INTERSECTION) || snapprefs->isSourceSnappable(Inkscape::SNAPSOURCE_PATH_INTERSECTION)) {
1251 Geom::Crossings cs;
1252
1253 try {
1254 cs = self_crossings(path_it); // This can be slow!
1255
1256 if (!cs.empty()) { // There might be multiple intersections...
1257 for (const auto & c : cs) {
1258 Geom::Point p_ix = path_it.pointAt(c.ta);
1259 p.emplace_back(p_ix * i2dt, Inkscape::SNAPSOURCE_PATH_INTERSECTION, Inkscape::SNAPTARGET_PATH_INTERSECTION);
1260 }
1261 }
1262 } catch (Geom::RangeError &e) {
1263 // do nothing
1264 // The exception could be Geom::InfiniteSolutions: then no snappoints should be added
1265 }
1266
1267 }
1268 }
1269 }
1270
1271 /*
1272 Local Variables:
1273 mode:c++
1274 c-file-style:"stroustrup"
1275 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1276 indent-tabs-mode:nil
1277 fill-column:99
1278 End:
1279 */
1280 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
1281