Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
sp-item.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Authors:
4 * Lauris Kaplinski <lauris@kaplinski.com>
5 * bulia byak <buliabyak@users.sf.net>
6 * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
7 * Abhishek Sharma
8 * Jon A. Cruz <jon@joncruz.org>
9 *
10 * Copyright (C) 2001-2006 authors
11 * Copyright (C) 2001 Ximian, Inc.
12 *
13 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
14 */
15
16#include "sp-item.h"
17
18#include <algorithm>
19#include <glibmm/i18n.h>
20
21#include "colors/manager.h"
22#include "helper/geom.h"
23#include "path/path-util.h"
24#include "svg/svg.h"
25#include "print.h"
27#include "attributes.h"
28#include "document.h"
29#include "preferences.h"
30
31#include "inkscape.h"
32#include "desktop.h"
33#include "gradient-chemistry.h"
34#include "conn-avoid-ref.h"
35#include "conditions.h"
36#include "enums.h"
37#include "filter-chemistry.h"
38
39#include "sp-clippath.h"
40#include "sp-desc.h"
41#include "sp-guide.h"
42#include "sp-hatch.h"
43#include "sp-mask.h"
44#include "sp-pattern.h"
45#include "sp-rect.h"
46#include "sp-root.h"
47#include "sp-switch.h"
48#include "sp-text.h"
49#include "sp-textpath.h"
50#include "sp-title.h"
51#include "sp-use.h"
52
53#include "style.h"
54#include "display/nr-filter.h"
55#include "snap-preferences.h"
56#include "snap-candidate.h"
57
58#include "extract-uri.h"
60#include "live_effects/effect.h"
62
63#include "ui/util.h"
64#include "util/units.h"
65
66#define noSP_ITEM_DEBUG_IDLE
67
68//#define OBJECT_TRACE
69
71 : flags(flags)
72 , key(key)
73 , drawingitem(std::move(drawingitem)) {}
74
76{
77 sensitive = TRUE;
78 bbox_valid = FALSE;
79
82
83 freeze_stroke_width = false;
84 _is_evaluated = true;
86
88 // doc_bbox = Geom::OptRect();
89
90 clip_ref = nullptr;
91 mask_ref = nullptr;
92
93 style->signal_fill_ps_changed.connect ([this] (auto old_obj, auto obj) { fill_ps_ref_changed (old_obj, obj); });
94 style->signal_stroke_ps_changed.connect([this] (auto old_obj, auto obj) { stroke_ps_ref_changed(old_obj, obj); });
95 style->signal_filter_changed.connect ([this] (auto old_obj, auto obj) { filter_ref_changed (old_obj, obj); });
96
97 avoidRef = nullptr;
98}
99
100SPItem::~SPItem() = default;
101
103{
104 return clip_ref ? clip_ref->getObject() : nullptr;
105}
106
112std::optional<Geom::PathVector> SPItem::getClipPathVector() const
113{
114 if (auto clip = getClipObject()) {
116 }
117 return {};
118}
119
127{
128 if (auto clip = getClipObject()) {
129 return clip->getTextObject();
130 }
131 return nullptr;
132}
133
141std::optional<Geom::PathVector> SPItem::getClipPathVector(SPItem const *root) const
142{
143 auto intersection = getClipPathVector();
144
145 auto cumulative_transform = transform.inverse();
146
147 // Traverse ancestors from parent to root inclusive.
148 for (auto anc = parent; ; anc = anc->parent) {
149 if (!anc) {
150 std::cerr << "SPItem::getClipPathVector: precondition violation: root specified but not an ancestor" << std::endl;
151 break;
152 }
153
154 // Bail out if fully clipped.
155 if (intersection && intersection->empty()) {
156 return intersection;
157 }
158
159 auto const anc_item = cast<SPItem>(anc);
160 assert(anc_item);
161
162 // Combine with clip of ancestor.
163 if (auto clip = anc_item->getClipObject()) {
164 intersection = intersect_clips(std::move(intersection), clip->getPathVector(cumulative_transform));
165 }
166
167 if (anc == root) {
168 break;
169 }
170
171 cumulative_transform = anc_item->transform.inverse() * cumulative_transform;
172 }
173
174 return intersection;
175}
176
178{
179 return mask_ref ? mask_ref->getObject() : nullptr;
180}
181
183{
184 if (!mask_ref) {
185 mask_ref = new SPMaskReference(this);
186 mask_ref->changedSignal().connect([this] (auto old_obj, auto obj) { mask_ref_changed(old_obj, obj); });
187 }
188
189 return *mask_ref;
190}
191
193{
194 if (!clip_ref) {
195 clip_ref = new SPClipPathReference(this);
196 clip_ref->changedSignal().connect([this] (auto old_obj, auto obj) { clip_ref_changed(old_obj, obj); });
197 }
198
199 return *clip_ref;
200}
201
203{
204 if (!avoidRef) {
205 avoidRef = new SPAvoidRef(this);
206 }
207 return *avoidRef;
208}
209
211 return !isHidden() && !isLocked();
212}
213
214bool SPItem::isVisibleAndUnlocked(unsigned display_key) const {
215 return !isHidden(display_key) && !isLocked();
216}
217
218bool SPItem::isLocked() const {
219 for (SPObject const *o = this; o != nullptr; o = o->parent) {
220 SPItem const *item = cast<SPItem>(o);
221 if (item && !(item->sensitive)) {
222 return true;
223 }
224 }
225 return false;
226}
227
228void SPItem::setLocked(bool locked) {
229 setAttribute("sodipodi:insensitive",
230 ( locked ? "1" : nullptr ));
231 updateRepr();
233}
234
235bool SPItem::isHidden() const {
236 if (!isEvaluated())
237 return true;
238 return style->display.computed == SP_CSS_DISPLAY_NONE;
239}
240
241void SPItem::setHidden(bool hide) {
242 style->display.set = TRUE;
244 style->display.computed = style->display.value;
245 style->display.inherit = FALSE;
246 updateRepr();
247}
248
249bool SPItem::isHidden(unsigned display_key) const
250{
251 if (!isEvaluated()) {
252 return true;
253 }
254 for (auto &v : views) {
255 if (v.key == display_key) {
256 g_assert(v.drawingitem);
257 for (auto di = v.drawingitem.get(); di; di = di->parent()) {
258 if (!di->visible()) {
259 return true;
260 }
261 }
262 return false;
263 }
264 }
265 return true;
266}
267
269 _highlightColor = std::move(color);
270 updateRepr();
271}
272
274 return _highlightColor.has_value();
275}
276
278 if (isHighlightSet()) {
279 return *_highlightColor;
280 }
281
282 SPItem const *item = cast<SPItem>(parent);
283 if (parent && (parent != this) && item) {
284 return item->highlight_color();
285 }
286 auto prefs = Inkscape::Preferences::get();
287 return prefs->getColor("/tools/nodes/highlight_color", "#aaaaaaff");
288}
289
290void SPItem::setEvaluated(bool evaluated) {
291 _is_evaluated = evaluated;
293}
294
298 bool oldValue = _is_evaluated;
299 if ( oldValue != isEvaluated() ) {
300 requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
301 }
302 } if ( StatusSet == _evaluated_status ) {
303 auto switchItem = cast<SPSwitch>(parent);
304 if (switchItem) {
305 switchItem->resetChildEvaluated();
306 }
307 }
308}
309
317
319{
320 return (style->display.set
321 && style->display.value == SP_CSS_DISPLAY_NONE);
322}
323
325 style->display.set = val;
327 style->display.computed = style->display.value;
328 updateRepr();
329}
330
331void SPItem::setCenter(Geom::Point const &object_centre) {
333
334 // Copied from DocumentProperties::onDocUnitChange()
335 gdouble viewscale = 1.0;
336 Geom::Rect vb = this->document->getRoot()->viewBox;
337 if ( !vb.hasZeroArea() ) {
338 gdouble viewscale_w = this->document->getWidth().value("px") / vb.width();
339 gdouble viewscale_h = this->document->getHeight().value("px")/ vb.height();
340 viewscale = std::min(viewscale_h, viewscale_w);
341 }
342
343 // FIXME this is seriously wrong
345 if (bbox) {
346 // object centre is document coordinates (i.e. in pixels), so we need to consider the viewbox
347 // to translate to user units; transform_center_x/y is in user units
348 transform_center_x = (object_centre[Geom::X] - bbox->midpoint()[Geom::X])/viewscale;
349 if (Geom::are_near(transform_center_x, 0)) // rounding error
351 transform_center_y = (object_centre[Geom::Y] - bbox->midpoint()[Geom::Y])/viewscale;
352 if (Geom::are_near(transform_center_y, 0)) // rounding error
354 }
355}
356
358{
359 if (isCenterSet()) {
360 setCenter(center);
361 return true;
362 }
363 return false;
364}
365
366void
371
373 return (transform_center_x != 0 || transform_center_y != 0);
374}
375
376// Get the item's transformation center in desktop coordinates (i.e. in pixels)
377Geom::Point SPItem::getCenter(bool ensure_uptodate) const {
378 // This call is very bad as it can cascade to thousands of calls
379 // it should be removed in future refactoring as document scale should be
380 // cached and controlled properly elsewhere.
381 if (ensure_uptodate)
383
384 // Copied from DocumentProperties::onDocUnitChange()
385 gdouble viewscale = 1.0;
386 Geom::Rect vb = this->document->getRoot()->viewBox;
387 if ( !vb.hasZeroArea() ) {
388 gdouble viewscale_w = this->document->getWidth().value("px") / vb.width();
389 gdouble viewscale_h = this->document->getHeight().value("px")/ vb.height();
390 viewscale = std::min(viewscale_h, viewscale_w);
391 }
392
393 // FIXME this is seriously wrong
395 if (bbox) {
396 // transform_center_x/y are stored in user units, so we have to take the viewbox into account to translate to document coordinates
397 return bbox->midpoint() + Geom::Point (transform_center_x*viewscale, transform_center_y*viewscale);
398
399 } else {
400 return Geom::Point(0, 0); // something's wrong!
401 }
402
403}
404
405void
410
411namespace {
412
413bool is_item(SPObject const &object) {
414 return cast<SPItem>(&object) != nullptr;
415}
416
417}
418
420 auto& list = parent->children;
421 auto end = SPObject::ChildrenList::reverse_iterator(list.iterator_to(*this));
422 auto topmost = std::find_if(list.rbegin(), end, &is_item);
423 // auto topmost = find_last_if(++parent->children.iterator_to(*this), parent->children.end(), &is_item);
424 if (topmost != list.rend()) {
425 getRepr()->parent()->changeOrder(getRepr(), topmost->getRepr());
426 }
427}
428
430 auto next_higher = std::find_if(++parent->children.iterator_to(*this), parent->children.end(), &is_item);
431 if (next_higher != parent->children.end()) {
432 Inkscape::XML::Node *ref = next_higher->getRepr();
434 return true;
435 }
436 return false;
437}
438
440 auto& list = parent->children;
441 auto self = list.iterator_to(*this);
442 auto start = SPObject::ChildrenList::reverse_iterator(self);
443 auto next_lower = std::find_if(start, list.rend(), &is_item);
444 if (next_lower != list.rend()) {
445 auto next = list.iterator_to(*next_lower);
446 if (next == list.begin()) {
447 getRepr()->parent()->changeOrder(getRepr(), nullptr);
448 } else {
449 --next;
450 auto ref = next->getRepr();
452 }
453 return true;
454 }
455 return false;
456}
457
459 auto bottom = std::find_if(parent->children.begin(), parent->children.iterator_to(*this), &is_item);
460 if (bottom != parent->children.iterator_to(*this)) {
461 Inkscape::XML::Node *ref = nullptr;
462 if (bottom != parent->children.begin()) {
463 bottom--;
464 ref = bottom->getRepr();
465 }
467 }
468}
469
474{
475 return cast<SPGroup>(parent);
476}
477
478void SPItem::moveTo(SPItem *target, bool intoafter) {
479
480 Inkscape::XML::Node *target_ref = ( target ? target->getRepr() : nullptr );
481 Inkscape::XML::Node *our_ref = getRepr();
482
483 if (!target_ref) {
484 // Assume move to the "first" in the top node, find the top node
485 intoafter = false;
486 SPObject* bottom = this->document->getObjectByRepr(our_ref->root())->firstChild();
487 while (!is<SPItem>(bottom->getNext())) {
488 bottom = bottom->getNext();
489 }
490 target_ref = bottom->getRepr();
491 }
492
493 if (target_ref == our_ref) {
494 // Move to ourself ignore
495 return;
496 }
497
498 if (intoafter) {
499 // Move this inside of the target at the end
500 our_ref->parent()->removeChild(our_ref);
501 target_ref->addChild(our_ref, nullptr);
502 } else if (target_ref->parent() != our_ref->parent()) {
503 // Change in parent, need to remove and add
504 our_ref->parent()->removeChild(our_ref);
505 target_ref->parent()->addChild(our_ref, target_ref);
506 } else {
507 // Same parent, just move
508 our_ref->parent()->changeOrder(our_ref, target_ref);
509 }
510}
511
513#ifdef OBJECT_TRACE
514 objectTrace( "SPItem::build");
515#endif
516
517 SPItem* object = this;
518 object->readAttr(SPAttr::STYLE);
519 object->readAttr(SPAttr::TRANSFORM);
520 object->readAttr(SPAttr::CLIP_PATH);
521 object->readAttr(SPAttr::MASK);
522 object->readAttr(SPAttr::SODIPODI_INSENSITIVE);
523 object->readAttr(SPAttr::TRANSFORM_CENTER_X);
524 object->readAttr(SPAttr::TRANSFORM_CENTER_Y);
525 object->readAttr(SPAttr::CONNECTOR_AVOID);
526 object->readAttr(SPAttr::CONNECTION_POINTS);
527 object->readAttr(SPAttr::INKSCAPE_HIGHLIGHT_COLOR);
528
530#ifdef OBJECT_TRACE
531 objectTrace( "SPItem::build", false);
532#endif
533}
534
536{
537 // Note: do this here before the clip_ref is deleted, since calling
538 // ensureUpToDate() for triggered routing may reference
539 // the deleted clip_ref.
540 delete avoidRef;
541 avoidRef = nullptr;
542
543 // we do NOT disconnect from the changed signal of those before deletion.
544 // The destructor will call *_ref_changed with NULL as the new value,
545 // which will cause the set_visible(false) function to be called.
546 delete clip_ref;
547 clip_ref = nullptr;
548 delete mask_ref;
549 mask_ref = nullptr;
550
551 // the first thing SPObject::release() does is destroy the fill/stroke/filter references.
552 // as above, this calls *_ref_changed() which performs the set_visible(false).
553 // it is important this happens before the views are cleared.
555
556 views.clear();
557}
558
559void SPItem::set(SPAttr key, gchar const* value) {
560#ifdef OBJECT_TRACE
561 std::stringstream temp;
562 temp << "SPItem::set: " << sp_attribute_name(key) << " " << (value?value:"null");
563 objectTrace( temp.str() );
564#endif
565 SPItem *item = this;
566 SPItem* object = item;
567
568 switch (key) {
569 case SPAttr::TRANSFORM: {
570 Geom::Affine t;
571 if (value && sp_svg_transform_read(value, &t)) {
573 } else {
575 }
576 break;
577 }
578 case SPAttr::CLIP_PATH: {
579 auto uri = extract_uri(value);
580 if (!uri.empty() || item->clip_ref) {
581 item->getClipRef().try_attach(uri.c_str());
582 }
583 break;
584 }
585 case SPAttr::MASK: {
586 auto uri = extract_uri(value);
587 if (!uri.empty() || item->mask_ref) {
588 item->getMaskRef().try_attach(uri.c_str());
589 }
590 break;
591 }
593 {
594 item->sensitive = !value;
595 for (auto &v : item->views) {
596 v.drawingitem->setSensitive(item->sensitive);
597 }
598 break;
599 }
601 {
602 item->_highlightColor = Inkscape::Colors::Color::parse(value);
603 break;
604 }
606 if (value || item->avoidRef) {
607 item->getAvoidRef().setAvoid(value);
608 }
609 break;
611 if (value) {
612 item->transform_center_x = g_strtod(value, nullptr);
613 } else {
615 }
616 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
617 break;
619 if (value) {
620 item->transform_center_y = g_strtod(value, nullptr);
622 } else {
624 }
625 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
626 break;
630 {
632 // pass to default handler
633 }
634 default:
636 // FIXME: See if this is really necessary. Also, check after modifying SPIPaint to preserve
637 // non-#abcdef color formats.
638
639 // Propagate the property change to all clones
640 style->readFromObject(object);
641 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
642 } else {
643 SPObject::set(key, value);
644 }
645 break;
646 }
647#ifdef OBJECT_TRACE
648 objectTrace( "SPItem::set", false);
649#endif
650}
651
652template <typename F>
653class lazy
654{
655public:
656 explicit lazy(F &&f): f(std::move(f)) {}
657
658 auto operator()()
659 {
660 if (!result) result = f();
661 return *result;
662 }
663
664private:
665 F f;
666 std::optional<typename std::invoke_result<F>::type> result;
667};
668
670{
671 if (old_clip) {
672 clip_ref->modified_connection.disconnect();
673 for (auto &v : views) {
674 auto oldPath = cast<SPClipPath>(old_clip);
675 g_assert(oldPath);
676 oldPath->hide(v.drawingitem->key() + ITEM_KEY_CLIP);
677 }
678 }
679 auto clipPath = cast<SPClipPath>(clip);
680 if (clipPath) {
682 for (auto &v : views) {
683 auto clip_key = SPItem::ensure_key(v.drawingitem.get()) + ITEM_KEY_CLIP;
684 auto ai = clipPath->show(v.drawingitem->drawing(), clip_key, bbox);
685 v.drawingitem->setClip(ai);
686 }
687 clip_ref->modified_connection = clipPath->connectModified([this] (auto, unsigned flags) {
688 if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)) {
689 requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
690 }
691 });
692 }
693 requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); // To update bbox.
694}
695
697{
698 if (old_mask) {
699 mask_ref->modified_connection.disconnect();
700 for (auto &v : views) {
701 auto maskItem = cast<SPMask>(old_mask);
702 g_assert(maskItem);
703 maskItem->hide(v.drawingitem->key() + ITEM_KEY_MASK);
704 }
705 }
706 auto maskItem = cast<SPMask>(mask);
707 if (maskItem) {
709 for (auto &v : views) {
710 auto mask_key = SPItem::ensure_key(v.drawingitem.get()) + ITEM_KEY_MASK;
711 auto ai = maskItem->show(v.drawingitem->drawing(), mask_key, bbox);
712 v.drawingitem->setMask(ai);
713 }
714 mask_ref->modified_connection = maskItem->connectModified([this] (auto, unsigned flags) {
715 if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)) {
716 requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
717 }
718 });
719 }
720 requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); // To update bbox.
721}
722
724{
725 auto old_fill_ps = cast<SPPaintServer>(old_ps);
726 if (old_fill_ps) {
727 for (auto &v : views) {
728 old_fill_ps->hide(v.drawingitem->key() + ITEM_KEY_FILL);
729 }
730 }
731
732 auto new_fill_ps = cast<SPPaintServer>(ps);
733 if (new_fill_ps) {
735 for (auto &v : views) {
736 auto fill_key = SPItem::ensure_key(v.drawingitem.get()) + ITEM_KEY_FILL;
737 auto pi = new_fill_ps->show(v.drawingitem->drawing(), fill_key, bbox);
738 v.drawingitem->setFillPattern(pi);
739 }
740 }
741}
742
744{
745 auto old_stroke_ps = cast<SPPaintServer>(old_ps);
746 if (old_stroke_ps) {
747 for (auto &v : views) {
748 old_stroke_ps->hide(v.drawingitem->key() + ITEM_KEY_STROKE);
749 }
750 }
751
752 auto new_stroke_ps = cast<SPPaintServer>(ps);
753 if (new_stroke_ps) {
755 for (auto &v : views) {
756 auto stroke_key = SPItem::ensure_key(v.drawingitem.get()) + ITEM_KEY_STROKE;
757 auto pi = new_stroke_ps->show(v.drawingitem->drawing(), stroke_key, bbox);
758 v.drawingitem->setStrokePattern(pi);
759 }
760 }
761}
762
764{
765 auto old_filter = cast<SPFilter>(old_obj);
766 if (old_filter) {
767 for (auto &v : views) {
768 old_filter->hide(v.drawingitem.get());
769 }
770 }
771
772 auto new_filter = cast<SPFilter>(obj);
773 if (new_filter) {
774 for (auto &v : views) {
775 new_filter->show(v.drawingitem.get());
776 }
777 }
778}
779
780void SPItem::update(SPCtx *ctx, unsigned flags)
781{
782 auto ictx = static_cast<SPItemCtx const*>(ctx);
783
784 // Any of the modifications defined in sp-object.h might change bbox,
785 // so we invalidate it unconditionally
786 bbox_valid = false;
787
788 viewport = ictx->viewport; // Cache viewport
789
790 auto bbox = lazy([this] {
791 return geometricBounds();
792 });
793
794 if (flags & (SP_OBJECT_CHILD_MODIFIED_FLAG |
795 SP_OBJECT_MODIFIED_FLAG |
796 SP_OBJECT_STYLE_MODIFIED_FLAG))
797 {
798 if (flags & SP_OBJECT_MODIFIED_FLAG) {
799 for (auto &v : views) {
800 v.drawingitem->setTransform(transform);
801 }
802 }
803
804 auto set_bboxes = [&, this] (auto obj, int type) {
805 if (obj) {
806 for (auto &v : views) {
807 obj->setBBox(v.drawingitem->key() + type, bbox());
808 }
809 }
810 };
811
812 set_bboxes(getClipObject(), ITEM_KEY_CLIP);
813 set_bboxes(getMaskObject(), ITEM_KEY_MASK);
814 set_bboxes(style->getFillPaintServer(), ITEM_KEY_FILL);
816
817 if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
818 for (auto &v : views) {
819 v.drawingitem->setOpacity(SP_SCALE24_TO_FLOAT(style->opacity.value));
821 v.drawingitem->setIsolation(style->isolation.value);
822 v.drawingitem->setBlendMode(style->mix_blend_mode.value);
823 v.drawingitem->setVisible(!isHidden());
824 }
825 }
826 }
827
828 // Update bounding box in user space, used for filter and objectBoundingBox units.
829 if (style->filter.set) {
830 for (auto &v : views) {
831 if (v.drawingitem) {
832 v.drawingitem->setItemBounds(bbox());
833 }
834 }
835 }
836
837 // Update libavoid with item geometry (for connector routing).
838 if (avoidRef && document) {
840 }
841}
842
843void SPItem::modified(unsigned int /*flags*/)
844{
845#ifdef OBJECT_TRACE
846 objectTrace( "SPItem::modified" );
847 objectTrace( "SPItem::modified", false );
848#endif
849}
850
852 SPItem *item = this;
853 SPItem* object = item;
854
855 // in the case of SP_OBJECT_WRITE_BUILD, the item should always be newly created,
856 // so we need to add any children from the underlying object to the new repr
857 if (flags & SP_OBJECT_WRITE_BUILD) {
858 std::vector<Inkscape::XML::Node *>l;
859 for (auto& child: object->children) {
860 if (is<SPTitle>(&child) || is<SPDesc>(&child)) {
861 Inkscape::XML::Node *crepr = child.updateRepr(xml_doc, nullptr, flags);
862 if (crepr) {
863 l.push_back(crepr);
864 }
865 }
866 }
867 for (auto i = l.rbegin(); i!= l.rend(); ++i) {
868 repr->addChild(*i, nullptr);
870 }
871 } else {
872 for (auto& child: object->children) {
873 if (is<SPTitle>(&child) || is<SPDesc>(&child)) {
874 child.updateRepr(flags);
875 }
876 }
877 }
878
880
881 if (flags & SP_OBJECT_WRITE_EXT) {
882 repr->setAttribute("sodipodi:insensitive", ( item->sensitive ? nullptr : "true" ));
883 if (item->transform_center_x != 0)
884 repr->setAttributeSvgDouble("inkscape:transform-center-x", item->transform_center_x);
885 else
886 repr->removeAttribute("inkscape:transform-center-x");
887 if (item->transform_center_y != 0) {
888 auto y = item->transform_center_y;
889 y *= -document->yaxisdir();
890 repr->setAttributeSvgDouble("inkscape:transform-center-y", y);
891 } else
892 repr->removeAttribute("inkscape:transform-center-y");
893 }
894
895 if (getClipObject()) {
896 auto value = item->clip_ref->getURI()->cssStr();
897 repr->setAttributeOrRemoveIfEmpty("clip-path", value);
898 }
899 if (getMaskObject()) {
900 auto value = item->mask_ref->getURI()->cssStr();
901 repr->setAttributeOrRemoveIfEmpty("mask", value);
902 }
903 if (item->isHighlightSet()) {
904 repr->setAttribute("inkscape:highlight-color", item->_highlightColor->toString());
905 } else {
906 repr->removeAttribute("inkscape:highlight-color");
907 }
908
909 SPObject::write(xml_doc, repr, flags);
910
911 return repr;
912}
913
914// CPPIFY: make pure virtual
915Geom::OptRect SPItem::bbox(Geom::Affine const & /*transform*/, SPItem::BBoxType /*type*/) const {
916 //throw;
917 return Geom::OptRect();
918}
919
924
925Geom::OptRect SPItem::visualBounds(Geom::Affine const &transform, bool wfilter, bool wclip, bool wmask) const
926{
928
929 auto gbox = lazy([this] {
930 return geometricBounds();
931 });
932
933 if (auto filter = style ? style->getFilter() : nullptr; filter && wfilter) {
934 // call the subclass method
935 bbox = gbox(); // see LP Bug 1229971
936
937 // default filter area per the SVG spec:
938 SVGLength x, y, w, h;
939 x.set(SVGLength::PERCENT, -0.10, 0);
940 y.set(SVGLength::PERCENT, -0.10, 0);
941 w.set(SVGLength::PERCENT, 1.20, 0);
942 h.set(SVGLength::PERCENT, 1.20, 0);
943
944 // if area is explicitly set, override:
945 if (filter->x._set) x = filter->x;
946 if (filter->y._set) y = filter->y;
947 if (filter->width._set) w = filter->width;
948 if (filter->height._set) h = filter->height;
949
950 auto const len = bbox ? bbox->dimensions() : Geom::Point();
951
952 x.update(12, 6, len.x());
953 y.update(12, 6, len.y());
954 w.update(12, 6, len.x());
955 h.update(12, 6, len.y());
956
957 if (filter->filterUnits == SP_FILTER_UNITS_OBJECTBOUNDINGBOX && bbox) {
959 bbox->left() + x.computed * (x.unit == SVGLength::PERCENT ? 1.0 : len.x()),
960 bbox->top() + y.computed * (y.unit == SVGLength::PERCENT ? 1.0 : len.y()),
961 w.computed * (w.unit == SVGLength::PERCENT ? 1.0 : len.x()),
962 h.computed * (h.unit == SVGLength::PERCENT ? 1.0 : len.y())
963 );
964 } else {
966 }
967
968 *bbox *= transform;
969 } else {
970 // call the subclass method
971 bbox = this->bbox(transform, SPItem::VISUAL_BBOX);
972 }
973
974 auto transform_with_units = [&] (bool contentunits) {
975 return contentunits == SP_CONTENT_UNITS_OBJECTBOUNDINGBOX && gbox()
976 ? Geom::Scale(gbox()->dimensions()) * Geom::Translate(gbox()->min()) * transform
977 : transform;
978 };
979
980 auto apply_clip_or_mask_bbox = [&] (auto const *obj, bool contentunits) {
981 bbox.intersectWith(obj->geometricBounds(transform_with_units(contentunits)));
982 };
983
984 if (auto clip = getClipObject(); clip && wclip) {
985 apply_clip_or_mask_bbox(clip, clip->clippath_units());
986 }
987
988 if (auto mask = getMaskObject(); mask && wmask) {
989 apply_clip_or_mask_bbox(mask, mask->mask_content_units());
990 }
991
992 return bbox;
993}
994
996{
997 if (type == GEOMETRIC_BBOX) {
999 } else {
1000 return visualBounds(transform);
1001 }
1002}
1003
1005{
1006 if (Inkscape::Preferences::get()->getInt("/tools/bounding_box") == 0) {
1008 } else {
1010 }
1011}
1012
1017
1019{
1020 if (!bbox_valid) {
1022 bbox_valid = true;
1023 }
1024 return doc_bbox;
1025}
1027{
1028 if (type == GEOMETRIC_BBOX) {
1029 return documentGeometricBounds();
1030 } else {
1031 return documentVisualBounds();
1032 }
1033}
1034
1035std::optional<Geom::PathVector> SPItem::documentExactBounds() const
1036{
1037 std::optional<Geom::PathVector> result;
1038 if (auto bounding_rect = visualBounds()) {
1039 result = Geom::Path(*bounding_rect) * i2doc_affine();
1040 }
1041 return result;
1042}
1043
1048
1050{
1052 if (ret) {
1053 *ret *= document->doc2dt();
1054 }
1055 return ret;
1056}
1057
1059{
1060 if (Inkscape::Preferences::get()->getInt("/tools/bounding_box") == 0) {
1062 } else {
1064 }
1065}
1066
1068{
1069 if (type == GEOMETRIC_BBOX) {
1070 return desktopGeometricBounds();
1071 } else {
1072 return desktopVisualBounds();
1073 }
1074}
1075
1076unsigned int SPItem::pos_in_parent() const {
1077 g_assert(parent != nullptr);
1078
1079 unsigned int pos = 0;
1080
1081 for (auto& iter: parent->children) {
1082 if (&iter == this) {
1083 return pos;
1084 }
1085
1086 if (is<SPItem>(&iter)) {
1087 pos++;
1088 }
1089 }
1090
1091 g_assert_not_reached();
1092 return 0;
1093}
1094
1095// CPPIFY: make pure virtual, see below!
1096void SPItem::snappoints(std::vector<Inkscape::SnapCandidatePoint> & /*p*/, Inkscape::SnapPreferences const */*snapprefs*/) const {
1097 //throw;
1098}
1099 /* This will only be called if the derived class doesn't override this.
1100 * see for example sp_genericellipse_snappoints in sp-ellipse.cpp
1101 * We don't know what shape we could be dealing with here, so we'll just
1102 * do nothing
1103 */
1104
1105void SPItem::getSnappoints(std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs) const
1106{
1107 // Get the snappoints of the item
1108 snappoints(p, snapprefs);
1109
1110 // Get the snappoints at the item's center
1111 if (snapprefs && snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_ROTATION_CENTER)) {
1113 }
1114
1115 // Get the snappoints of clipping paths and mask, if any
1116 auto desktop = SP_ACTIVE_DESKTOP;
1117
1118 auto gbox = lazy([this] {
1119 return geometricBounds();
1120 });
1121
1122 auto add_clip_or_mask_points = [&, this] (SPObject const *obj, bool contentunits) {
1123 // obj is a group object, the children are the actual clippers
1124 for (auto &child: obj->children) {
1125 if (auto item = cast<SPItem>(&child)) {
1126 std::vector<Inkscape::SnapCandidatePoint> p_clip_or_mask;
1127 // Please note the recursive call here!
1128 item->getSnappoints(p_clip_or_mask, snapprefs);
1129 // Take into account the transformation of the item being clipped or masked
1130 for (auto const &p_orig : p_clip_or_mask) {
1131 // All snappoints are in desktop coordinates, but the item's transformation is
1132 // in document coordinates. Hence the awkward construction below
1133 auto pt = p_orig.getPoint();
1134 if (contentunits == SP_CONTENT_UNITS_OBJECTBOUNDINGBOX && gbox()) {
1135 pt *= Geom::Scale(gbox()->dimensions()) * Geom::Translate(gbox()->min());
1136 }
1137 pt = desktop->dt2doc(pt) * i2dt_affine();
1138 p.emplace_back(pt, p_orig.getSourceType(), p_orig.getTargetType());
1139 }
1140 }
1141 }
1142 };
1143
1144 if (auto clip = getClipObject()) {
1145 add_clip_or_mask_points(clip, clip->clippath_units());
1146 }
1147
1148 if (auto mask = getMaskObject()) {
1149 add_clip_or_mask_points(mask, mask->mask_content_units());
1150 }
1151}
1152
1153// CPPIFY: make pure virtual
1155 //throw;
1156}
1157
1159{
1160 if (!isHidden()) {
1161 if (!transform.isIdentity() || style->opacity.value != SP_SCALE24_MAX) {
1162 ctx->bind(transform, SP_SCALE24_TO_FLOAT(style->opacity.value));
1163 print(ctx);
1164 ctx->release();
1165 } else {
1166 print(ctx);
1167 }
1168 }
1169}
1170
1176const char* SPItem::typeName() const {
1177 return "item";
1178}
1179
1185const char* SPItem::displayName() const {
1186 return _("Object");
1187}
1188
1189gchar* SPItem::description() const {
1190 return g_strdup("");
1191}
1192
1194 gchar* s = g_strdup_printf("<b>%s</b> %s",
1195 this->displayName(), this->description());
1196
1197 if (s && getClipObject()) {
1198 char *snew = g_strdup_printf(_("%s; <i>clipped</i>"), s);
1199 g_free(s);
1200 s = snew;
1201 }
1202
1203 if (s && getMaskObject()) {
1204 char *snew = g_strdup_printf(_("%s; <i>masked</i>"), s);
1205 g_free(s);
1206 s = snew;
1207 }
1208
1209 if (style && style->filter.href && style->filter.href->getObject()) {
1210 char const *label = style->filter.href->getObject()->label();
1211 char *snew = nullptr;
1212
1213 if (label) {
1214 snew = g_strdup_printf(_("%s; <i>filtered (%s)</i>"), s, _(label));
1215 } else {
1216 snew = g_strdup_printf(_("%s; <i>filtered</i>"), s);
1217 }
1218
1219 g_free(s);
1220 s = snew;
1221 }
1222
1223 return s;
1224}
1225
1227 return style && style->filter.href && style->filter.href->getObject();
1228}
1229
1231 SPObject* parent = this->parent;
1232 while (parent && !is<SPMask>(parent)) {
1233 parent = parent->parent;
1234 }
1235 return parent;
1236}
1237
1239 SPObject* parent = this->parent;
1240 while (parent && !is<SPClipPath>(parent)) {
1241 parent = parent->parent;
1242 }
1243 return parent;
1244}
1245
1246unsigned SPItem::display_key_new(unsigned numkeys)
1247{
1248 static unsigned dkey = 1;
1249
1250 dkey += numkeys;
1251
1252 return dkey - numkeys;
1253}
1254
1256{
1257 if (!di->key()) {
1259 }
1260 return di->key();
1261}
1262
1263// CPPIFY: make pure virtual
1264Inkscape::DrawingItem* SPItem::show(Inkscape::Drawing& /*drawing*/, unsigned int /*key*/, unsigned int /*flags*/) {
1265 //throw;
1266 return nullptr;
1267}
1268
1270{
1271 auto ai = show(drawing, key, flags);
1272 if (!ai) {
1273 return nullptr;
1274 }
1275
1276 auto const bbox = geometricBounds();
1277
1278 ai->setItem(this);
1279 ai->setItemBounds(bbox);
1280 ai->setTransform(transform);
1281 ai->setOpacity(SP_SCALE24_TO_FLOAT(style->opacity.value));
1282 ai->setIsolation(style->isolation.value);
1283 ai->setBlendMode(style->mix_blend_mode.value);
1284 ai->setVisible(!isHidden());
1285 ai->setSensitive(sensitive);
1286 views.emplace_back(flags, key, DrawingItemPtr<Inkscape::DrawingItem>(ai));
1287
1288 if (auto clip = getClipObject()) {
1289 auto clip_key = SPItem::ensure_key(ai) + ITEM_KEY_CLIP;
1290 auto ac = clip->show(drawing, clip_key, bbox);
1291 ai->setClip(ac);
1292 }
1293 if (auto mask = getMaskObject()) {
1294 auto mask_key = SPItem::ensure_key(ai) + ITEM_KEY_MASK;
1295 auto ac = mask->show(drawing, mask_key, bbox);
1296 ai->setMask(ac);
1297 }
1298 if (auto fill = style->getFillPaintServer()) {
1299 auto fill_key = SPItem::ensure_key(ai) + ITEM_KEY_FILL;
1300 auto ap = fill->show(drawing, fill_key, bbox);
1301 ai->setFillPattern(ap);
1302 }
1303 if (auto stroke = style->getStrokePaintServer()) {
1304 auto stroke_key = SPItem::ensure_key(ai) + ITEM_KEY_STROKE;
1305 auto ap = stroke->show(drawing, stroke_key, bbox);
1306 ai->setStrokePattern(ap);
1307 }
1308 if (auto filter = style->getFilter()) {
1309 filter->show(ai);
1310 }
1311
1312 return ai;
1313}
1314
1315// CPPIFY: make pure virtual
1316void SPItem::hide(unsigned int /*key*/) {
1317 //throw;
1318}
1319
1321{
1322 hide(key);
1323
1324 for (auto it = views.begin(); it != views.end(); ) {
1325 auto &v = *it;
1326 if (v.key == key) {
1327 unsigned ai_key = v.drawingitem->key();
1328
1329 if (auto clip = getClipObject()) {
1330 clip->hide(ai_key + ITEM_KEY_CLIP);
1331 }
1332 if (auto mask = getMaskObject()) {
1333 mask->hide(ai_key + ITEM_KEY_MASK);
1334 }
1335 if (auto fill_ps = style->getFillPaintServer()) {
1336 fill_ps->hide(ai_key + ITEM_KEY_FILL);
1337 }
1338 if (auto stroke_ps = style->getStrokePaintServer()) {
1339 stroke_ps->hide(ai_key + ITEM_KEY_STROKE);
1340 }
1341 if (auto filter = style->getFilter()) {
1342 filter->hide(v.drawingitem.get());
1343 }
1344
1345 v.drawingitem.reset();
1346
1347 *it = std::move(views.back());
1348 views.pop_back();
1349 } else {
1350 ++it;
1351 }
1352 }
1353}
1354
1358void SPItem::invoke_hide_except(unsigned key, const std::vector<SPItem const *> &to_keep)
1359{
1360 // If item is not in the list of items to keep.
1361 if (to_keep.end() == find(to_keep.begin(), to_keep.end(), this)) {
1362 // Only hide the item if it's not a group, root or use.
1363 if (!is<SPRoot>(this) &&
1364 !is<SPGroup>(this) &&
1365 !is<SPUse>(this)
1366 ) {
1367 this->invoke_hide(key);
1368 }
1369 // recurse
1370 for (auto &obj : this->children) {
1371 if (auto child = cast<SPItem>(&obj)) {
1372 child->invoke_hide_except(key, to_keep);
1373 }
1374 }
1375 }
1376}
1377
1378// Adjusters
1379
1381{
1382 bool fill = (pt == TRANSFORM_FILL || pt == TRANSFORM_BOTH);
1383 if (fill && style && (style->fill.isPaintserver())) {
1384 SPObject *server = style->getFillPaintServer();
1385 auto serverPatt = cast<SPPattern>(server);
1386 if ( serverPatt ) {
1387 SPPattern *pattern = serverPatt->clone_if_necessary(this, "fill");
1388 pattern->transform_multiply(postmul, set);
1389 }
1390 }
1391
1392 bool stroke = (pt == TRANSFORM_STROKE || pt == TRANSFORM_BOTH);
1393 if (stroke && style && (style->stroke.isPaintserver())) {
1394 SPObject *server = style->getStrokePaintServer();
1395 auto serverPatt = cast<SPPattern>(server);
1396 if ( serverPatt ) {
1397 SPPattern *pattern = serverPatt->clone_if_necessary(this, "stroke");
1398 pattern->transform_multiply(postmul, set);
1399 }
1400 }
1401}
1402
1404{
1405 bool fill = (pt == TRANSFORM_FILL || pt == TRANSFORM_BOTH);
1406 if (fill && style && (style->fill.isPaintserver())) {
1407 SPObject *server = style->getFillPaintServer();
1408 auto serverHatch = cast<SPHatch>(server);
1409 if (serverHatch) {
1410 SPHatch *hatch = serverHatch->clone_if_necessary(this, "fill");
1411 hatch->transform_multiply(postmul, set);
1412 }
1413 }
1414
1415 bool stroke = (pt == TRANSFORM_STROKE || pt == TRANSFORM_BOTH);
1416 if (stroke && style && (style->stroke.isPaintserver())) {
1417 SPObject *server = style->getStrokePaintServer();
1418 auto serverHatch = cast<SPHatch>(server);
1419 if (serverHatch) {
1420 SPHatch *hatch = serverHatch->clone_if_necessary(this, "stroke");
1421 hatch->transform_multiply(postmul, set);
1422 }
1423 }
1424}
1425
1426void SPItem::adjust_gradient( Geom::Affine const &postmul, bool set )
1427{
1428 if ( style && style->fill.isPaintserver() ) {
1430 auto serverGrad = cast<SPGradient>(server);
1431 if ( serverGrad ) {
1432
1442 SPGradient *gradient = sp_gradient_convert_to_userspace( serverGrad, this, "fill" );
1443
1444 sp_gradient_transform_multiply( gradient, postmul, set );
1445 }
1446 }
1447
1448 if ( style && style->stroke.isPaintserver() ) {
1450 auto serverGrad = cast<SPGradient>(server);
1451 if ( serverGrad ) {
1452 SPGradient *gradient = sp_gradient_convert_to_userspace( serverGrad, this, "stroke");
1453 sp_gradient_transform_multiply( gradient, postmul, set );
1454 }
1455 }
1456}
1457
1458void SPItem::adjust_clip(Geom::Affine const &postmul, bool set)
1459{
1460 if (auto clip_group = getClipObject()) {
1461 clip_group->transform_multiply(postmul, set);
1462 }
1463}
1464
1466{
1467 if (auto clip_group = getClipObject()) {
1468 clip_group->removeTransformsRecursively(clip_group->parent);
1469 }
1470}
1471
1472void SPItem::adjust_stroke( gdouble ex )
1473{
1474 if (freeze_stroke_width) {
1475 return;
1476 }
1477
1478 SPStyle *style = this->style;
1479
1480 if (style && !Geom::are_near(ex, 1.0, Geom::EPSILON) && !style->stroke_extensions.hairline) {
1481 style->stroke_width.computed *= ex;
1482 style->stroke_width.set = TRUE;
1483
1484 if ( !style->stroke_dasharray.values.empty() ) {
1485 for (auto & value : style->stroke_dasharray.values) {
1486 value.value *= ex;
1487 value.computed *= ex;
1488 }
1489 style->stroke_dashoffset.value *= ex;
1490 style->stroke_dashoffset.computed *= ex;
1491 }
1492
1493 updateRepr();
1494 }
1495}
1496
1501{
1503 gchar const *t_attr = item->getRepr()->attribute("transform");
1504 if (t_attr) {
1505 Geom::Affine t;
1506 if (sp_svg_transform_read(t_attr, &t)) {
1507 t_old = t;
1508 }
1509 }
1510
1511 return t_old;
1512}
1513
1514
1516{
1517 adjust_stroke (expansion);
1518
1519// A clone's child is the ghost of its original - we must not touch it, skip recursion
1520 if (!is<SPUse>(this)) {
1521 for (auto& o: children) {
1522 auto item = cast<SPItem>(&o);
1523 if (item) {
1525 }
1526 }
1527 }
1528}
1529
1531{
1532 freeze_stroke_width = freeze;
1533
1534// A clone's child is the ghost of its original - we must not touch it, skip recursion
1535 if (!is<SPUse>(this)) {
1536 for (auto& o: children) {
1537 auto item = cast<SPItem>(&o);
1538 if (item) {
1540 }
1541 }
1542 }
1543}
1544
1548static void
1550{
1551 auto rect = cast<SPRect>(item);
1552 if (rect) {
1553 rect->compensateRxRy(advertized_transform);
1554 }
1555
1556 for(auto& o: item->children) {
1557 auto itm = cast<SPItem>(&o);
1558 if (itm) {
1559 sp_item_adjust_rects_recursive(itm, advertized_transform);
1560 }
1561 }
1562}
1563
1565{
1566// _Before_ full pattern/gradient transform: t_paint * t_item * t_ancestors
1567// _After_ full pattern/gradient transform: t_paint_new * t_item * t_ancestors * advertised_transform
1568// By equating these two expressions we get t_paint_new = t_paint * paint_delta, where:
1569 Geom::Affine t_item = sp_item_transform_repr (this);
1570 Geom::Affine paint_delta = t_item * t_ancestors * advertized_transform * t_ancestors.inverse() * t_item.inverse();
1571
1572// Within text, we do not fork gradients, and so must not recurse to avoid double compensation;
1573// also we do not recurse into clones, because a clone's child is the ghost of its original -
1574// we must not touch it
1575 if (!(cast<SPText>(this) || cast<SPUse>(this))) {
1576 for (auto& o: children) {
1577 auto item = cast<SPItem>(&o);
1578 if (item) {
1579 // At the level of the transformed item, t_ancestors is identity;
1580 // below it, it is the accumulated chain of transforms from this level to the top level
1581 item->adjust_paint_recursive(advertized_transform, t_item * t_ancestors, type);
1582 }
1583 }
1584 }
1585
1586// We recursed into children first, and are now adjusting this object second;
1587// this is so that adjustments in a tree are done from leaves up to the root,
1588// and paintservers on leaves inheriting their values from ancestors could adjust themselves properly
1589// before ancestors themselves are adjusted, probably differently (bug 1286535)
1590
1591 switch (type) {
1592 case PATTERN: {
1593 adjust_pattern(paint_delta);
1594 break;
1595 }
1596 case HATCH: {
1597 adjust_hatch(paint_delta);
1598 break;
1599 }
1600 default: {
1601 adjust_gradient(paint_delta);
1602 }
1603 }
1604}
1605
1607{
1608 auto our_shape = documentExactBounds();
1609 return our_shape ? pathvs_have_nonempty_overlap(*our_shape, shape) : false;
1610}
1611
1612bool SPItem::collidesWith(SPItem const &other) const
1613{
1614 auto other_shape = other.documentExactBounds();
1615 return other_shape ? collidesWith(*other_shape) : false;
1616}
1617
1618// CPPIFY:: make pure virtual?
1619// Not all SPItems must necessarily have a set transform method!
1621// throw;
1622 return transform;
1623}
1624
1626 // The default action is to remove the transform from it's parents
1627 // but retain it in the given shape. Other item types may remove the
1628 // transform entirely.
1629 setAttribute("transform", sp_svg_transform_write(i2i_affine(this, root)));
1630}
1631
1635static bool is_satellite_item(SPItem const &item)
1636{
1637 for (SPObject const *ref : item.hrefList) {
1638 if (is<LivePathEffectObject>(ref)) {
1639 return true;
1640 }
1641 }
1642 return false;
1643}
1644
1646 if (auto path_effect = getAttribute("inkscape:path-effect")) {
1647 assert(path_effect[0]);
1648 return true;
1649 }
1650
1651 if (is_satellite_item(*this)) {
1652 return true;
1653 }
1654
1655 return false;
1656}
1657
1658void SPItem::doWriteTransform(Geom::Affine const &transform, Geom::Affine const *adv, bool compensate)
1659{
1660 // calculate the relative transform, if not given by the adv attribute
1661 Geom::Affine advertized_transform;
1662 if (adv != nullptr) {
1663 advertized_transform = *adv;
1664 } else {
1665 advertized_transform = sp_item_transform_repr (this).inverse() * transform;
1666 }
1667
1669 if (compensate) {
1670 // recursively compensating for stroke scaling will not always work, because it can be scaled to zero or infinite
1671 // from which we cannot ever recover by applying an inverse scale; therefore we temporarily block any changes
1672 // to the strokewidth in such a case instead, and unblock these after the transformation
1673 // (as reported in https://bugs.launchpad.net/inkscape/+bug/825840/comments/4)
1674 if (!prefs->getBool("/options/transform/stroke", true)) {
1675 double const expansion = 1. / advertized_transform.descrim();
1676 if (expansion < 1e-9 || expansion > 1e9) {
1678 // This will only work if the item has a set_transform method (in this method adjust_stroke() will be called)
1679 // We will still have to apply the inverse scaling to other items, not having a set_transform method
1680 // such as ellipses and stars
1681 // PS: We cannot use this freeze_stroke_width_recursive() trick in all circumstances. For example, it will
1682 // break pasting objects within their group (because in such a case the transformation of the group will affect
1683 // the strokewidth, and has to be compensated for. See https://bugs.launchpad.net/inkscape/+bug/959223/comments/10)
1684 } else {
1686 }
1687 }
1688
1689 // recursively compensate rx/ry of a rect if requested
1690 if (!prefs->getBool("/options/transform/rectcorners", true)) {
1691 sp_item_adjust_rects_recursive(this, advertized_transform);
1692 }
1693
1694 // recursively compensate pattern fill if it's not to be transformed
1695 if (!prefs->getBool("/options/transform/pattern", true)) {
1696 adjust_paint_recursive(advertized_transform.inverse(), Geom::identity(), PATTERN);
1697 }
1698 if (!prefs->getBool("/options/transform/hatch", true)) {
1699 adjust_paint_recursive(advertized_transform.inverse(), Geom::identity(), HATCH);
1700 }
1701
1704 if (!prefs->getBool("/options/transform/gradient", true)) {
1705 adjust_paint_recursive(advertized_transform.inverse(), Geom::identity(), GRADIENT);
1706 } else {
1707 // this converts the gradient/pattern fill/stroke, if any, to userSpaceOnUse; we need to do
1708 // it here _before_ the new transform is set, so as to use the pre-transform bbox
1710 }
1711
1712 } // endif(compensate)
1713
1714 gint preserve = prefs->getBool("/options/preservetransform/value", false);
1715 Geom::Affine transform_attr (transform);
1716
1717 // CPPIFY: check this code.
1718 // If onSetTransform is not overridden, CItem::onSetTransform will return the transform it was given as a parameter.
1719 // onSetTransform cannot be pure due to the fact that not all visible Items are transformable.
1720 auto lpeitem = cast<SPLPEItem>(this);
1721 if (lpeitem) {
1722 lpeitem->notifyTransform(transform);
1723 }
1724 bool unoptimiced = unoptimized();
1725 if ( // run the object's set_transform (i.e. embed transform) only if:
1726 (cast<SPText>(this) && firstChild() && cast<SPTextPath>(firstChild())) ||
1727 (!preserve && // user did not chose to preserve all transforms
1728 !getClipObject() && // the object does not have a clippath
1729 !getMaskObject() && // the object does not have a mask
1730 !(!transform.isTranslation() && style && style->getFilter()) &&
1731 !unoptimiced)
1732 // the object does not have a filter, or the transform is translation (which is supposed to not affect filters)
1733 )
1734 {
1735 transform_attr = this->set_transform(transform);
1736 }
1737 if (freeze_stroke_width) {
1739 if (compensate) {
1740 if (!prefs->getBool("/options/transform/stroke", true)) {
1741 // Recursively compensate for stroke scaling, depending on user preference
1742 // (As to why we need to do this, see the comment a few lines above near the freeze_stroke_width_recursive(true) call)
1743 double const expansion = 1. / advertized_transform.descrim();
1745 }
1746 }
1747 }
1748 // this avoid temporary scaling issues on display when near identity
1749 // this must be a bit grater than EPSILON * transform.descrim()
1750 double e = 1e-5 * transform.descrim();
1751 if (transform_attr.isIdentity(e)) {
1752 transform_attr = Geom::Affine();
1753 }
1754 set_item_transform(transform_attr);
1755
1756 // Note: updateRepr comes before emitting the transformed signal since
1757 // it causes clone SPUse's copy of the original object to be brought up to
1758 // date with the original. Otherwise, sp_use_bbox returns incorrect
1759 // values if called in code handling the transformed signal.
1760 updateRepr();
1761
1762 if (lpeitem) {
1763 if (!lpeitem->hasPathEffectOfType(Inkscape::LivePathEffect::SLICE)) {
1764 sp_lpe_item_update_patheffect(lpeitem, false, true);
1765 }
1766 }
1767
1768 // send the relative transform with a _transformed_signal
1769 _transformed_signal.emit(&advertized_transform, this);
1770}
1771
1772void SPItem::set_item_transform(Geom::Affine const &transform_matrix)
1773{
1774 if (!Geom::are_near(transform_matrix, transform, 1e-18)) {
1775 transform = transform_matrix;
1776 /* The SP_OBJECT_USER_MODIFIED_FLAG_B is used to mark the fact that it's only a
1777 transformation. It's apparently not used anywhere else. */
1778 requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_USER_MODIFIED_FLAG_B);
1779 }
1780}
1781
1782//void SPItem::convert_to_guides() const {
1783// // CPPIFY: If not overridden, call SPItem::convert_to_guides() const, see below!
1784// this->convert_to_guides();
1785//}
1786
1787Geom::Affine i2anc_affine(SPObject const *object, SPObject const *ancestor)
1788{
1789 Geom::Affine ret;
1790
1791 // Stop at first non-renderable ancestor.
1792 while (object != ancestor && is<SPItem>(object)) {
1793 if (auto root = cast<SPRoot>(object)) {
1794 ret *= root->c2p;
1795 } else {
1796 auto item = cast_unsafe<SPItem>(object);
1797 ret *= item->transform;
1798 }
1799 object = object->parent;
1800 }
1801
1802 return ret;
1803}
1804
1806i2i_affine(SPObject const *src, SPObject const *dest) {
1807 g_return_val_if_fail(src != nullptr && dest != nullptr, Geom::identity());
1808 SPObject const *ancestor = src->nearestCommonAncestor(dest);
1809 return i2anc_affine(src, ancestor) * i2anc_affine(dest, ancestor).inverse();
1810}
1811
1813 return i2i_affine(this, dest);
1814}
1815
1817{
1818 return i2anc_affine(this, nullptr);
1819}
1820
1822{
1823 return i2doc_affine() * document->doc2dt();
1824}
1825
1826// TODO should be named "set_i2dt_affine"
1828{
1829 Geom::Affine dt2p; /* desktop to item parent transform */
1830 if (parent) {
1831 dt2p = static_cast<SPItem *>(parent)->i2dt_affine().inverse();
1832 } else {
1833 dt2p = document->dt2doc();
1834 }
1835
1836 Geom::Affine const i2p( i2dt * dt2p );
1837 set_item_transform(i2p);
1838}
1839
1840
1842{
1843 /* fixme: Implement the right way (Lauris) */
1844 return i2dt_affine().inverse();
1845}
1846
1847// Item views
1849{
1850 auto const view_it = std::find_if(views.begin(), views.end(), [key](SPItemView const &v) {
1851 return v.key == key;
1852 });
1853 return view_it == views.end() ? nullptr : view_it->drawingitem.get();
1854}
1855
1856int sp_item_repr_compare_position(SPItem const *first, SPItem const *second)
1857{
1858 return sp_repr_compare_position(first->getRepr(),
1859 second->getRepr());
1860}
1861
1863{
1864 return sp_item_first_item_child( const_cast<SPObject *>(obj) );
1865}
1866
1868{
1869 SPItem *child = nullptr;
1870 for (auto& iter: obj->children) {
1871 auto tmp = cast<SPItem>(&iter);
1872 if ( tmp ) {
1873 child = tmp;
1874 break;
1875 }
1876 }
1877 return child;
1878}
1879
1882 int prefs_bbox = prefs->getInt("/tools/bounding_box", 0);
1883
1885 if (!bbox) {
1886 g_warning ("Cannot determine item's bounding box during conversion to guides.\n");
1887 return;
1888 }
1889
1890 std::list<std::pair<Geom::Point, Geom::Point> > pts;
1891
1892 Geom::Point A((*bbox).min());
1893 Geom::Point C((*bbox).max());
1894 Geom::Point B(A[Geom::X], C[Geom::Y]);
1895 Geom::Point D(C[Geom::X], A[Geom::Y]);
1896
1897 pts.emplace_back(A, B);
1898 pts.emplace_back(B, C);
1899 pts.emplace_back(C, D);
1900 pts.emplace_back(D, A);
1901
1903}
1904
1906{
1907 Geom::Point center = getCenter();
1908 Geom::Translate const s(getCenter());
1909 Geom::Affine affine = Geom::Affine(s).inverse() * Geom::Affine(rotation) * Geom::Affine(s);
1910
1911 // Rotate item.
1913 // Use each item's own transform writer, consistent with sp_selection_apply_affine()
1915
1916 // Restore the center position (it's changed because the bbox center changed)
1917 if (isCenterSet()) {
1918 setCenter(center * affine);
1919 updateRepr();
1920 }
1921}
1922
1924{
1926 if (bbox) {
1927 Geom::Translate const s(bbox->midpoint()); // use getCenter?
1928 set_i2d_affine(i2dt_affine() * s.inverse() * scale * s);
1930 }
1931}
1932
1933void SPItem::skew_rel(double skewX, double skewY)
1934{
1935 Geom::Point center = getCenter();
1936 Geom::Translate const s(getCenter());
1937
1938 Geom::Affine const skew(1, skewY, skewX, 1, 0, 0);
1939 Geom::Affine affine = Geom::Affine(s).inverse() * skew * Geom::Affine(s);
1940
1941 set_i2d_affine(i2dt_affine() * affine);
1943
1944 // Restore the center position (it's changed because the bbox center changed)
1945 if (isCenterSet()) {
1946 setCenter(center * affine);
1947 updateRepr();
1948 }
1949}
1950
1952{
1954
1956}
1957
1958/*
1959 Local Variables:
1960 mode:c++
1961 c-file-style:"stroustrup"
1962 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1963 indent-tabs-mode:nil
1964 fill-column:99
1965 End:
1966*/
1967// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
double scale
Definition aa.cpp:228
gchar const * sp_attribute_name(SPAttr id)
Get attribute name by id.
bool SP_ATTRIBUTE_IS_CSS(SPAttr k)
True iff k is a property in SVG, i.e.
Lookup dictionary for attributes/properties.
SPAttr
Definition attributes.h:27
@ REQUIRED_FEATURES
@ INKSCAPE_HIGHLIGHT_COLOR
@ SYSTEM_LANGUAGE
@ TRANSFORM_CENTER_X
@ CONNECTION_POINTS
@ SODIPODI_INSENSITIVE
@ CONNECTOR_AVOID
@ REQUIRED_EXTENSIONS
@ TRANSFORM_CENTER_Y
3x3 matrix representing an affine transformation.
Definition affine.h:70
Coord descrim() const
Calculate the descriminant.
Definition affine.cpp:434
bool isIdentity(Coord eps=EPSILON) const
Check whether this matrix is an identity matrix.
Definition affine.cpp:109
bool isTranslation(Coord eps=EPSILON) const
Check whether this matrix represents a pure translation.
Definition affine.cpp:123
Affine inverse() const
Compute the inverse matrix.
Definition affine.cpp:388
void intersectWith(CRect const &b)
Leave only the area overlapping with the argument.
static CRect from_xywh(Coord x, Coord y, Coord w, Coord h)
Create rectangle from origin and dimensions.
C height() const
Get the vertical extent of the rectangle.
C width() const
Get the horizontal extent of the rectangle.
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Sequence of subpaths.
Definition pathvector.h:122
Sequence of contiguous curves, aka spline.
Definition path.h:353
Two-dimensional point that doubles as a vector.
Definition point.h:66
Axis aligned, non-empty rectangle.
Definition rect.h:92
bool hasZeroArea(Coord eps=EPSILON) const
Check whether the rectangle has zero area up to specified tolerance.
Definition rect.h:113
Rotation around the origin.
Definition transforms.h:187
Scaling from the origin.
Definition transforms.h:150
Translation by a vector.
Definition transforms.h:115
Translate inverse() const
Get the inverse translation.
Definition transforms.h:133
SVG drawing item for display.
unsigned key() const
void setKey(unsigned key)
Preference storage class.
Definition preferences.h:66
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
static Preferences * get()
Access the singleton Preferences object.
int getInt(Glib::ustring const &pref_path, int def=0)
Retrieve an integer.
Storing of snapping preferences.
bool isTargetSnappable(Inkscape::SnapTargetType const target) const
URI const * getURI() const
Returns a pointer to a URI containing the currently attached URI, or NULL if no URI is currently atta...
bool try_attach(char const *uri)
Try to attach to a URI.
sigc::signal< void(SPObject *, SPObject *)> changedSignal()
Accessor for the referrent change notification signal; this signal is emitted whenever the URIReferen...
std::string cssStr(char const *baseuri=nullptr) const
Return a CSS formatted url value.
Definition uri.h:176
double value(Unit const *u) const
Return the quantity's value in the specified unit.
Definition units.cpp:502
Interface for refcounted XML nodes.
Definition node.h:80
virtual Node * parent()=0
Get the parent of this node.
virtual void addChild(Node *child, Node *after)=0
Insert another node as a child of this node.
virtual void changeOrder(Node *child, Node *after)=0
Move a given node in this node's child order.
void setAttributeOrRemoveIfEmpty(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:167
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
void removeAttribute(Inkscape::Util::const_char_ptr key)
Remove an attribute of this node.
Definition node.h:280
virtual void removeChild(Node *child)=0
Remove a child of this node.
bool setAttributeSvgDouble(Util::const_char_ptr key, double val)
For attributes where an exponent is allowed.
Definition node.cpp:111
virtual Node * root()=0
Get the root node of this node's document.
void handleSettingChange()
void setAvoid(char const *value)
sigc::connection modified_connection
Definition sp-clippath.h:88
SPClipPath * getObject() const
Definition sp-clippath.h:83
Geom::PathVector getPathVector(Geom::Affine const &transform) const
This gets a compiled path vector from all the objects.
Geom::Affine const & dt2doc() const
Definition desktop.cpp:1343
Typed SVG document implementation.
Definition document.h:103
const Geom::Affine & doc2dt() const
Document to desktop coordinate transformation.
Definition document.cpp:935
void _emitModified(unsigned int object_modified_tag=0)
SPRoot * getRoot()
Returns our SPRoot.
Definition document.h:202
const Geom::Affine & dt2doc() const
Desktop to document coordinate transformation.
Definition document.h:270
Inkscape::Util::Quantity getWidth() const
Definition document.cpp:857
double yaxisdir() const
"1" if the desktop Y-axis points down, "-1" if it points up.
Definition document.h:278
int ensureUpToDate(unsigned int object_modified_tag=0)
Repeatedly works on getting the document updated, since sometimes it takes more than one pass to get ...
SPObject * getObjectByRepr(Inkscape::XML::Node *repr) const
Inkscape::Util::Quantity getHeight() const
Definition document.cpp:896
void show(Inkscape::DrawingItem *item)
Gradient.
Definition sp-gradient.h:86
void transform_multiply(Geom::Affine postmul, bool set)
Definition sp-hatch.cpp:524
SPHatch * clone_if_necessary(SPItem *item, const gchar *property)
Definition sp-hatch.cpp:495
Base class for visual SVG elements.
Definition sp-item.h:109
void scale_rel(Geom::Scale const &scale)
Definition sp-item.cpp:1923
Geom::OptRect desktopPreferredBounds() const
Definition sp-item.cpp:1058
void modified(unsigned int flags) override
Definition sp-item.cpp:843
double transform_center_y
Definition sp-item.h:131
SPGroup * getParentGroup() const
Return the parent, only if it's a group object.
Definition sp-item.cpp:473
virtual Geom::Affine set_transform(Geom::Affine const &transform)
Definition sp-item.cpp:1620
bool raiseOne()
Definition sp-item.cpp:429
bool isEvaluated() const
Definition sp-item.cpp:310
SPClipPathReference & getClipRef()
Definition sp-item.cpp:192
void clip_ref_changed(SPObject *old_clip, SPObject *clip)
Definition sp-item.cpp:669
void set_i2d_affine(Geom::Affine const &transform)
Definition sp-item.cpp:1827
void stroke_ps_ref_changed(SPObject *old_ps, SPObject *ps)
Definition sp-item.cpp:743
void set_item_transform(Geom::Affine const &transform_matrix)
Sets item private transform (not propagated to repr), without compensating stroke widths,...
Definition sp-item.cpp:1772
EvaluatedStatus _evaluated_status
Definition sp-item.h:423
Geom::Affine i2dt_affine() const
Returns the transformation from item to desktop coords.
Definition sp-item.cpp:1821
void update(SPCtx *ctx, unsigned int flags) override
Definition sp-item.cpp:780
SPAvoidRef * avoidRef
Definition sp-item.h:160
Geom::OptRect bounds(BBoxType type, Geom::Affine const &transform=Geom::identity()) const
Definition sp-item.cpp:995
virtual void snappoints(std::vector< Inkscape::SnapCandidatePoint > &p, Inkscape::SnapPreferences const *snapprefs) const
Definition sp-item.cpp:1096
virtual void print(SPPrintContext *ctx)
Definition sp-item.cpp:1154
virtual std::optional< Geom::PathVector > documentExactBounds() const
Get an exact geometric shape representing the visual bounds of the item in the document coordinates.
Definition sp-item.cpp:1035
Geom::OptRect desktopBounds(BBoxType type) const
Definition sp-item.cpp:1067
Geom::OptRect documentVisualBounds() const
Get item's visual bbox in document coordinate system.
Definition sp-item.cpp:1018
void setHighlight(Inkscape::Colors::Color color)
Definition sp-item.cpp:268
SPMaskReference & getMaskRef()
Definition sp-item.cpp:182
virtual char * description() const
Definition sp-item.cpp:1189
Geom::OptRect documentPreferredBounds() const
Definition sp-item.cpp:1004
void freeze_stroke_width_recursive(bool freeze)
Definition sp-item.cpp:1530
void lowerToBottom()
Definition sp-item.cpp:458
void raiseToTop()
Definition sp-item.cpp:419
sigc::signal< void(Geom::Affine const *, SPItem *)> _transformed_signal
Definition sp-item.h:165
Geom::OptRect documentBounds(BBoxType type) const
Definition sp-item.cpp:1026
Geom::OptRect documentGeometricBounds() const
Get item's geometric bbox in document coordinate system.
Definition sp-item.cpp:1013
virtual Inkscape::Colors::Color highlight_color() const
Definition sp-item.cpp:277
Inkscape::XML::Node * write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, unsigned int flags) override
Definition sp-item.cpp:851
SPClipPathReference * clip_ref
Definition sp-item.h:156
Inkscape::DrawingItem * invoke_show(Inkscape::Drawing &drawing, unsigned int key, unsigned int flags)
Definition sp-item.cpp:1269
void set(SPAttr key, char const *value) override
Definition sp-item.cpp:559
Geom::Point getCenter(bool ensure_uptodate=true) const
Definition sp-item.cpp:377
SPMask * getMaskObject() const
Definition sp-item.cpp:177
Inkscape::DrawingItem * get_arenaitem(unsigned key) const
Return the arenaitem corresponding to the given item in the display with the given key.
Definition sp-item.cpp:1848
bool isHidden() const
Definition sp-item.cpp:235
bool lowerOne()
Definition sp-item.cpp:439
bool isVisibleAndUnlocked() const
Definition sp-item.cpp:210
void invoke_print(SPPrintContext *ctx)
Definition sp-item.cpp:1158
void release() override
Definition sp-item.cpp:535
void adjust_gradient(Geom::Affine const &postmul, bool set=false)
Definition sp-item.cpp:1426
void filter_ref_changed(SPObject *old_obj, SPObject *obj)
Definition sp-item.cpp:763
virtual void hide(unsigned int key)
Definition sp-item.cpp:1316
void moveTo(SPItem *target, bool intoafter)
Move this SPItem into or after another SPItem in the doc.
Definition sp-item.cpp:478
void adjust_stroke_width_recursive(double ex)
Recursively scale stroke width in item and its children by expansion.
Definition sp-item.cpp:1515
void skew_rel(double skewX, double skewY)
Definition sp-item.cpp:1933
Geom::Affine transform
Definition sp-item.h:138
void adjust_paint_recursive(Geom::Affine advertized_transform, Geom::Affine t_ancestors, PaintServerType type=GRADIENT)
Recursively compensate pattern or gradient transform.
Definition sp-item.cpp:1564
Geom::Rect viewport
Definition sp-item.h:140
void invoke_hide(unsigned int key)
Definition sp-item.cpp:1320
bool isLocked() const
Definition sp-item.cpp:218
bool unoptimized()
Definition sp-item.cpp:1645
void setEvaluated(bool visible)
Definition sp-item.cpp:290
void setHidden(bool hidden)
Definition sp-item.cpp:241
void unsetCenter()
Definition sp-item.cpp:367
SPObject * isInClipPath() const
Definition sp-item.cpp:1238
std::optional< Inkscape::Colors::Color > _highlightColor
Definition sp-item.h:409
void fill_ps_ref_changed(SPObject *old_ps, SPObject *ps)
Definition sp-item.cpp:723
void getSnappoints(std::vector< Inkscape::SnapCandidatePoint > &p, Inkscape::SnapPreferences const *snapprefs=nullptr) const
Definition sp-item.cpp:1105
void rotate_rel(Geom::Rotate const &rotation)
Definition sp-item.cpp:1905
bool collidesWith(Geom::PathVector const &shape) const
Checks for visual collision with another item.
Definition sp-item.cpp:1606
void move_rel(Geom::Translate const &tr)
Definition sp-item.cpp:1951
virtual Geom::OptRect bbox(Geom::Affine const &transform, SPItem::BBoxType type) const
Definition sp-item.cpp:915
SPAvoidRef & getAvoidRef()
Definition sp-item.cpp:202
unsigned bbox_valid
Definition sp-item.h:129
SPText const * getClipTextObject() const
Return the text object, IF and only if this item is clipped by a single SPText object.
Definition sp-item.cpp:126
void resetEvaluated()
Definition sp-item.cpp:295
Geom::OptRect desktopVisualBounds() const
Get item's visual bbox in desktop coordinate system.
Definition sp-item.cpp:1049
void adjust_hatch(Geom::Affine const &postmul, bool set=false, PaintServerTransform=TRANSFORM_BOTH)
Definition sp-item.cpp:1403
bool updateCenterIfSet(Geom::Point const &center)
Definition sp-item.cpp:357
Geom::Affine getRelativeTransform(SPObject const *obj) const
Definition sp-item.cpp:1812
void scaleCenter(Geom::Scale const &sc)
Definition sp-item.cpp:406
Geom::OptRect desktopGeometricBounds() const
Get item's geometric bbox in desktop coordinate system.
Definition sp-item.cpp:1044
void adjust_pattern(Geom::Affine const &postmul, bool set=false, PaintServerTransform=TRANSFORM_BOTH)
Definition sp-item.cpp:1380
virtual void removeTransformsRecursively(SPObject const *root)
Definition sp-item.cpp:1625
void adjust_stroke(double ex)
Definition sp-item.cpp:1472
virtual const char * typeName() const
The item's type name, not node tag name.
Definition sp-item.cpp:1176
bool isExplicitlyHidden() const
Returns something suitable for the ‘Hide’ checkbox in the Object Properties dialog box.
Definition sp-item.cpp:318
@ VISUAL_BBOX
Definition sp-item.h:118
@ GEOMETRIC_BBOX
Definition sp-item.h:116
unsigned int sensitive
Definition sp-item.h:127
Geom::OptRect geometricBounds(Geom::Affine const &transform=Geom::identity()) const
Get item's geometric bounding box in this item's coordinate system.
Definition sp-item.cpp:920
static unsigned ensure_key(Inkscape::DrawingItem *di)
Ensures that a drawing item's key is the first of a block of ITEM_KEY_SIZE keys, assigning it such a ...
Definition sp-item.cpp:1255
Geom::Affine i2doc_affine() const
Returns the accumulated transformation of the item and all its ancestors, including root's viewport.
Definition sp-item.cpp:1816
SPItem()
Definition sp-item.cpp:75
SPMaskReference * mask_ref
Definition sp-item.h:157
~SPItem() override
bool isFiltered() const
Returns true if the item is filtered, false otherwise.
Definition sp-item.cpp:1226
std::optional< Geom::PathVector > getClipPathVector() const
Return the path vector of the clipping region.
Definition sp-item.cpp:112
virtual const char * displayName() const
The item's type name as a translated human string.
Definition sp-item.cpp:1185
char * detailedDescription() const
Returns a string suitable for status bar, formatted in pango markup language.
Definition sp-item.cpp:1193
bool isCenterSet() const
Definition sp-item.cpp:372
static unsigned int display_key_new(unsigned numkeys)
Allocates unique integer keys.
Definition sp-item.cpp:1246
PaintServerType
Definition sp-item.h:121
@ GRADIENT
Definition sp-item.h:121
@ HATCH
Definition sp-item.h:121
@ PATTERN
Definition sp-item.h:121
bool freeze_stroke_width
Definition sp-item.h:132
void build(SPDocument *document, Inkscape::XML::Node *repr) override
Definition sp-item.cpp:512
Geom::OptRect visualBounds(Geom::Affine const &transform=Geom::identity(), bool wfilter=true, bool wclip=true, bool wmask=true) const
Get item's visual bounding box in this item's coordinate system.
Definition sp-item.cpp:925
virtual Inkscape::DrawingItem * show(Inkscape::Drawing &drawing, unsigned int key, unsigned int flags)
Definition sp-item.cpp:1264
SPObject * isInMask() const
Definition sp-item.cpp:1230
virtual void convert_to_guides() const
Definition sp-item.cpp:1880
unsigned int pos_in_parent() const
Definition sp-item.cpp:1076
Geom::OptRect doc_bbox
Definition sp-item.h:139
void doWriteTransform(Geom::Affine const &transform, Geom::Affine const *adv=nullptr, bool compensate=true)
Set a new transform on an object.
Definition sp-item.cpp:1658
void remove_clip_transforms()
Definition sp-item.cpp:1465
void setLocked(bool lock)
Definition sp-item.cpp:228
@ StatusCalculated
Definition sp-item.h:418
@ StatusSet
Definition sp-item.h:419
@ StatusUnknown
Definition sp-item.h:417
void adjust_clip(Geom::Affine const &postmul, bool set=false)
Definition sp-item.cpp:1458
std::vector< SPItemView > views
Definition sp-item.h:163
void setExplicitlyHidden(bool val)
Sets the display CSS property to ‘hidden’ if val is true, otherwise makes it unset.
Definition sp-item.cpp:324
void mask_ref_changed(SPObject *old_mask, SPObject *mask)
Definition sp-item.cpp:696
void setCenter(Geom::Point const &object_centre)
Sets the transform_center_x and transform_center_y properties to retain the rotation center.
Definition sp-item.cpp:331
Geom::Affine dt2i_affine() const
Returns the transformation from desktop to item coords.
Definition sp-item.cpp:1841
bool _is_evaluated
Definition sp-item.h:422
SPClipPath * getClipObject() const
Definition sp-item.cpp:102
double transform_center_x
Definition sp-item.h:130
void invoke_hide_except(unsigned key, const std::vector< SPItem const * > &to_keep)
Invoke hide on all non-group items, except for the list of items to keep.
Definition sp-item.cpp:1358
bool isHighlightSet() const
Definition sp-item.cpp:273
sigc::connection modified_connection
Definition sp-mask.h:88
SPMask * getObject() const
Definition sp-mask.h:83
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
char const * label() const
Gets the author-visible label property for the object or a default if no label is defined.
SPObject * getNext()
Inkscape::XML::Node * repr
Definition sp-object.h:193
void setAttribute(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
SPDocument * document
Definition sp-object.h:188
virtual void set(SPAttr key, const char *value)
SPObject * firstChild()
Definition sp-object.h:315
SPStyle * style
Represents the style properties, whether from presentation attributes, the style attribute,...
Definition sp-object.h:248
virtual Inkscape::XML::Node * write(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags)
SPObject * parent
Definition sp-object.h:189
virtual void release()
Inkscape::XML::Node * updateRepr(unsigned int flags=SP_OBJECT_WRITE_EXT)
Updates the object's repr based on the object's state.
void readAttr(char const *key)
Read value of key attribute from XML node into object.
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
char const * getAttribute(char const *name) const
void objectTrace(std::string const &, bool in=true, unsigned flags=0)
std::list< SPObject * > hrefList
Definition sp-object.h:197
virtual void build(SPDocument *doc, Inkscape::XML::Node *repr)
SPObject const * nearestCommonAncestor(SPObject const *object) const
Returns youngest object being parent to this and object.
ChildrenList children
Definition sp-object.h:907
void requestDisplayUpdate(unsigned int flags)
Queues an deferred update of this object's display.
void transform_multiply(Geom::Affine postmul, bool set)
SPPattern * clone_if_necessary(SPItem *item, char const *property)
An SVG style object.
Definition style.h:45
sigc::signal< void(SPObject *, SPObject *)> signal_fill_ps_changed
Emitted when the fill paint server changes, meaning it starts pointing to a different object.
Definition style.h:322
SPPaintServer * getFillPaintServer()
Definition style.h:339
T< SPAttr::FILL, SPIPaint > fill
fill
Definition style.h:240
T< SPAttr::STROKE_DASHARRAY, SPIDashArray > stroke_dasharray
stroke-dasharray
Definition style.h:257
T< SPAttr::STROKE, SPIPaint > stroke
stroke
Definition style.h:247
SPFilter * getFilter()
Definition style.h:335
SPPaintServer * getStrokePaintServer()
Definition style.h:343
void readFromObject(SPObject *object)
Read style properties from object's repr.
Definition style.cpp:608
T< SPAttr::STROKE_WIDTH, SPILength > stroke_width
stroke-width
Definition style.h:249
sigc::signal< void(SPObject *, SPObject *)> signal_stroke_ps_changed
Emitted when the stroke paint server changes, meaning it starts pointing to a different object.
Definition style.h:328
T< SPAttr::DISPLAY, SPIEnum< SPCSSDisplay > > display
display
Definition style.h:207
T< SPAttr::ISOLATION, SPIEnum< SPIsolation > > isolation
mix-blend-mode: CSS Compositing and Blending Level 1
Definition style.h:219
T< SPAttr::MIX_BLEND_MODE, SPIEnum< SPBlendMode > > mix_blend_mode
Definition style.h:220
T< SPAttr::OPACITY, SPIScale24 > opacity
opacity
Definition style.h:216
T< SPAttr::FILTER, SPIFilter > filter
Filter effect.
Definition style.h:275
T< SPAttr::STROKE_DASHOFFSET, SPILength > stroke_dashoffset
stroke-dashoffset
Definition style.h:259
T< SPAttr::STROKE_EXTENSIONS, SPIStrokeExtensions > stroke_extensions
-inkscape-stroke
Definition style.h:263
sigc::signal< void(SPObject *, SPObject *)> signal_filter_changed
Emitted when the filter changes, meaning it starts pointing to a different object.
Definition style.h:333
T< SPAttr::SHAPE_RENDERING, SPIEnum< SPShapeRendering > > shape_rendering
Definition style.h:293
Geom::Rect viewBox
Definition viewbox.h:35
SVG length type.
Definition svg-length.h:22
void set(Unit u, float v)
Unit unit
Definition svg-length.h:44
float computed
Definition svg-length.h:50
void update(double em, double ex, double scale)
bool sp_item_evaluate(SPItem const *item)
TODO: insert short description here.
const double w
Definition conic-4.cpp:19
A class for handling shape interaction with libavoid.
RootCluster root
Css & result
Editable view implementation.
std::unique_ptr< T, UnlinkDeleter > DrawingItemPtr
Smart pointer used by the Object Tree to hold items in the Display Tree, like std::unique_ptr.
Canvas item belonging to an SVG drawing element.
@ SP_CONTENT_UNITS_OBJECTBOUNDINGBOX
Definition enums.h:64
std::string extract_uri(char const *s, char const **endptr)
Parse functional URI notation, as per 4.3.4 of CSS 2.1.
TODO: insert short description here.
SPFilter * new_filter(SPDocument *document)
void sp_gradient_transform_multiply(SPGradient *gradient, Geom::Affine postmul, bool set)
SPGradient * sp_gradient_convert_to_userspace(SPGradient *gr, SPItem *item, gchar const *property)
Convert an item's gradient to userspace if necessary, also fork it if necessary.
constexpr Coord EPSILON
Default "acceptably small" value.
Definition coord.h:84
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
bool pathvs_have_nonempty_overlap(Geom::PathVector const &a, Geom::PathVector const &b)
An exact check for whether the two pathvectors intersect or overlap, including the case of a line cro...
Definition geom.cpp:512
Specific geometry functions for Inkscape, not provided my lib2geom.
SPItem * item
Geom::Point start
Geom::Point end
Affine identity()
Create an identity matrix.
Definition affine.h:210
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
static R & release(R &r)
Decrements the reference count of a anchored object.
@ SNAPSOURCE_ROTATION_CENTER
Definition snap-enums.h:52
@ SNAPTARGET_ROTATION_CENTER
Definition snap-enums.h:117
STL namespace.
static T clip(T const &v, T const &a, T const &b)
static cairo_user_data_key_t key
std::optional< Geom::PathVector > intersect_clips(std::optional< Geom::PathVector > &&a, std::optional< Geom::PathVector > &&b)
Intersect two clips, treating empty optionals as no clip.
Path utilities.
Singleton class to access the preferences file in a convenient way.
Ocnode * child[8]
Definition quantize.cpp:33
Ocnode ** ref
Definition quantize.cpp:32
int sp_repr_compare_position(Inkscape::XML::Node const *first, Inkscape::XML::Node const *second)
Works for different-parent objects, so long as they have a common ancestor.
auto len
Definition safe-printf.h:21
Some utility classes to store various kinds of snap candidates.
@ SP_FILTER_UNITS_OBJECTBOUNDINGBOX
void sp_guide_pt_pairs_to_guides(SPDocument *doc, std::list< std::pair< Geom::Point, Geom::Point > > &pts)
Definition sp-guide.cpp:268
SPGuide – a guideline.
SVG <hatch> implementation.
static bool is_satellite_item(SPItem const &item)
Return true if the item is referenced by an LPE.
Definition sp-item.cpp:1635
Geom::Affine sp_item_transform_repr(SPItem *item)
Find out the inverse of previous transform of an item (from its repr)
Definition sp-item.cpp:1500
Geom::Affine i2i_affine(SPObject const *src, SPObject const *dest)
Definition sp-item.cpp:1806
SPItem const * sp_item_first_item_child(SPObject const *obj)
Definition sp-item.cpp:1862
Geom::Affine i2anc_affine(SPObject const *object, SPObject const *ancestor)
Definition sp-item.cpp:1787
static void sp_item_adjust_rects_recursive(SPItem *item, Geom::Affine advertized_transform)
Recursively adjust rx and ry of rects.
Definition sp-item.cpp:1549
int sp_item_repr_compare_position(SPItem const *first, SPItem const *second)
Definition sp-item.cpp:1856
Some things pertinent to all visible shapes: SPItem, SPItemView, SPItemCtx.
PaintServerTransform
Definition sp-item.h:56
@ TRANSFORM_STROKE
Definition sp-item.h:56
@ TRANSFORM_FILL
Definition sp-item.h:56
@ TRANSFORM_BOTH
Definition sp-item.h:56
Geom::Affine sp_item_transform_repr(SPItem *item)
Find out the inverse of previous transform of an item (from its repr)
Definition sp-item.cpp:1500
Geom::Affine i2i_affine(SPObject const *src, SPObject const *dest)
Definition sp-item.cpp:1806
Geom::Affine i2anc_affine(SPObject const *item, SPObject const *ancestor)
Definition sp-item.cpp:1787
@ ITEM_KEY_SIZE
Definition sp-item.h:73
@ ITEM_KEY_CLIP
Definition sp-item.h:68
@ ITEM_KEY_STROKE
Definition sp-item.h:71
@ ITEM_KEY_FILL
Definition sp-item.h:70
@ ITEM_KEY_MASK
Definition sp-item.h:69
void sp_lpe_item_update_patheffect(SPLPEItem *lpeitem, bool wholetree, bool write, bool with_satellites)
Calls any registered handlers for the update_patheffect action.
SVG <pattern> implementation.
SPRoot: SVG <svg> implementation.
TODO: insert short description here.
Interface for XML documents.
Definition document.h:43
Unused.
Definition sp-object.h:94
Contains transformations to document/viewport and the viewport size.
Definition sp-item.h:92
SPItemView(unsigned flags, unsigned key, DrawingItemPtr< Inkscape::DrawingItem > drawingitem)
Definition sp-item.cpp:70
unsigned int bind(Geom::Affine const &transform, float opacity)
Definition print.cpp:39
unsigned int release()
Definition print.cpp:45
@ SP_CSS_SHAPE_RENDERING_CRISPEDGES
@ SP_CSS_DISPLAY_NONE
@ SP_CSS_DISPLAY_INLINE
static const unsigned SP_SCALE24_MAX
SPStyle - a style object for SPItem objects.
bool sp_svg_transform_read(gchar const *str, Geom::Affine *transform)
std::string sp_svg_transform_write(Geom::Affine const &transform)
SPDesktop * desktop
Geom::IntPoint dimensions(const Cairo::RefPtr< Cairo::ImageSurface > &surface)
Definition util.cpp:367