diff --git a/src/display/drawing-item.cpp b/src/display/drawing-item.cpp index 2c6848f6c2a1ecd6acc4f3853dbc08c36df23ebe..a06ab3016385eb3ded236cb2d9fbcef081abe321 100644 --- a/src/display/drawing-item.cpp +++ b/src/display/drawing-item.cpp @@ -14,6 +14,7 @@ #include "display/drawing-context.h" #include "display/drawing-group.h" +#include "display/drawing-shape.h" #include "display/drawing-item.h" #include "display/drawing-pattern.h" #include "display/drawing-surface.h" @@ -65,6 +66,7 @@ DrawingItem::DrawingItem(Drawing &drawing) , _transform(nullptr) , _clip(nullptr) , _mask(nullptr) + , _satellite(nullptr) , _fill_pattern(nullptr) , _stroke_pattern(nullptr) , _filter(nullptr) @@ -146,6 +148,7 @@ DrawingItem::~DrawingItem() delete _clip; delete _mask; delete _filter; + _satellite = nullptr; if(_style) sp_style_unref(_style); } @@ -280,6 +283,21 @@ DrawingItem::setVisible(bool v) } } + +void +DrawingItem::setSatellite(DrawingItem *s) +{ + _satellite = s; + _markForUpdate(STATE_ALL, true); +} + +void +DrawingItem::unsetSatellite() +{ + _satellite = nullptr; + _markForUpdate(STATE_ALL, true); +} + /// This is currently unused void DrawingItem::setSensitive(bool s) @@ -585,6 +603,11 @@ DrawingItem::update(Geom::IntRect const &area, UpdateContext const &ctx, unsigne _drawbox.intersectWith(_mask->_drawbox); } } + // Satellite + if (_satellite) { + _bbox.unionWith(_satellite->_bbox); + _drawbox.unionWith(_satellite->_drawbox); + } } if (to_update & STATE_CACHE) { // Update cache score for this item @@ -987,10 +1010,9 @@ DrawingItem::pick(Geom::Point const &p, double delta, unsigned flags) return nullptr; } // ignore invisible and insensitive items unless sticky - if (!(flags & PICK_STICKY) && !(_visible && _sensitive)) { + if (!_satellite && !(flags & PICK_STICKY) && !(_visible && _sensitive)) { return nullptr; } - bool outline = _drawing.outline() || _drawing.outlineOverlay() || _drawing.getOutlineSensitive(); if (!_drawing.outline() && !_drawing.outlineOverlay() && !_drawing.getOutlineSensitive()) { @@ -1023,6 +1045,9 @@ DrawingItem::pick(Geom::Point const &p, double delta, unsigned flags) } if (expanded.contains(p)) { + if (_satellite && _satellite->_pickItem(p, delta, PICK_STICKY)) { + return this; + } return _pickItem(p, delta, flags); } return nullptr; diff --git a/src/display/drawing-item.h b/src/display/drawing-item.h index b1f88a017ea1788ed8db1ee09e351ae5699eee22..917b2d0702db73de4516a4d0a59156ed96595d1d 100644 --- a/src/display/drawing-item.h +++ b/src/display/drawing-item.h @@ -111,6 +111,8 @@ public: bool visible() const { return _visible; } void setVisible(bool v); + void setSatellite(DrawingItem *s); + void unsetSatellite(); bool sensitive() const { return _sensitive; } void setSensitive(bool v); bool cached() const { return _cached; } @@ -208,6 +210,7 @@ protected: DrawingItem *_clip; DrawingItem *_mask; + DrawingItem *_satellite; //satellite item for pick item extended by LPE not to render DrawingPattern *_fill_pattern; DrawingPattern *_stroke_pattern; Inkscape::Filters::Filter *_filter; diff --git a/src/display/drawing-shape.h b/src/display/drawing-shape.h index 319e0e12ceb1c43171c95d449b2ba1d4b688eadb..fcad070984e7c9d544638f03254b740bec2674e6 100644 --- a/src/display/drawing-shape.h +++ b/src/display/drawing-shape.h @@ -47,7 +47,7 @@ protected: void _renderStroke(DrawingContext &dc); void _renderMarkers(DrawingContext &dc, Geom::IntRect const &area, unsigned flags, DrawingItem *stop_at); - + friend class DrawingItem; std::unique_ptr _curve; NRStyle _nrstyle; diff --git a/src/file.cpp b/src/file.cpp index 811e2564b6385513cebac22258c673f02e4d1156..7e1809e16059a900b31794a4d40b8ee660bc4197 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -1148,6 +1148,10 @@ file_import(SPDocument *in_doc, const Glib::ustring &uri, // select and move the imported item if (new_obj && SP_IS_ITEM(new_obj)) { + SPLPEItem *lpeitem = dynamic_cast(new_obj); + if (lpeitem) { + sp_lpe_item_onload_patheffect(lpeitem); + } Inkscape::Selection *selection = desktop->getSelection(); selection->set(SP_ITEM(new_obj)); diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index 4863a62a1973642f221bf41850c445edd463b2d3..2399f3d3afff0ed86a7ce19d7e11461b6b7256cc 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -1186,7 +1186,7 @@ void Effect::transform_multiply(Geom::Affine const &postmul, bool /*set*/) {} * FIXME Probably only makes sense if this effect is referenced by exactly one * item (`this->lpeobj->hrefList` contains exactly one element)? */ -void Effect::transform_multiply(Geom::Affine const &postmul, SPLPEItem *lpeitem) +void Effect::transform_multiply_impl(Geom::Affine const &postmul, SPLPEItem *lpeitem) { assert("pre: effect is referenced by lpeitem" && std::any_of(lpeobj->hrefList.begin(), lpeobj->hrefList.end(), @@ -1240,9 +1240,11 @@ void Effect::processObjects(LPEAction lpe_action) { SPDocument *document = getSPDoc(); - if (!document) { + sp_lpe_item = dynamic_cast(*getLPEObj()->hrefList.begin()); + if (!document || !sp_lpe_item) { return; } + sp_lpe_item_enable_path_effects(sp_lpe_item, false); for (auto id : items) { if (id.empty()) { return; @@ -1296,6 +1298,7 @@ Effect::processObjects(LPEAction lpe_action) if (lpe_action == LPE_ERASE || lpe_action == LPE_TO_OBJECTS) { items.clear(); } + sp_lpe_item_enable_path_effects(sp_lpe_item, true); } /** @@ -1306,6 +1309,16 @@ Effect::doBeforeEffect (SPLPEItem const*/*lpeitem*/) { //Do nothing for simple effects } + +/** + * Is performed each time lpe is load into document. + */ +void +Effect::doOnLoad (SPLPEItem const*/*lpeitem*/) +{ + //Do nothing for simple effects +} + /** * Is performed at the end of the LPE only one time per "lpeitem" * in paths/shapes is called in middle of the effect so we add the @@ -1319,6 +1332,19 @@ void Effect::doAfterEffect (SPLPEItem const* /*lpeitem*/, SPCurve *curve) { is_load = false; } +/** + * Is performed at the end of all lpe`s stack + */ +void Effect::doAfterAllEffects (SPLPEItem const* /*lpeitem*/) +{ +} + +/** + * Is performed at lpe`s fork + */ +void Effect::doOnFork (SPLPEItem const* /*lpeitem*/) +{ +} void Effect::doOnException(SPLPEItem const * /*lpeitem*/) { @@ -1340,6 +1366,19 @@ void Effect::doAfterEffect_impl(SPLPEItem const *lpeitem, SPCurve *curve) is_load = false; is_applied = false; } + +void Effect::doOnFork_impl(SPLPEItem const *lpeitem) +{ + is_load = true; + is_applied = false; + doOnFork(lpeitem); +} + +void Effect::doAfterAllEffects_impl(SPLPEItem const* lpeitem) +{ + doAfterAllEffects(lpeitem); +} + void Effect::doOnApply_impl(SPLPEItem const* lpeitem) { sp_lpe_item = const_cast(lpeitem); @@ -1354,6 +1393,7 @@ void Effect::doOnApply_impl(SPLPEItem const* lpeitem) doOnApply(lpeitem); setReady(); has_exception = false; + lpeversion.param_setValue("1", true); // we can override this value in each LPE when we change backward copat } void Effect::doBeforeEffect_impl(SPLPEItem const* lpeitem) @@ -1363,6 +1403,11 @@ void Effect::doBeforeEffect_impl(SPLPEItem const* lpeitem) update_helperpath(); } +void Effect::doEffect_impl(SPCurve * curve) +{ + doEffect(curve); +} + void Effect::writeParamsToSVG() { std::vector::iterator p; diff --git a/src/live_effects/effect.h b/src/live_effects/effect.h index 2b68661347b7e1fe050c636dd72ba0154e7acc0d..b6524f821e5373f1e3a92adfb55733dfb6970bdb 100644 --- a/src/live_effects/effect.h +++ b/src/live_effects/effect.h @@ -68,25 +68,31 @@ public: //basically, to get this method called before the derived classes, a bit //of indirection is needed. We first call these methods, then the below. - void doAfterEffect_impl(SPLPEItem const *lpeitem, SPCurve *curve); - void doOnApply_impl(SPLPEItem const* lpeitem); - void doBeforeEffect_impl(SPLPEItem const* lpeitem); + void setCurrentZoom(double cZ); void setSelectedNodePoints(std::vector sNP); bool isNodePointSelected(Geom::Point const &nodePoint) const; bool isOnClipboard(); + void doOnApply_impl(SPLPEItem const* lpeitem); + void doBeforeEffect_impl(SPLPEItem const* lpeitem); + void doOnFork_impl(SPLPEItem const* lpeitem); + void doEffect_impl(SPCurve * curve); + void doAfterEffect_impl(SPLPEItem const *lpeitem, SPCurve *curve); + void doAfterAllEffects_impl(SPLPEItem const* lpeitem); + void transform_multiply_impl(Geom::Affine const &postmul, SPLPEItem *); +private: virtual void doOnApply (SPLPEItem const* lpeitem); virtual void doBeforeEffect (SPLPEItem const* lpeitem); - -private: + virtual void doOnFork (SPLPEItem const* lpeitem); + virtual void doEffect (SPCurve * curve); + virtual void doAfterEffect (SPLPEItem const* lpeitem, SPCurve *curve); + virtual void doAfterAllEffects (SPLPEItem const* sp_lpe_item); virtual void transform_multiply(Geom::Affine const &postmul, bool set); - public: - void transform_multiply(Geom::Affine const &postmul, SPLPEItem *); - virtual void doAfterEffect (SPLPEItem const* lpeitem, SPCurve *curve); - virtual void doOnException(SPLPEItem const *lpeitem); + virtual void doOnLoad (SPLPEItem const* lpeitem); + virtual void doOnException (SPLPEItem const *lpeitem); virtual void doOnRemove (SPLPEItem const* lpeitem); - virtual void doOnVisibilityToggled(SPLPEItem const* lpeitem); + virtual void doOnVisibilityToggled (SPLPEItem const* lpeitem); void writeParamsToSVG(); virtual void acceptParamPath (SPPath const* param_path); @@ -104,7 +110,6 @@ public: inline bool isReady() const { return is_ready; } inline void setReady(bool ready = true) { is_ready = ready; } - virtual void doEffect (SPCurve * curve); virtual Gtk::Widget * newWidget(); virtual Gtk::Widget * defaultParamSet(); @@ -153,6 +158,7 @@ public: SPLPEItem *sp_lpe_item; // these get stored in doBeforeEffect_impl, and derived classes may do as they please with // them. SPShape *current_shape; // these get stored in performPathEffects. + std::vector items; protected: Effect(LivePathEffectObject *lpeobject); @@ -188,7 +194,6 @@ public: // this boolean defaults to false, it concatenates the input path to one pwd2, // instead of normally 'splitting' the path into continuous pwd2 paths and calling doEffect_pwd2 for each. bool concatenate_before_pwd2; - std::vector items; double current_zoom; std::vector selectedNodesPoints; diff --git a/src/live_effects/lpe-bendpath.cpp b/src/live_effects/lpe-bendpath.cpp index f4248e53227a3c7ea46bf66a4b9dc986270eb736..07833c02f675371db5da0b4cf2e311d40443b6e6 100644 --- a/src/live_effects/lpe-bendpath.cpp +++ b/src/live_effects/lpe-bendpath.cpp @@ -10,6 +10,7 @@ #include "display/curve.h" #include "live_effects/lpe-bendpath.h" +#include "live_effects/lpeobject.h" #include "ui/knot/knot-holder.h" #include "ui/knot/knot-holder-entity.h" @@ -96,11 +97,15 @@ LPEBendPath::doBeforeEffect (SPLPEItem const* lpeitem) } _knot_entity->update_knot(); } + } void LPEBendPath::transform_multiply(Geom::Affine const &postmul, bool /*set*/) { - if (sp_lpe_item && sp_lpe_item->pathEffectsEnabled() && sp_lpe_item->optimizeTransforms()) { + if (sp_lpe_item == dynamic_cast(*getLPEObj()->hrefList.begin()) && + sp_lpe_item->pathEffectsEnabled() && + sp_lpe_item->optimizeTransforms()) + { bend_path.param_transform_multiply(postmul, false); } } @@ -111,13 +116,11 @@ LPEBendPath::doEffect_pwd2 (Geom::Piecewise > const & pwd using namespace Geom; /* Much credit should go to jfb and mgsloan of lib2geom development for the code below! */ - if (bend_path.changed) { uskeleton = arc_length_parametrization(Piecewise >(bend_path.get_pwd2()),2,.1); uskeleton = remove_short_cuts(uskeleton,.01); n = rot90(derivative(uskeleton)); n = force_continuity(remove_short_cuts(n,.01)); - bend_path.changed = false; } diff --git a/src/live_effects/lpe-bool.cpp b/src/live_effects/lpe-bool.cpp index 09b0ccf02d754a4aa0bdebdf01f52ffb5bd17c9b..73b7873af5715f729a666aaa5cd7daeed70afcaa 100644 --- a/src/live_effects/lpe-bool.cpp +++ b/src/live_effects/lpe-bool.cpp @@ -508,7 +508,9 @@ void LPEBool::doBeforeEffect(SPLPEItem const *lpeitem) void LPEBool::transform_multiply(Geom::Affine const &postmul, bool /*set*/) { - if (operand && !isOnClipboard()) { + if (sp_lpe_item == dynamic_cast(*getLPEObj()->hrefList.begin()) && + operand && !isOnClipboard()) + { SPDesktop *desktop = SP_ACTIVE_DESKTOP; if (desktop && !desktop->getSelection()->includes(operand)) { prev_affine = operand->transform * sp_item_transform_repr(sp_lpe_item).inverse() * postmul; diff --git a/src/live_effects/lpe-copy_rotate.cpp b/src/live_effects/lpe-copy_rotate.cpp index 3cfa27024f18f48e0e522365e0dd80b92e2a7843..b087a7f027ab4738515a8dbfa1151d9d91abddb5 100644 --- a/src/live_effects/lpe-copy_rotate.cpp +++ b/src/live_effects/lpe-copy_rotate.cpp @@ -129,6 +129,8 @@ LPECopyRotate::doAfterEffect (SPLPEItem const* lpeitem, SPCurve *curve) id += std::to_string(counter); id += "-"; id += this->lpeobj->getId(); + id += "_"; + id += this->sp_lpe_item->getId(); if (id.empty()) { return; } @@ -146,11 +148,18 @@ LPECopyRotate::doAfterEffect (SPLPEItem const* lpeitem, SPCurve *curve) guint counter = 0; Glib::ustring id = "rotated-0-"; id += this->lpeobj->getId(); + id += "_"; + id += this->sp_lpe_item->getId(); while((elemref = document->getObjectById(id.c_str()))) { id = Glib::ustring("rotated-"); id += std::to_string(counter); id += "-"; id += this->lpeobj->getId(); + id += "_"; + id += this->sp_lpe_item->getId(); + if (id.empty()) { + return; + } if (SP_ITEM(elemref)->isHidden()) { items.push_back(id); } @@ -181,11 +190,42 @@ LPECopyRotate::doAfterEffect (SPLPEItem const* lpeitem, SPCurve *curve) } reset = false; } else { + SPDocument *document = getSPDoc(); + if (!document) { + return; + } + regenerateItems(); processObjects(LPE_ERASE); items.clear(); } } +void +LPECopyRotate::upgradeLegacy() { + lpeversion.param_setValue("1.1", true); // to avoid multiple applyes + SPDocument *document = getSPDoc(); + if (!document) { + return; + } + items.clear(); + SPObject *elemref = nullptr; + guint counter = 0; + Glib::ustring id = "rotated-0-"; + id += this->lpeobj->getId(); + while((elemref = document->getObjectById(id.c_str()))) { + id = Glib::ustring("rotated-"); + id += std::to_string(counter); + id += "-"; + id += this->lpeobj->getId(); + Glib::ustring new_elemref_id = id; + new_elemref_id += "_"; + new_elemref_id += this->sp_lpe_item->getId(); + items.push_back(new_elemref_id); + elemref->setAttribute("id",new_elemref_id.c_str()); + counter++; + } +} + void LPECopyRotate::cloneStyle(SPObject *orig, SPObject *dest) { dest->getRepr()->setAttribute("style", orig->getRepr()->attribute("style")); @@ -297,11 +337,12 @@ LPECopyRotate::toItem(Geom::Affine transform, size_t i, bool reset) if (!document) { return; } - Inkscape::XML::Document *xml_doc = document->getReprDoc(); Glib::ustring elemref_id = Glib::ustring("rotated-"); elemref_id += std::to_string(i); elemref_id += "-"; elemref_id += this->lpeobj->getId(); + elemref_id += "_"; + elemref_id += this->sp_lpe_item->getId(); items.push_back(elemref_id); SPObject *elemref = document->getObjectById(elemref_id.c_str()); Inkscape::XML::Node *phantom = nullptr; @@ -312,10 +353,12 @@ LPECopyRotate::toItem(Geom::Affine transform, size_t i, bool reset) phantom->setAttribute("id", elemref_id); reset = true; elemref = container->appendChildRepr(phantom); + elemref->parent->reorder(elemref, sp_lpe_item); Inkscape::GC::release(phantom); } cloneD(SP_OBJECT(sp_lpe_item), elemref, transform, reset); elemref->getRepr()->setAttributeOrRemoveIfEmpty("transform", sp_svg_transform_write(transform)); + Inkscape::XML::Document *xml_doc = document->getReprDoc(); SP_ITEM(elemref)->setHidden(false); if (elemref->parent != container) { Inkscape::XML::Node *copy = phantom->duplicate(xml_doc); @@ -376,6 +419,7 @@ Gtk::Widget * LPECopyRotate::newWidget() void LPECopyRotate::doOnApply(SPLPEItem const* lpeitem) { + using namespace Geom; original_bbox(lpeitem, false, true); @@ -385,11 +429,15 @@ LPECopyRotate::doOnApply(SPLPEItem const* lpeitem) origin.param_update_default(A); dist_angle_handle = L2(B - A); dir = unit_vector(B - A); + lpeversion.param_setValue("1.1", true); } void LPECopyRotate::doBeforeEffect (SPLPEItem const* lpeitem) { + if (is_load && lpeversion.param_getSVGValue() < "1.1") { + upgradeLegacy(); + } using namespace Geom; original_bbox(lpeitem, false, true); if (copies_to_360 && num_copies > 2) { @@ -657,15 +705,50 @@ LPECopyRotate::resetDefaults(SPItem const* item) original_bbox(SP_LPE_ITEM(item), false, true); } +void +LPECopyRotate::regenerateItems() { + SPDocument *document = getSPDoc(); + items.clear(); + sp_lpe_item = dynamic_cast(*getLPEObj()->hrefList.begin()); + if (!document || !sp_lpe_item) { + return; + } + SPObject *elemref = nullptr; + guint counter = 0; + Glib::ustring id = "rotated-0-"; + id += getLPEObj()->getId(); + if (sp_lpe_item->getId()) { + id += "_"; + id += sp_lpe_item->getId(); + } + while((elemref = document->getObjectById(id.c_str()))) { + id = Glib::ustring("rotated-"); + id += std::to_string(counter); + id += "-"; + id += this->lpeobj->getId(); + if (sp_lpe_item->getId()) { + id += "_"; + id += this->sp_lpe_item->getId(); + } + if (id.empty()) { + return; + } + items.push_back(id); + counter++; + } +} + void LPECopyRotate::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/) { + regenerateItems(); processObjects(LPE_VISIBILITY); } void LPECopyRotate::doOnRemove (SPLPEItem const* lpeitem) { + regenerateItems(); //set "keep paths" hook on sp-lpe-item.cpp if (keep_paths) { processObjects(LPE_TO_OBJECTS); diff --git a/src/live_effects/lpe-copy_rotate.h b/src/live_effects/lpe-copy_rotate.h index b97beefc06b66b4597ce4970ed90486e22d330f8..45db264777cd14810f11c9ea0bb516622c1c66c5 100644 --- a/src/live_effects/lpe-copy_rotate.h +++ b/src/live_effects/lpe-copy_rotate.h @@ -54,6 +54,8 @@ public: void toItem(Geom::Affine transform, size_t i, bool reset); void cloneD(SPObject *orig, SPObject *dest, Geom::Affine transform, bool reset); Inkscape::XML::Node * createPathBase(SPObject *elemref); + void upgradeLegacy(); + void regenerateItems(); void resetStyles(); //virtual void setFusion(Geom::PathVector &path_in, Geom::Path divider, double sizeDivider); protected: diff --git a/src/live_effects/lpe-envelope.cpp b/src/live_effects/lpe-envelope.cpp index b0ff8a4956ad29bd29b12b41a995595130b3c708..0f17e9ddce922321adc53b7843ea80177c82ed0d 100644 --- a/src/live_effects/lpe-envelope.cpp +++ b/src/live_effects/lpe-envelope.cpp @@ -6,6 +6,7 @@ */ #include "live_effects/lpe-envelope.h" +#include "live_effects/lpeobject.h" #include "display/curve.h" // TODO due to internal breakage in glibmm headers, this must be last: #include @@ -37,7 +38,10 @@ LPEEnvelope::~LPEEnvelope() void LPEEnvelope::transform_multiply(Geom::Affine const &postmul, bool /*set*/) { - if (sp_lpe_item && sp_lpe_item->pathEffectsEnabled() && sp_lpe_item->optimizeTransforms()) { + if (sp_lpe_item == dynamic_cast(*getLPEObj()->hrefList.begin()) && + sp_lpe_item->pathEffectsEnabled() && + sp_lpe_item->optimizeTransforms()) + { bend_path1.param_transform_multiply(postmul, false); bend_path2.param_transform_multiply(postmul, false); bend_path3.param_transform_multiply(postmul, false); diff --git a/src/live_effects/lpe-interpolate.cpp b/src/live_effects/lpe-interpolate.cpp index 3da8726f264f872e6d7d105d7aab8bafc00c4997..1b860e121c0ab1e94467927336acf3d77a09ba0b 100644 --- a/src/live_effects/lpe-interpolate.cpp +++ b/src/live_effects/lpe-interpolate.cpp @@ -11,6 +11,7 @@ * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ #include "live_effects/lpe-interpolate.h" +#include "live_effects/lpeobject.h" #include <2geom/sbasis-to-bezier.h> @@ -49,7 +50,10 @@ LPEInterpolate::~LPEInterpolate() = default; void LPEInterpolate::transform_multiply(Geom::Affine const &postmul, bool /*set*/) { - if (sp_lpe_item && sp_lpe_item->pathEffectsEnabled() && sp_lpe_item->optimizeTransforms()) { + if (sp_lpe_item == dynamic_cast(*getLPEObj()->hrefList.begin()) && + sp_lpe_item->pathEffectsEnabled() && + sp_lpe_item->optimizeTransforms()) + { trajectory_path.param_transform_multiply(postmul, false); } } diff --git a/src/live_effects/lpe-jointype.cpp b/src/live_effects/lpe-jointype.cpp index 4a8b6261c5e032c805e1e1b47e63732f22d50e13..5249ac9a397fa71df5f2bfeead17ee87d552eb5c 100644 --- a/src/live_effects/lpe-jointype.cpp +++ b/src/live_effects/lpe-jointype.cpp @@ -10,6 +10,7 @@ #include "live_effects/parameter/enum.h" #include "live_effects/fill-conversion.h" +#include "live_effects/lpeobject.h" #include "helper/geom-pathstroke.h" #include "desktop-style.h" @@ -116,7 +117,7 @@ void LPEJoinType::transform_multiply(Geom::Affine const &postmul, bool /*set*/) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool transform_stroke = prefs ? prefs->getBool("/options/transform/stroke", true) : true; - if (transform_stroke) { + if (sp_lpe_item == dynamic_cast(*getLPEObj()->hrefList.begin()) && transform_stroke) { line_width.param_transform_multiply(postmul, false); } } diff --git a/src/live_effects/lpe-mirror_symmetry.cpp b/src/live_effects/lpe-mirror_symmetry.cpp index 9edbd21f5023f2408a2ab59a53535a154b6cac4b..57842112b4f3d3ee3b0cc1fce57754cbaffce820 100644 --- a/src/live_effects/lpe-mirror_symmetry.cpp +++ b/src/live_effects/lpe-mirror_symmetry.cpp @@ -103,10 +103,26 @@ LPEMirrorSymmetry::doAfterEffect (SPLPEItem const* lpeitem, SPCurve *curve) m *= sp_lpe_item->transform; toMirror(m); } else { + regenerateItems(); processObjects(LPE_ERASE); items.clear(); } } +void +LPEMirrorSymmetry::regenerateItems() { + items.clear(); + sp_lpe_item = dynamic_cast(*getLPEObj()->hrefList.begin()); + if (sp_lpe_item) { + Glib::ustring elemref_id = Glib::ustring("mirror-"); + elemref_id += getLPEObj()->getId(); + if (sp_lpe_item->getId()) { + elemref_id += "_"; + elemref_id += sp_lpe_item->getId(); + } + items.push_back(elemref_id); + } +} + Gtk::Widget * LPEMirrorSymmetry::newWidget() @@ -176,6 +192,9 @@ LPEMirrorSymmetry::centerHoriz(){ void LPEMirrorSymmetry::doBeforeEffect (SPLPEItem const* lpeitem) { + if (is_load && lpeversion.param_getSVGValue() < "1.1") { + upgradeLegacy(); + } using namespace Geom; original_bbox(lpeitem, false, true); Point point_a(boundingbox_X.max(), boundingbox_Y.min()); @@ -358,26 +377,45 @@ LPEMirrorSymmetry::createPathBase(SPObject *elemref) { } void -LPEMirrorSymmetry::toMirror(Geom::Affine transform) -{ +LPEMirrorSymmetry::upgradeLegacy() { + lpeversion.param_setValue("1.1", true); // to avoid multiple applyes SPDocument *document = getSPDoc(); if (!document) { return; } - Inkscape::XML::Document *xml_doc = document->getReprDoc(); Glib::ustring elemref_id = Glib::ustring("mirror-"); elemref_id += this->lpeobj->getId(); + Glib::ustring new_elemref_id = Glib::ustring("mirror-"); + new_elemref_id += this->lpeobj->getId(); + new_elemref_id += "_"; + new_elemref_id += this->sp_lpe_item->getId(); items.clear(); - items.push_back(elemref_id); + items.push_back(new_elemref_id); SPObject *elemref = document->getObjectById(elemref_id.c_str()); + if (elemref) { + elemref->setAttribute("id",new_elemref_id.c_str()); + } +} + +void +LPEMirrorSymmetry::toMirror(Geom::Affine transform) +{ + SPDocument *document = getSPDoc(); + if (!document) { + return; + } + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + regenerateItems(); + SPObject *elemref = document->getObjectById(items[0].c_str()); Inkscape::XML::Node *phantom = nullptr; if (elemref) { phantom = elemref->getRepr(); } else { phantom = createPathBase(sp_lpe_item); - phantom->setAttribute("id", elemref_id); + phantom->setAttribute("id", items[0]); reset = true; elemref = container->appendChildRepr(phantom); + elemref->parent->reorder(elemref, sp_lpe_item); Inkscape::GC::release(phantom); } cloneD(SP_OBJECT(sp_lpe_item), elemref); @@ -385,7 +423,7 @@ LPEMirrorSymmetry::toMirror(Geom::Affine transform) elemref->getRepr()->setAttributeOrRemoveIfEmpty("transform", sp_svg_transform_write(transform)); if (elemref->parent != container) { Inkscape::XML::Node *copy = phantom->duplicate(xml_doc); - copy->setAttribute("id", elemref_id); + copy->setAttribute("id", items[0]); container->appendChildRepr(copy); Inkscape::GC::release(copy); elemref->deleteObject(); @@ -405,6 +443,7 @@ LPEMirrorSymmetry::resetStyles(){ void LPEMirrorSymmetry::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/) { + regenerateItems(); processObjects(LPE_VISIBILITY); } @@ -412,6 +451,7 @@ void LPEMirrorSymmetry::doOnRemove (SPLPEItem const* /*lpeitem*/) { //set "keep paths" hook on sp-lpe-item.cpp + regenerateItems(); if (keep_paths) { processObjects(LPE_TO_OBJECTS); items.clear(); diff --git a/src/live_effects/lpe-mirror_symmetry.h b/src/live_effects/lpe-mirror_symmetry.h index 9a87ab34a4d850a7aa976ad4f1226c7ec51b972b..c70f7651f0a1492e758f61a4a3b94246549be466 100644 --- a/src/live_effects/lpe-mirror_symmetry.h +++ b/src/live_effects/lpe-mirror_symmetry.h @@ -53,6 +53,8 @@ public: void toMirror(Geom::Affine transform); void cloneD(SPObject *orig, SPObject *dest); Inkscape::XML::Node * createPathBase(SPObject *elemref); + void upgradeLegacy(); + void regenerateItems(); void resetStyles(); void centerVert(); void centerHoriz(); diff --git a/src/live_effects/lpe-offset.cpp b/src/live_effects/lpe-offset.cpp index 530b97adf28fc45345f6f2851f22db4624b4cac6..5b76c8e9d20d8780195ec7cfc28066bb66350a76 100644 --- a/src/live_effects/lpe-offset.cpp +++ b/src/live_effects/lpe-offset.cpp @@ -15,6 +15,7 @@ */ #include "lpe-offset.h" +#include "live_effects/lpeobject.h" #include <2geom/path-intersection.h> #include <2geom/piecewise.h> @@ -180,7 +181,9 @@ void LPEOffset::transform_multiply(Geom::Affine const &postmul, bool /*set*/) refresh_widgets = true; if (!postmul.isTranslation()) { Geom::Affine current_affine = sp_item_transform_repr(sp_lpe_item); - offset.param_transform_multiply(postmul * current_affine.inverse(), true); + if (sp_lpe_item == dynamic_cast(*getLPEObj()->hrefList.begin())) { + offset.param_transform_multiply(postmul * current_affine.inverse(), true); + } } offset_pt *= postmul; } diff --git a/src/live_effects/lpe-patternalongpath.cpp b/src/live_effects/lpe-patternalongpath.cpp index 716ecc6cde1fc2f9ee69894dd8512aa0f431e124..b9d809037621e4d1700b63d88cdacc44dafed9b4 100644 --- a/src/live_effects/lpe-patternalongpath.cpp +++ b/src/live_effects/lpe-patternalongpath.cpp @@ -121,7 +121,10 @@ LPEPatternAlongPath::~LPEPatternAlongPath() void LPEPatternAlongPath::transform_multiply(Geom::Affine const &postmul, bool /*set*/) { - if (sp_lpe_item && sp_lpe_item->pathEffectsEnabled() && sp_lpe_item->optimizeTransforms()) { + if (sp_lpe_item == dynamic_cast(*getLPEObj()->hrefList.begin()) && + sp_lpe_item->pathEffectsEnabled() && + sp_lpe_item->optimizeTransforms()) + { pattern.param_transform_multiply(postmul, false); } } diff --git a/src/live_effects/lpe-perspective-envelope.cpp b/src/live_effects/lpe-perspective-envelope.cpp index 5321ae63d1d5394a9d89b3cf53b3419253d276f3..d7baaab736aafa9343fd2b1a7f18ffe7266c82ab 100644 --- a/src/live_effects/lpe-perspective-envelope.cpp +++ b/src/live_effects/lpe-perspective-envelope.cpp @@ -17,6 +17,7 @@ #include #include "live_effects/lpe-perspective-envelope.h" +#include "live_effects/lpeobject.h" #include "helper/geom.h" #include "display/curve.h" #include @@ -69,7 +70,10 @@ LPEPerspectiveEnvelope::~LPEPerspectiveEnvelope() void LPEPerspectiveEnvelope::transform_multiply(Geom::Affine const &postmul, bool /*set*/) { - if (sp_lpe_item && sp_lpe_item->pathEffectsEnabled() && sp_lpe_item->optimizeTransforms()) { + if (sp_lpe_item == dynamic_cast(*getLPEObj()->hrefList.begin()) && + sp_lpe_item->pathEffectsEnabled() && + sp_lpe_item->optimizeTransforms()) + { up_left_point.param_transform_multiply(postmul, false); up_right_point.param_transform_multiply(postmul, false); down_left_point.param_transform_multiply(postmul, false); diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp index ee40717648168c3913369145ec4ed31bb1a4eb2f..01cdf09517a2ac0dc2347fdad8710b93f8a340fe 100644 --- a/src/live_effects/lpe-powerstroke.cpp +++ b/src/live_effects/lpe-powerstroke.cpp @@ -27,6 +27,7 @@ #include "display/curve.h" #include "display/control/canvas-item-enums.h" +#include "display/drawing-item.h" #include "helper/geom.h" #include "object/sp-shape.h" #include "svg/css-ostringstream.h" @@ -169,14 +170,16 @@ LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) : end_linecap_type(_("End cap:"), _("Determines the shape of the path's end"), "end_linecap_type", LineCapTypeConverter, &wr, this, LINECAP_ZERO_WIDTH) { show_orig_path = true; - + elemref = nullptr; + forked = false; + /// @todo offset_points are initialized with empty path, is that bug-save? interpolator_beta.addSlider(true); interpolator_beta.param_set_range(0.,1.); registerParameter(&offset_points); - registerParameter(¬_jump); + registerParameter(¬_jump); //festure missed need to recreate for 1.1 registerParameter(&sort_points); registerParameter(&interpolator_type); registerParameter(&interpolator_beta); @@ -190,13 +193,131 @@ LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) : scale_width.param_set_digits(4); recusion_limit = 0; has_recursion = false; + displays = 0; + transforming = false; +} + +LPEPowerStroke::~LPEPowerStroke() +{ + if (modified_connection) { + modified_connection.disconnect(); + } + if (satellite_modified_connection) { + satellite_modified_connection.disconnect(); + } + if (parent_modified_connection) { + parent_modified_connection.disconnect(); + } +}; + +void +LPEPowerStroke::regenerateItems() { + sp_lpe_item = dynamic_cast(*getLPEObj()->hrefList.begin()); + if (sp_lpe_item) { + Glib::ustring lpobjid = getLPEObj()->getId(); + Glib::ustring strokeid = lpobjid + "_stroke"; + if (sp_lpe_item->getId()) { + strokeid += "_"; + strokeid += sp_lpe_item->getId(); + } + items.clear(); + items.push_back(strokeid); + } +} + +void +LPEPowerStroke::inicialize() { + if (sp_lpe_item) { + if (!elemref) { + SPDocument *document = getSPDoc(); + regenerateItems(); + elemref = document->getObjectById(items[0].c_str()); + } + if (elemref) { + size_t cdisplays = 0; + for (SPItemView *v = ((SPItem *) (elemref))->display; v != nullptr; v = v->next) { + ++cdisplays; + } + if (displays == 0 || cdisplays != displays) { + Inkscape::DrawingItem *strokeds = nullptr; + SPItemView *v2 = ((SPItem *) (sp_lpe_item))->display; + + for (SPItemView *v = ((SPItem *) (elemref))->display; v != nullptr; v = v->next) { + ++displays; + strokeds = dynamic_cast(v->arenaitem); + if (strokeds) { + Inkscape::DrawingItem *sh = dynamic_cast(v2->arenaitem); + sh->setSatellite(strokeds); + } + v2 = v2->next; + } + } + if (!sp_lpe_item->satellite) { + SPLPEItem *satelliteitem = dynamic_cast(elemref); + sp_lpe_item->satellite = satelliteitem; + } + if (!modified_connection) { + modified_connection = sp_lpe_item->connectModified(sigc::mem_fun(*this, &LPEPowerStroke::modified)); + } + if (!satellite_modified_connection) { + satellite_modified_connection = elemref->connectModified(sigc::mem_fun(*this, &LPEPowerStroke::satellite_modified)); + } + if (!parent_modified_connection) { + satellite_modified_connection = sp_lpe_item->parent->connectModified(sigc::mem_fun(*this, &LPEPowerStroke::parent_modified)); + } + } + } +} + +int LPEPowerStroke::_enableforked(gpointer data) { + LPEPowerStroke *pslpe = static_cast(data); + sp_lpe_item_enable_path_effects(pslpe->sp_lpe_item, true); + return FALSE; +} + +void +LPEPowerStroke::doOnFork (SPLPEItem const* lpeitem) { + sp_lpe_item = const_cast(lpeitem); + sp_lpe_item_enable_path_effects(sp_lpe_item, false); + g_timeout_add(2500, &LPEPowerStroke::_enableforked, this); + doOnLoad(lpeitem); } -LPEPowerStroke::~LPEPowerStroke() = default; +void +LPEPowerStroke::doOnLoad (SPLPEItem const *lpeitem) { + sp_lpe_item = const_cast(lpeitem); + displays = 0; + applyStyle(nullptr); + elemref = nullptr; + inicialize(); + sp_lpe_item_update_patheffect(sp_lpe_item, false, false); +} void LPEPowerStroke::doBeforeEffect(SPLPEItem const *lpeItem) { + + SPDocument *document = getSPDoc(); + regenerateItems(); + elemref = document->getObjectById(items[0].c_str()); + if (elemref) { + SPLPEItem *satellite = dynamic_cast(elemref); + sp_lpe_item_enable_path_effects(satellite, false); + SPObject *obj = dynamic_cast(sp_lpe_item); + if (obj->getAttribute("clip-path") && !elemref->getAttribute("clip-path")) { + elemref->setAttribute("clip-path", obj->getAttribute("clip-path")); + } else if ((g_strcmp0(obj->getAttribute("clip-path"),"none") == 0 || !obj->getAttribute("clip-path")) && elemref->getAttribute("clip-path")) { + elemref->setAttribute("clip-path", nullptr); + } + if (obj->getAttribute("mask") && !elemref->getAttribute("mask")) { + elemref->setAttribute("mask", obj->getAttribute("mask")); + } else if ((g_strcmp0(obj->getAttribute("mask"),"none") == 0 || !obj->getAttribute("mask")) && elemref->getAttribute("mask")) { + elemref->setAttribute("mask", nullptr); + } + if (g_strcmp0(obj->getAttribute("transform"), elemref->getAttribute("transform")) != 0) { + elemref->setAttribute("transform", obj->getAttribute("transform")); + } + } offset_points.set_scale_width(scale_width); if (has_recursion) { has_recursion = false; @@ -204,9 +325,215 @@ LPEPowerStroke::doBeforeEffect(SPLPEItem const *lpeItem) } } -void LPEPowerStroke::applyStyle(SPLPEItem *lpeitem) +void +LPEPowerStroke::createStroke(Geom::PathVector strokepv) +{ + SPDocument *document = getSPDoc(); + if (!document || !sp_lpe_item|| !sp_lpe_item->getId()) { + return; + } + if (!elemref) { + regenerateItems(); + //Here we find if the element of the strokeid exist or not + if (!(elemref = document->getObjectById(items[0].c_str()))) { + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + Inkscape::XML::Node *stroke = xml_doc->createElement("svg:path"); + stroke->setAttribute("id", items[0].c_str()); + elemref = SP_OBJECT(sp_lpe_item->parent->appendChildRepr(stroke)); + Inkscape::GC::release(stroke); + satellite_modified_connection = elemref->connectModified(sigc::mem_fun(*this, &LPEPowerStroke::satellite_modified)); + SPLPEItem *satelliteitem = dynamic_cast(elemref); + sp_lpe_item->satellite = satelliteitem; + } + + } + std::string strokestr = sp_svg_write_path(strokepv); + //Here we find if the element of the strokeid exist or not + Glib::ustring effects = sp_lpe_item->getAttribute("inkscape:path-effect"); + Glib::ustring lpobjid = this->lpeobj->getId(); + Glib::ustring pseffectid = "#" + lpobjid + ";"; + size_t pos = effects.find(pseffectid); + if (pos != Glib::ustring::npos) { + effects.erase(0,pos+pseffectid.size()); + } else { + pseffectid = "#" + lpobjid; + size_t pos = effects.find(pseffectid); + if (pos != Glib::ustring::npos) { + effects.erase(0,pos+pseffectid.size()); + } + } + Inkscape::XML::Node *stroke = elemref->getRepr(); + + elemref->setAttribute("d", strokestr.c_str()); + if (effects.empty()) { + elemref->setAttribute("inkscape:original-d", nullptr); + } else { + elemref->setAttribute("inkscape:original-d", strokestr.c_str()); + } + + if (!effects.empty() && g_strcmp0(stroke->attribute("inkscape:path-effect"), effects.c_str()) != 0) { + stroke->setAttribute("inkscape:path-effect", effects.c_str()); + } else if (effects.empty() && stroke->attribute("inkscape:path-effect")) { + stroke->setAttribute("inkscape:path-effect", nullptr); + } + //we also lock the item to not be selectable + if (!stroke->attribute("sodipodi:insensitive")) { + stroke->setAttribute("sodipodi:insensitive", "true"); + } + inicialize(); +} + +void +LPEPowerStroke::upgradeLegacy() { + lpeversion.param_setValue("1.1", true); // to avoid multiple applyes + sp_lpe_item->style->stroke.read(sp_lpe_item->style->fill.get_value().c_str()); + sp_lpe_item->style->stroke_opacity.read(sp_lpe_item->style->stroke_opacity.get_value().c_str()); + sp_lpe_item->style->stroke_width.read("0"); + sp_lpe_item->style->fill_opacity.read("1.0"); + sp_lpe_item->style->fill.read("none"); + sp_lpe_item->updateRepr(); +} + + + +void +LPEPowerStroke::satellite_modified(SPObject *obj, guint flags) +{ + if ((flags & SP_OBJECT_MODIFIED_FLAG) && !transforming) { + //inicialize(); + } +} + + +void +LPEPowerStroke::parent_modified(SPObject *obj, guint flags) +{ + // TODO:not use flags directy + if (flags == 3) { + SPDocument *document = getSPDoc(); + sp_lpe_item = dynamic_cast(*getLPEObj()->hrefList.begin()); + if (!sp_lpe_item || !document) { + return; + } + elemref = document->getObjectById(items[0].c_str()); + if (elemref && elemref->parent != sp_lpe_item->parent) { + sp_lpe_item->satellite = nullptr; + elemref->deleteObject(true); + elemref = nullptr; + SPLPEItem * lpeitem = dynamic_cast(sp_lpe_item); + sp_lpe_item_update_patheffect(lpeitem, false, false); + applyStyle(nullptr); + } + } +} + + +void +LPEPowerStroke::modified(SPObject *obj, guint flags) +{ + // TODO get the flags for transforms + SPDocument *document = getSPDoc(); + if (!document || !sp_lpe_item) { + return; + } + if (elemref && flags == 65) { + elemref = document->getObjectById(items[0].c_str()); + SPLPEItem * lpeitem = dynamic_cast(elemref); + if (sp_lpe_item->transform == Geom::identity()) { + lpeitem->setAttribute("transform",sp_lpe_item->getAttribute("transform")); + transforming = false; + } else if (sp_lpe_item->transform != Geom::identity()){ + lpeitem->transform = sp_lpe_item->transform; + lpeitem->updateRepr(); + transforming = true; + } + } + regenerateItems(); + // this function is executed each time the item style is changed + if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG && items.size()) { //style changed + //get the fill item + elemref = document->getObjectById(items[0].c_str()); + if (elemref) { + elemref->style->fill.read(sp_lpe_item->style->stroke.get_value().c_str()); + elemref->style->fill_opacity.read(sp_lpe_item->style->stroke_opacity.get_value().c_str()); + + if (sp_lpe_item->style->filter.set) { + elemref->style->filter.read(sp_lpe_item->style->filter.get_value().c_str()); + } else { + elemref->style->filter.read(nullptr); + } + elemref->style->stroke.read(nullptr); + elemref->style->stroke_opacity.read("1.0"); + elemref->updateRepr(); + + sp_lpe_item->style->fill.readIfUnset("none"); + sp_lpe_item->style->stroke_width.read("0.0"); + sp_lpe_item->updateRepr(); + auto paint_order = sp_lpe_item->style->paint_order.get_value(); + auto item_a = dynamic_cast(sp_lpe_item); + auto item_b = dynamic_cast(elemref); + if(item_a && item_b) { + // We're ignoring markers at the moment, there isn't any markers + // Paint order where stroke or fill doesn't appear 'normal' will be Fill, then stroke + if(paint_order.find("stroke") < paint_order.find("fill")) { + // Stroke, then fill + if (item_a->getPosition() < item_b->getPosition()) { + item_a->moveTo(item_b, false); + } + } else { + // Fill, then stroke (normal) + if (item_a->getPosition() > item_b->getPosition()) { + item_b->moveTo(item_a, false); + } + } + } + } + } +} + +//now adding common to handle external items +void +LPEPowerStroke::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/) +{ + regenerateItems(); + processObjects(LPE_VISIBILITY); +} + +void +LPEPowerStroke::doOnRemove (SPLPEItem const* /*lpeitem*/) { - lpe_shape_convert_stroke_and_fill(SP_SHAPE(lpeitem)); + //set "keep paths" hook on sp-lpe-item.cpp + regenerateItems(); + for (SPItemView *v = ((SPItem *) (sp_lpe_item))->display; v != nullptr; v = v->next) { + Inkscape::DrawingItem *sh = dynamic_cast(v->arenaitem); + sh->unsetSatellite(); + } + if (keep_paths) { + processObjects(LPE_TO_OBJECTS); + items.clear(); + return; + } + SPShape *shape = dynamic_cast(sp_lpe_item); + if (shape) { + SPCSSAttr *css = sp_repr_css_attr_new(); + Inkscape::CSSOStringStream os; + os << fabs(offset_points.median_width() * 2 * sp_lpe_item->i2doc_affine().descrim()); + sp_repr_css_set_property(css, "stroke-width", os.str().c_str()); + + sp_desktop_apply_css_recursive(shape, css, true); + sp_repr_css_attr_unref(css); + } + sp_lpe_item->satellite = nullptr; + processObjects(LPE_ERASE); + +} + +void LPEPowerStroke::applyStyle(SPLPEItem */*lpeitem*/) +{ + SPObject *object = dynamic_cast(sp_lpe_item); + if (object) { + modified(object, SP_OBJECT_STYLE_MODIFIED_FLAG); + } } void @@ -244,6 +571,7 @@ LPEPowerStroke::doOnApply(SPLPEItem const* lpeitem) offset_points.param_set_and_write_new_value(points); } offset_points.set_scale_width(scale_width); + lpeversion.param_setValue("1.1", true); // we can override this value in each LPE when we change backward copat } else { if (!SP_IS_SHAPE(lpeitem)) { g_warning("LPE Powerstroke can only be applied to shapes (not groups)."); @@ -251,13 +579,6 @@ LPEPowerStroke::doOnApply(SPLPEItem const* lpeitem) } } -void LPEPowerStroke::doOnRemove(SPLPEItem const* lpeitem) -{ - if (SP_IS_SHAPE(lpeitem) && !keep_paths) { - lpe_shape_revert_stroke_and_fill(SP_SHAPE(lpeitem), offset_points.median_width()*2); - } -} - void LPEPowerStroke::adjustForNewPath(Geom::PathVector const & path_in) { @@ -422,11 +743,11 @@ static Geom::Path path_from_piecewise_fix_cusps( Geom::PiecewiseisDegenerate() && !arc0->isLineSegment()) { // FIX: Some assertions errors here build_from_sbasis(pb,arc0->toSBasis(), tol, false); build = true; - } else if (arc1) { + } else if (arc1 && !arc1->isDegenerate() && !arc1->isLineSegment()) { boost::optional p = intersection_point( B[prev_i].at1(), tang1, B[i].at0(), tang2 ); if (p) { @@ -769,14 +1090,45 @@ LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in) } if (path_out.empty()) { return path_in; - // doEffect_path (path_in); } - return path_out; + createStroke(path_out); + if (is_load && lpeversion.param_getSVGValue() < "1.1") { + upgradeLegacy(); + } + return path_in; } void LPEPowerStroke::transform_multiply(Geom::Affine const &postmul, bool /*set*/) { - offset_points.param_transform_multiply(postmul, false); + if (sp_lpe_item == dynamic_cast(*getLPEObj()->hrefList.begin())) { + offset_points.param_transform_multiply(postmul, false); + } +} + +/** + * Is performed at the end of all lpe`s stack + */ +void LPEPowerStroke::doAfterAllEffects (SPLPEItem const* /*lpeitem*/) +{ + inicialize(); + SPDocument *document = getSPDoc(); + if (!document || !sp_lpe_item|| !sp_lpe_item->getId()) { + return; + } + regenerateItems(); + elemref = document->getObjectById(items[0].c_str()); + if (elemref && elemref->parent != sp_lpe_item->parent) { + SPLPEItem * lpeitem = dynamic_cast(sp_lpe_item); + sp_lpe_item->satellite = nullptr; + elemref->deleteObject(true); + elemref = nullptr; + + sp_lpe_item_update_patheffect(lpeitem, false, false); + } else { + SPLPEItem *satellite = dynamic_cast(elemref); + sp_lpe_item_enable_path_effects(satellite, true); + sp_lpe_item_update_patheffect(satellite, false, false); + } } void LPEPowerStroke::doAfterEffect(SPLPEItem const *lpeitem, SPCurve *curve) diff --git a/src/live_effects/lpe-powerstroke.h b/src/live_effects/lpe-powerstroke.h index d88352d5f0561fe5d906baa6e30adf0d3551a195..e70ec8b83c4ff2b68b3f221a1d3fc79d100ec6c7 100644 --- a/src/live_effects/lpe-powerstroke.h +++ b/src/live_effects/lpe-powerstroke.h @@ -43,15 +43,27 @@ public: void doBeforeEffect(SPLPEItem const *lpeItem) override; void doOnApply(SPLPEItem const* lpeitem) override; void doOnRemove(SPLPEItem const* lpeitem) override; + void doOnLoad (SPLPEItem const* lpeitem) override; + void doOnFork (SPLPEItem const* lpeitem) override; void doAfterEffect(SPLPEItem const *lpeitem, SPCurve *curve) override; + void doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/) override; void transform_multiply(Geom::Affine const &postmul, bool set) override; void applyStyle(SPLPEItem *lpeitem); + void createStroke(Geom::PathVector stroke); + void inicialize(); + void doAfterAllEffects (SPLPEItem const* /*lpeitem*/) override; + void modified(SPObject *obj, guint flags); + void satellite_modified(SPObject *obj, guint flags); + void parent_modified(SPObject *obj, guint flags); + void regenerateItems(); + void upgradeLegacy(); // methods called by path-manipulator upon edits void adjustForNewPath(Geom::PathVector const & path_in); PowerStrokePointArrayParam offset_points; BoolParam not_jump; private: + static int _enableforked(gpointer data); BoolParam sort_points; EnumParam interpolator_type; ScalarParam interpolator_beta; @@ -60,8 +72,15 @@ private: EnumParam linejoin_type; ScalarParam miter_limit; EnumParam end_linecap_type; + SPObject * elemref; size_t recusion_limit; + sigc::connection modified_connection; + sigc::connection satellite_modified_connection; + sigc::connection parent_modified_connection; bool has_recursion; + bool transforming; + bool forked; + size_t displays; }; } //namespace LivePathEffect diff --git a/src/live_effects/lpe-taperstroke.cpp b/src/live_effects/lpe-taperstroke.cpp index 13f1c54c31f799b0d3b1fc96f1c4ed20ddf027e8..90c9826e6feccc6acb136dc5fff13c386d4de905 100644 --- a/src/live_effects/lpe-taperstroke.cpp +++ b/src/live_effects/lpe-taperstroke.cpp @@ -14,6 +14,7 @@ #include "live_effects/lpe-taperstroke.h" #include "live_effects/fill-conversion.h" +#include "live_effects/lpeobject.h" #include <2geom/circle.h> #include <2geom/sbasis-to-bezier.h> @@ -120,7 +121,7 @@ void LPETaperStroke::transform_multiply(Geom::Affine const &postmul, bool /*set* { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool transform_stroke = prefs ? prefs->getBool("/options/transform/stroke", true) : true; - if (transform_stroke) { + if (sp_lpe_item == dynamic_cast(*getLPEObj()->hrefList.begin()) && transform_stroke) { line_width.param_transform_multiply(postmul, false); } } diff --git a/src/live_effects/lpe-test-doEffect-stack.cpp b/src/live_effects/lpe-test-doEffect-stack.cpp index 30b93881c8881d1e95413afa8bf0cae79a371f7d..4a22cfb2be68b527a15d103c2cdf30c4e34cccd6 100644 --- a/src/live_effects/lpe-test-doEffect-stack.cpp +++ b/src/live_effects/lpe-test-doEffect-stack.cpp @@ -36,7 +36,7 @@ void LPEdoEffectStackTest::doEffect (SPCurve * curve) { if (step >= 1) { - Effect::doEffect(curve); + Effect::doEffect_impl(curve); } else { // return here return; diff --git a/src/live_effects/lpe-transform_2pts.cpp b/src/live_effects/lpe-transform_2pts.cpp index 99b081b6598d667e520cbe0315062c7739f289c6..94415066d0aba1b369d31d3dda2731e820767cb2 100644 --- a/src/live_effects/lpe-transform_2pts.cpp +++ b/src/live_effects/lpe-transform_2pts.cpp @@ -16,6 +16,7 @@ #include "display/curve.h" #include "helper/geom.h" #include "live_effects/lpe-transform_2pts.h" +#include "live_effects/lpeobject.h" #include "object/sp-path.h" #include "svg/svg.h" #include "ui/icon-names.h" @@ -119,7 +120,10 @@ LPETransform2Pts::doOnApply(SPLPEItem const* lpeitem) void LPETransform2Pts::transform_multiply(Geom::Affine const &postmul, bool /*set*/) { - if (sp_lpe_item && sp_lpe_item->pathEffectsEnabled() && sp_lpe_item->optimizeTransforms()) { + if (sp_lpe_item == dynamic_cast(*getLPEObj()->hrefList.begin()) && + sp_lpe_item->pathEffectsEnabled() && + sp_lpe_item->optimizeTransforms()) + { start.param_transform_multiply(postmul, false); end.param_transform_multiply(postmul, false); } diff --git a/src/live_effects/lpegroupbbox.cpp b/src/live_effects/lpegroupbbox.cpp index 863b08cc139828c7718310de543dff7ba28c3195..f6f27a6ee8339f1dbe3293b7eac536958a3b977e 100644 --- a/src/live_effects/lpegroupbbox.cpp +++ b/src/live_effects/lpegroupbbox.cpp @@ -67,6 +67,9 @@ void GroupBBoxEffect::original_bbox(SPLPEItem const* lpeitem, bool absolute, boo } Geom::OptRect bbox = lpeitem->geometricBounds(transform); + if (lpeitem->satellite) { + //bbox.unionWith(lpeitem->satellite->geometricBounds(transform)); + } if (clip_mask) { SPLPEItem * item = const_cast(lpeitem); bbox.unionWith(clip_mask_bbox(item, transform * item->transform.inverse())); diff --git a/src/object/sp-item-group.cpp b/src/object/sp-item-group.cpp index 64c69cb6be518780eb1cfbbcef8f08f2c4df0830..7a12484b34a543259bc8f0cd53d6f45777805d6b 100644 --- a/src/object/sp-item-group.cpp +++ b/src/object/sp-item-group.cpp @@ -282,7 +282,9 @@ Geom::OptRect SPGroup::bbox(Geom::Affine const &transform, SPItem::BBoxType bbox bbox |= item->bounds(bboxtype, ct); } } - + if (satellite) { + bbox.unionWith(satellite->bbox(transform, bboxtype)); + } return bbox; } @@ -849,6 +851,7 @@ void SPGroup::update_patheffect(bool write) { } } } + this->finishPatheffectStack(); } } @@ -876,15 +879,15 @@ sp_group_perform_patheffect(SPGroup *group, SPGroup *top_group, Inkscape::LivePa lpe->pathvector_before_effect = c->get_pathvector(); c->transform(i2anc_affine(sub_shape, top_group)); sub_shape->setCurveInsync(c.get()); - if (lpe->lpeversion.param_getSVGValue() != "0") { // we are on 1 or up - sub_shape->bbox_vis_cache_is_valid = false; - sub_shape->bbox_geom_cache_is_valid = false; - } success = top_group->performOnePathEffect(c.get(), sub_shape, lpe); c->transform(i2anc_affine(sub_shape, top_group).inverse()); Inkscape::XML::Node *repr = sub_item->getRepr(); if (c && success) { sub_shape->setCurveInsync(c.get()); + if (lpe->lpeversion.param_getSVGValue() != "0") { // we are on 1 or up + sub_shape->bbox_vis_cache_is_valid = false; + sub_shape->bbox_geom_cache_is_valid = false; + } lpe->pathvector_after_effect = c->get_pathvector(); if (write) { repr->setAttribute("d", sp_svg_write_path(lpe->pathvector_after_effect)); diff --git a/src/object/sp-lpe-item.cpp b/src/object/sp-lpe-item.cpp index 4dc4f07ff2907b4c605e9adce8bee0db23ebdc92..6ddc6001b2cabb088af8912e1090da32fd1abe17 100755 --- a/src/object/sp-lpe-item.cpp +++ b/src/object/sp-lpe-item.cpp @@ -73,6 +73,7 @@ SPLPEItem::SPLPEItem() , lpe_modified_connection_list(new std::list()) , current_path_effect(nullptr) , lpe_helperpaths() + , satellite(nullptr) { } @@ -225,6 +226,28 @@ bool SPLPEItem::performPathEffect(SPCurve *curve, SPShape *current, bool is_clip return true; } +void +SPLPEItem::finishPatheffectStack() { + if (this->hasPathEffect() && this->pathEffectsEnabled()) { + PathEffectList path_effect_list(*this->path_effect_list); + for (auto &lperef : path_effect_list) { + LivePathEffectObject *lpeobj = lperef->lpeobject; + if (!lpeobj) { + /** \todo Investigate the cause of this. + * For example, this happens when copy pasting an object with LPE applied. Probably because the object is pasted while the effect is not yet pasted to defs, and cannot be found. + */ + g_warning("SPLPEItem::performPathEffect - NULL lpeobj in list!"); + return; + } + + Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe(); + if (lpe) { + lpe->doAfterAllEffects_impl(this); + } + } + } +} + /** * returns true when LPE was successful. */ @@ -250,17 +273,13 @@ bool SPLPEItem::performOnePathEffect(SPCurve *curve, SPShape *current, Inkscape: } // To Calculate BBox on shapes and nested LPE current->setCurveInsync(curve); - if (lpe->lpeversion.param_getSVGValue() != "0") { // we are on 1 or up - current->bbox_vis_cache_is_valid = false; - current->bbox_geom_cache_is_valid = false; - } // Groups have their doBeforeEffect called elsewhere if (!SP_IS_GROUP(this) && !is_clip_or_mask) { lpe->doBeforeEffect_impl(this); } try { - lpe->doEffect(curve); + lpe->doEffect_impl(curve); lpe->has_exception = false; } @@ -283,6 +302,10 @@ bool SPLPEItem::performOnePathEffect(SPCurve *curve, SPShape *current, Inkscape: } lpe->doAfterEffect_impl(this, curve); } + if (lpe->lpeversion.param_getSVGValue() != "0") { // we are on 1 or up + current->bbox_vis_cache_is_valid = false; + current->bbox_geom_cache_is_valid = false; + } } } return true; @@ -297,6 +320,14 @@ bool SPLPEItem::optimizeTransforms() if (dynamic_cast(this)) { return false; } + auto* mask_path = this->getMaskObject(); + if(mask_path) { + return false; + } + auto* clip_path = this->getClipObject(); + if(clip_path) { + return false; + } PathEffectList path_effect_list(*this->path_effect_list); for (auto &lperef : path_effect_list) { if (!lperef) { @@ -350,7 +381,7 @@ void SPLPEItem::notifyTransform(Geom::Affine const &postmul) if (lpeobj) { Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe(); if (lpe && !lpe->is_load) { - lpe->transform_multiply(postmul, this); + lpe->transform_multiply_impl(postmul, this); } } } @@ -361,6 +392,43 @@ void SPLPEItem::update_patheffect(bool /*write*/) { //throw; } +/** + * Calls on load LPE from existing documents + * This can be used by LPE thar need some kind of process on loading outside the whole stack + */ + void +sp_lpe_item_onload_patheffect (SPLPEItem *lpeitem) { +#ifdef SHAPE_VERBOSE + g_message("sp_lpe_item_onload_patheffect: %p\n", lpeitem); +#endif + g_return_if_fail (lpeitem != nullptr); + g_return_if_fail (SP_IS_OBJECT (lpeitem)); + g_return_if_fail (SP_IS_LPE_ITEM (lpeitem)); + SPGroup * group = dynamic_cast(lpeitem); + if (group) { + std::vector item_list = sp_item_group_item_list(group); + for (auto iter : item_list) { + SPLPEItem* subitem = dynamic_cast(iter); + if (subitem) { + sp_lpe_item_onload_patheffect(subitem); + } + } + } + PathEffectList path_effect_list(*lpeitem->path_effect_list); + for (auto &lperef : path_effect_list) { + if (!lperef) { + continue; + } + LivePathEffectObject *lpeobj = lperef->lpeobject; + if (lpeobj) { + Inkscape::LivePathEffect::Effect * lpe = lpeobj->get_lpe(); + if (lpe) { + lpe->doOnLoad(lpeitem); + } + } + } +} + /** * Calls any registered handlers for the update_patheffect action */ @@ -406,6 +474,8 @@ lpeobject_ref_modified(SPObject */*href*/, guint flags, SPLPEItem *lpeitem) #endif if (flags != 29 && flags != 253) { sp_lpe_item_update_patheffect (lpeitem, true, true); + } else if (flags == 29) { + sp_lpe_item_onload_patheffect (lpeitem); } } @@ -431,8 +501,8 @@ sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem) sp_lpe_item_create_original_path_recursive(mask_data); } } - if (SP_IS_GROUP(lpeitem)) { - std::vector item_list = sp_item_group_item_list(SP_GROUP(lpeitem)); + if (SPGroup * group = dynamic_cast(lpeitem)) { + std::vector item_list = sp_item_group_item_list(group); for (auto subitem : item_list) { if (SP_IS_LPE_ITEM(subitem)) { sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(subitem)); @@ -614,7 +684,10 @@ void SPLPEItem::addPathEffect(std::string value, bool reset) if (!value.empty()) { // Apply the path effects here because in the casse of a group, lpe->resetDefaults // needs that all the subitems have their effects applied - sp_lpe_item_update_patheffect(this, false, true); + SPGroup *group = dynamic_cast(this); + if (group) { + sp_lpe_item_update_patheffect(this, false, true); + } // Disable the path effects while preparing the new lpe sp_lpe_item_enable_path_effects(this, false); @@ -1246,7 +1319,7 @@ size_t SPLPEItem::countLPEOfType(int const type, bool inc_hidden, bool is_ready) LivePathEffectObject const *lpeobj = (*it)->lpeobject; if (lpeobj) { Inkscape::LivePathEffect::Effect const* lpe = lpeobj->get_lpe(); - if (lpe && (lpe->effectType() == type) && lpe->is_visible || inc_hidden) { + if (lpe && (lpe->effectType() == type) && (lpe->is_visible || inc_hidden)) { if (is_ready || lpe->isReady()) { counter++; } @@ -1348,7 +1421,7 @@ bool SPLPEItem::forkPathEffectsIfNecessary(unsigned int nr_of_allowed_users, boo LivePathEffectObject *forked_lpeobj = lpeobj->fork_private_if_necessary(nr_of_allowed_users); if (forked_lpeobj && forked_lpeobj != lpeobj) { forked = true; - forked_lpeobj->get_lpe()->is_load = true; + forked_lpeobj->get_lpe()->doOnFork_impl(this); old_lpeobjs.push_back(lpeobj); new_lpeobjs.push_back(forked_lpeobj); } @@ -1377,6 +1450,19 @@ void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable) // Are the path effects enabled on this item ? bool SPLPEItem::pathEffectsEnabled() const { + Inkscape::XML::Node *root = this->document->getReprRoot(); + Inkscape::XML::Node *clipnode = sp_repr_lookup_name(root, "inkscape:clipboard", 1); + if (clipnode) { + return false; + } + SPLPEItem const *lpeitem = this; + while (lpeitem) { + lpeitem = dynamic_cast(lpeitem->parent); + if (lpeitem && lpeitem->getAttribute("inkscape:lpeinsensitive")) { + //return false; + } + } + return path_effects_enabled > 0; } diff --git a/src/object/sp-lpe-item.h b/src/object/sp-lpe-item.h index 95bee2dcf4ca01a81913156c305b5f1f3ef7eb95..185f063674eb26ad4941cb8a6db99a7e7ceeba6f 100644 --- a/src/object/sp-lpe-item.h +++ b/src/object/sp-lpe-item.h @@ -34,9 +34,19 @@ namespace Inkscape{ namespace LivePathEffect{ class LPEObjectReference; class Effect; + class LPEPowerStroke; + class GroupBBoxEffect; } } +namespace Inkscape { +class DrawingItem; +namespace LivePathEffect { +class LPEPowerStroke; +class GroupBBoxEffect; +} +} + typedef std::list PathEffectList; class SPLPEItem : public SPItem { @@ -91,6 +101,7 @@ public: void downCurrentPathEffect(); void upCurrentPathEffect(); + void finishPatheffectStack(); Inkscape::LivePathEffect::LPEObjectReference* getCurrentLPEReference(); Inkscape::LivePathEffect::Effect* getCurrentLPE(); Inkscape::LivePathEffect::LPEObjectReference* getPrevLPEReference(Inkscape::LivePathEffect::LPEObjectReference* lperef); @@ -107,9 +118,13 @@ public: void applyToClipPath(SPItem* to, Inkscape::LivePathEffect::Effect *lpe = nullptr); void applyToClipPathOrMask(SPItem * clip_mask, SPItem* to, Inkscape::LivePathEffect::Effect *lpe = nullptr); bool forkPathEffectsIfNecessary(unsigned int nr_of_allowed_users = 1, bool recursive = true); - void editNextParamOncanvas(SPDesktop *dt); + friend class Inkscape::LivePathEffect::LPEPowerStroke; + friend class Inkscape::LivePathEffect::GroupBBoxEffect; + // Maybe we need a vector in the future, keep single and use groups for simplicity + SPLPEItem * satellite; }; +void sp_lpe_item_onload_patheffect (SPLPEItem *lpeitem); void sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool wholetree, bool write); // careful, class already has method with *very* similar name! void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable); diff --git a/src/object/sp-shape.cpp b/src/object/sp-shape.cpp index 1d5df5ea3769cd8f549e48119916896ed78494a7..c4235271e08c509c3c2d854991e44845d2dd393e 100644 --- a/src/object/sp-shape.cpp +++ b/src/object/sp-shape.cpp @@ -473,7 +473,11 @@ Geom::OptRect SPShape::bbox(Geom::Affine const &transform, SPItem::BBoxType bbox bbox_vis_cache_transform = transform; bbox_vis_cache_is_valid = true; } - return bbox_vis_cache; + Geom::OptRect bbox = bbox_vis_cache; + if (satellite) { + bbox.unionWith(satellite->bbox(transform, bboxtype)); + } + return bbox; } else { bbox_geom_cache = either_bbox(transform, bboxtype, bbox_geom_cache_is_valid, bbox_geom_cache, bbox_geom_cache_transform); @@ -481,7 +485,11 @@ Geom::OptRect SPShape::bbox(Geom::Affine const &transform, SPItem::BBoxType bbox bbox_geom_cache_transform = transform; bbox_geom_cache_is_valid = true; } - return bbox_geom_cache; + Geom::OptRect bbox = bbox_geom_cache; + if (satellite) { + bbox.unionWith(satellite->bbox(transform, bboxtype)); + } + return bbox; } } @@ -838,6 +846,9 @@ void SPShape::update_patheffect(bool write) repr->removeAttribute("d"); } } + if (hasPathEffectRecursive() && pathEffectsEnabled()) { + finishPatheffectStack(); + } this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } } diff --git a/src/ui/toolbar/pencil-toolbar.cpp b/src/ui/toolbar/pencil-toolbar.cpp index 06ad319df1d526a4044bb920984c0cd0b8438ceb..7ec51fc19ea7780d0c6db7b25e42916c3d9d0f7f 100644 --- a/src/ui/toolbar/pencil-toolbar.cpp +++ b/src/ui/toolbar/pencil-toolbar.cpp @@ -566,7 +566,7 @@ PencilToolbar::simplify_flatten() SPShape * shape = dynamic_cast(lpeitem); if(shape){ auto c = SPCurve::copy(shape->curveForEdit()); - lpe->doEffect(c.get()); + lpe->doEffect_impl(c.get()); lpeitem->setCurrentPathEffect(*i); if (lpelist.size() > 1){ lpeitem->removeCurrentPathEffect(true); @@ -610,7 +610,7 @@ PencilToolbar::flatten_spiro_bspline() SPShape * shape = dynamic_cast(lpeitem); if(shape){ auto c = SPCurve::copy(shape->curveForEdit()); - lpe->doEffect(c.get()); + lpe->doEffect_impl(c.get()); lpeitem->setCurrentPathEffect(*i); if (lpelist.size() > 1){ lpeitem->removeCurrentPathEffect(true); diff --git a/src/ui/tools/freehand-base.cpp b/src/ui/tools/freehand-base.cpp index 501a30695d7e9f65259ae6e03b28294189ef4bf9..e03507eee70e90dcd7f7ad5c8c573d4a35795aa2 100644 --- a/src/ui/tools/freehand-base.cpp +++ b/src/ui/tools/freehand-base.cpp @@ -240,38 +240,6 @@ static void spdc_paste_curve_as_freehand_shape(Geom::PathVector const &newpath, DocumentUndo::setUndoSensitive(document, saved); } -void spdc_apply_style(SPObject *obj) -{ - SPCSSAttr *css = sp_repr_css_attr_new(); - if (obj->style) { - if (obj->style->stroke.isPaintserver()) { - SPPaintServer *server = obj->style->getStrokePaintServer(); - if (server) { - Glib::ustring str; - str += "url(#"; - str += server->getId(); - str += ")"; - sp_repr_css_set_property(css, "fill", str.c_str()); - } - } else if (obj->style->stroke.isColor()) { - gchar c[64]; - sp_svg_write_color( - c, sizeof(c), - obj->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(obj->style->stroke_opacity.value))); - sp_repr_css_set_property(css, "fill", c); - } else { - sp_repr_css_set_property(css, "fill", "none"); - } - } else { - sp_repr_css_unset_property(css, "fill"); - } - - sp_repr_css_set_property(css, "fill-rule", "nonzero"); - sp_repr_css_set_property(css, "stroke", "none"); - - sp_desktop_apply_css_recursive(obj, css, true); - sp_repr_css_attr_unref(css); -} static void spdc_apply_powerstroke_shape(std::vector points, FreehandBase *dc, SPItem *item, gint maxrecursion = 0) { @@ -285,11 +253,9 @@ static void spdc_apply_powerstroke_shape(std::vector points, Freeha if (dc->tablet_enabled) { SPObject *elemref = nullptr; if ((elemref = document->getObjectById("power_stroke_preview"))) { - elemref->getRepr()->removeAttribute("style"); SPItem *successor = dynamic_cast(elemref); sp_desktop_apply_style_tool(desktop, successor->getRepr(), - Glib::ustring("/tools/freehand/pencil").data(), false); - spdc_apply_style(successor); + "/tools/freehand/pencil", false); sp_object_ref(item); item->deleteObject(false); item->setSuccessor(successor); @@ -306,20 +272,22 @@ static void spdc_apply_powerstroke_shape(std::vector points, Freeha bool saved = DocumentUndo::getUndoSensitive(document); DocumentUndo::setUndoSensitive(document, false); Effect::createAndApply(POWERSTROKE, document, item); - Effect* lpe = SP_LPE_ITEM(item)->getCurrentLPE(); - - static_cast(lpe)->offset_points.param_set_and_write_new_value(points); - + LPEPowerStroke* lpe = dynamic_cast(SP_LPE_ITEM(item)->getCurrentLPE()); // write powerstroke parameters: - lpe->getRepr()->setAttribute("start_linecap_type", "zerowidth"); - lpe->getRepr()->setAttribute("end_linecap_type", "zerowidth"); - lpe->getRepr()->setAttribute("sort_points", "true"); - lpe->getRepr()->setAttribute("not_jump", "false"); - lpe->getRepr()->setAttribute("interpolator_type", "CubicBezierJohan"); - lpe->getRepr()->setAttribute("interpolator_beta", "0.2"); - lpe->getRepr()->setAttribute("miter_limit", "4"); - lpe->getRepr()->setAttribute("scale_width", "1"); - lpe->getRepr()->setAttribute("linejoin_type", "extrp_arc"); + if (lpe) { + sp_desktop_apply_style_tool(desktop, item->getRepr(), tool_name(dc), false); + lpe->offset_points.param_set_and_write_new_value(points); + lpe->getRepr()->setAttribute("start_linecap_type", "zerowidth"); + lpe->getRepr()->setAttribute("end_linecap_type", "zerowidth"); + lpe->getRepr()->setAttribute("sort_points", "true"); + lpe->getRepr()->setAttribute("not_jump", "false"); + lpe->getRepr()->setAttribute("interpolator_type", "CubicBezierJohan"); + lpe->getRepr()->setAttribute("interpolator_beta", "0.2"); + lpe->getRepr()->setAttribute("miter_limit", "4"); + lpe->getRepr()->setAttribute("scale_width", "1"); + lpe->getRepr()->setAttribute("linejoin_type", "extrp_arc"); + lpe->applyStyle(nullptr); + } DocumentUndo::setUndoSensitive(document, saved); } @@ -616,15 +584,18 @@ static void spdc_check_for_and_apply_waiting_LPE(FreehandBase *dc, SPItem *item, if (shape_applied) { // apply original stroke color as fill and unset stroke; then return - SPCSSAttr *css = sp_repr_css_attr_new(); - if (!strcmp(cfill, "none")) { - sp_repr_css_set_property (css, "fill", cstroke); - } else { - sp_repr_css_set_property (css, "fill", cfill); + //we need to take account on ellipse_not PowerStroke: + if (previous_shape_type == ELLIPSE) { + SPCSSAttr *css = sp_repr_css_attr_new(); + if (!strcmp(cfill, "none")) { + sp_repr_css_set_property (css, "fill", cstroke); + } else { + sp_repr_css_set_property (css, "fill", cfill); + } + sp_repr_css_set_property (css, "stroke", "none"); + sp_desktop_apply_css_recursive(dc->white_item, css, true); + sp_repr_css_attr_unref(css); } - sp_repr_css_set_property (css, "stroke", "none"); - sp_desktop_apply_css_recursive(dc->white_item, css, true); - sp_repr_css_attr_unref(css); return; } if (dc->waiting_LPE_type != INVALID_LPE) { @@ -926,7 +897,11 @@ static void spdc_flush_white(FreehandBase *dc, SPCurve *gc) item->updateRepr(); item->doWriteTransform(item->transform, nullptr, true); spdc_check_for_and_apply_waiting_LPE(dc, item, c.get(), false); - if(previous_shape_type == BEND_CLIPBOARD){ + if (previous_shape_type == TRIANGLE_IN || previous_shape_type == TRIANGLE_OUT) { + dc->selection->set(repr); + SPLPEItem *lpeitem = dynamic_cast(item); + sp_lpe_item_onload_patheffect(lpeitem); + } else if(previous_shape_type == BEND_CLIPBOARD){ repr->parent()->removeChild(repr); } else { dc->selection->set(repr); diff --git a/testfiles/rendering_tests/test-powerstroke-join.svg b/testfiles/rendering_tests/test-powerstroke-join.svg index 2c05fb39788d25fb7c56f61b163ed985e5b47ce4..cde3940913f9c11202b518459d2c9b39cd357399 100644 --- a/testfiles/rendering_tests/test-powerstroke-join.svg +++ b/testfiles/rendering_tests/test-powerstroke-join.svg @@ -2,5 +2,5 @@ - +