From 357f47ffca9c573f260649ca8b502d9215becf30 Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Sun, 6 Sep 2020 13:35:25 +0200 Subject: [PATCH 1/4] Martin and me are recording in [BBB instance](https://digimedia1.r2.enst.fr/b/vec-grt-cyu) of Mc university a session of live coding. First day is on 2020-08-23, next week 5 September [9:00 UTC](https://www.timeanddate.com/worldclock/fixedtime.html?msg=Inkscape+Live+Coding+%28Power+Stroke+%40doctormo+%26+%40jabiertxof%29&iso=20200906T09&p1=%3A) are the continuation. The main problem is powerstoke items are "visualy" a stroke but "realy" are a SVG filled path, so wen we apply styles to it we do the wrong applying We have in diferent LPE a new feature to allow extra elements linked to main LPE item (meassure segments for example) We take this feature and use to link fill based properties to LPE item and stroke based properties to the linked one. The linked (stroke) item is unselectable so we have only the path of fill and the knots See this for more info: https://gitlab.com/inkscape/inkscape/-/merge_requests/2241 --- src/live_effects/effect.cpp | 1 + src/live_effects/lpe-powerstroke.cpp | 148 ++++++++++++++++++++++++--- src/live_effects/lpe-powerstroke.h | 5 + src/ui/tools/freehand-base.cpp | 55 +++------- 4 files changed, 154 insertions(+), 55 deletions(-) diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index 4863a62a19..ec2d9ab4ad 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -1354,6 +1354,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) diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp index ee40717648..41d5087802 100644 --- a/src/live_effects/lpe-powerstroke.cpp +++ b/src/live_effects/lpe-powerstroke.cpp @@ -176,7 +176,7 @@ LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) : 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); @@ -192,11 +192,18 @@ LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) : has_recursion = false; } -LPEPowerStroke::~LPEPowerStroke() = default; +LPEPowerStroke::~LPEPowerStroke() +{ + modified_connection.disconnect(); +}; void LPEPowerStroke::doBeforeEffect(SPLPEItem const *lpeItem) { + SPObject *obj = dynamic_cast(sp_lpe_item); + if (is_load && obj) { + modified_connection = obj->connectModified(sigc::mem_fun(*this, &LPEPowerStroke::modified)); + } offset_points.set_scale_width(scale_width); if (has_recursion) { has_recursion = false; @@ -204,9 +211,127 @@ 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; + } + //we use the LPE object (defs defined) id to construct the unique id for the linked item + //we dont use sp_lpe_item (Current LPE Item) id because it can conflict + Glib::ustring lpobjid = this->lpeobj->getId(); + Glib::ustring strokeid = lpobjid + "_stroke"; + //We dalay to later style + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + SPObject *elemref = nullptr; + //here we set the stroke + gchar *strokestr = sp_svg_write_path(strokepv); + //Here we find if the element of the strokeid exist or not + if ((elemref = document->getObjectById(strokeid.c_str()))) { + Inkscape::XML::Node *stroke = elemref->getRepr(); + stroke->setAttribute("d", strokestr); + //we also lock the item to not be selectable + stroke->setAttribute("sodipodi:insensitive", "true"); + } else { + Inkscape::XML::Node *stroke = xml_doc->createElement("svg:path"); + stroke->setAttribute("id", strokeid.c_str()); + stroke->setAttribute("d", strokestr); + stroke->setAttribute("sodipodi:insensitive", "true"); + elemref = SP_OBJECT(sp_lpe_item->parent->appendChildRepr(stroke)); + Inkscape::GC::release(stroke); + } + //we use the ID of the new element to add to extra items list + items.push_back(strokeid); +} + +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::modified(SPObject *obj, guint flags) +{ + // Now we are going to link styles + // this function is executed each time the item style is changed + if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) { //style changed + //get the fill item + SPDocument *document = getSPDoc(); + if (!document || !sp_lpe_item || !sp_lpe_item->getId()) { + return; + } + Glib::ustring lpobjid = this->lpeobj->getId(); + Glib::ustring strokeid = lpobjid + "_stroke"; + SPObject * elemref = document->getObjectById(strokeid); + + 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()); + 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 + item_a->moveTo(item_b, false); + } else { + // Fill, then stroke (normal) + item_b->moveTo(item_a, false); + } + } + + } +} + +//now adding common to handle external items +void +LPEPowerStroke::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/) +{ + processObjects(LPE_VISIBILITY); +} + +void +LPEPowerStroke::doOnRemove (SPLPEItem const* /*lpeitem*/) +{ + //set "keep paths" hook on sp-lpe-item.cpp + if (keep_paths) { + processObjects(LPE_TO_OBJECTS); + items.clear(); + return; + } + processObjects(LPE_ERASE); + + //we see later how to handle remove + SPShape *shape = dynamic_cast(sp_lpe_item); + if (shape) { + lpe_shape_revert_stroke_and_fill(shape, offset_points.median_width()*2); + } +} + +void LPEPowerStroke::applyStyle(SPLPEItem */*lpeitem*/) { - lpe_shape_convert_stroke_and_fill(SP_SHAPE(lpeitem)); + SPObject *object = dynamic_cast(sp_lpe_item); + if (object) { + modified(object, SP_OBJECT_STYLE_MODIFIED_FLAG); + } } void @@ -244,6 +369,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 +377,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) { @@ -771,7 +890,12 @@ LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in) 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*/) diff --git a/src/live_effects/lpe-powerstroke.h b/src/live_effects/lpe-powerstroke.h index d88352d5f0..afc3bcc5dd 100644 --- a/src/live_effects/lpe-powerstroke.h +++ b/src/live_effects/lpe-powerstroke.h @@ -44,8 +44,12 @@ public: void doOnApply(SPLPEItem const* lpeitem) override; void doOnRemove(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 modified(SPObject *obj, guint flags); + void upgradeLegacy(); // methods called by path-manipulator upon edits void adjustForNewPath(Geom::PathVector const & path_in); @@ -61,6 +65,7 @@ private: ScalarParam miter_limit; EnumParam end_linecap_type; size_t recusion_limit; + sigc::connection modified_connection; bool has_recursion; }; diff --git a/src/ui/tools/freehand-base.cpp b/src/ui/tools/freehand-base.cpp index 501a30695d..51e608c351 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); @@ -616,15 +582,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) { -- GitLab From 262c8837b214a19dc4151dc077c5e6af855a14dc Mon Sep 17 00:00:00 2001 From: Martin Owens Date: Sat, 24 Oct 2020 18:19:04 -0400 Subject: [PATCH 2/4] Fix style for shape based power strokes --- src/ui/tools/freehand-base.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ui/tools/freehand-base.cpp b/src/ui/tools/freehand-base.cpp index 51e608c351..45becc0bde 100644 --- a/src/ui/tools/freehand-base.cpp +++ b/src/ui/tools/freehand-base.cpp @@ -286,6 +286,9 @@ static void spdc_apply_powerstroke_shape(std::vector points, Freeha lpe->getRepr()->setAttribute("miter_limit", "4"); lpe->getRepr()->setAttribute("scale_width", "1"); lpe->getRepr()->setAttribute("linejoin_type", "extrp_arc"); + + sp_desktop_apply_style_tool(desktop, item->getRepr(), tool_name(dc), false); + static_cast(lpe)->applyStyle(nullptr); DocumentUndo::setUndoSensitive(document, saved); } -- GitLab From f3b51f89c31e48891d303e099056afa108bafd73 Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Sun, 25 Oct 2020 00:25:45 +0200 Subject: [PATCH 3/4] Formating Group Coding --- src/ui/tools/freehand-base.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/ui/tools/freehand-base.cpp b/src/ui/tools/freehand-base.cpp index 45becc0bde..2da80966cd 100644 --- a/src/ui/tools/freehand-base.cpp +++ b/src/ui/tools/freehand-base.cpp @@ -277,6 +277,7 @@ static void spdc_apply_powerstroke_shape(std::vector points, Freeha static_cast(lpe)->offset_points.param_set_and_write_new_value(points); // write powerstroke parameters: + sp_desktop_apply_style_tool(desktop, item->getRepr(), tool_name(dc), false); lpe->getRepr()->setAttribute("start_linecap_type", "zerowidth"); lpe->getRepr()->setAttribute("end_linecap_type", "zerowidth"); lpe->getRepr()->setAttribute("sort_points", "true"); @@ -286,9 +287,7 @@ static void spdc_apply_powerstroke_shape(std::vector points, Freeha lpe->getRepr()->setAttribute("miter_limit", "4"); lpe->getRepr()->setAttribute("scale_width", "1"); lpe->getRepr()->setAttribute("linejoin_type", "extrp_arc"); - - sp_desktop_apply_style_tool(desktop, item->getRepr(), tool_name(dc), false); - static_cast(lpe)->applyStyle(nullptr); + lpe->applyStyle(nullptr); DocumentUndo::setUndoSensitive(document, saved); } -- GitLab From b8072ef19e844e280ca2c714a13fd6f52138bb39 Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Sun, 22 Nov 2020 22:12:48 +0100 Subject: [PATCH 4/4] Testing phase --- src/display/drawing-item.cpp | 29 +- src/display/drawing-item.h | 3 + src/display/drawing-shape.h | 2 +- src/file.cpp | 4 + src/live_effects/effect.cpp | 48 ++- src/live_effects/effect.h | 29 +- src/live_effects/lpe-bendpath.cpp | 9 +- src/live_effects/lpe-bool.cpp | 4 +- src/live_effects/lpe-copy_rotate.cpp | 85 ++++- src/live_effects/lpe-copy_rotate.h | 2 + src/live_effects/lpe-envelope.cpp | 6 +- src/live_effects/lpe-interpolate.cpp | 6 +- src/live_effects/lpe-jointype.cpp | 3 +- src/live_effects/lpe-mirror_symmetry.cpp | 52 ++- src/live_effects/lpe-mirror_symmetry.h | 2 + src/live_effects/lpe-offset.cpp | 5 +- src/live_effects/lpe-patternalongpath.cpp | 5 +- src/live_effects/lpe-perspective-envelope.cpp | 6 +- src/live_effects/lpe-powerstroke.cpp | 356 ++++++++++++++---- src/live_effects/lpe-powerstroke.h | 14 + src/live_effects/lpe-taperstroke.cpp | 3 +- src/live_effects/lpe-test-doEffect-stack.cpp | 2 +- src/live_effects/lpe-transform_2pts.cpp | 6 +- src/live_effects/lpegroupbbox.cpp | 3 + src/object/sp-item-group.cpp | 13 +- src/object/sp-lpe-item.cpp | 108 +++++- src/object/sp-lpe-item.h | 17 +- src/object/sp-shape.cpp | 15 +- src/ui/toolbar/pencil-toolbar.cpp | 4 +- src/ui/tools/freehand-base.cpp | 36 +- .../rendering_tests/test-powerstroke-join.svg | 2 +- 31 files changed, 740 insertions(+), 139 deletions(-) diff --git a/src/display/drawing-item.cpp b/src/display/drawing-item.cpp index 2c6848f6c2..a06ab30163 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 b1f88a017e..917b2d0702 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 319e0e12ce..fcad070984 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 811e2564b6..7e1809e160 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 ec2d9ab4ad..2399f3d3af 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); @@ -1364,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 2b68661347..b6524f821e 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 f4248e5322..07833c02f6 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 09b0ccf02d..73b7873af5 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 3cfa27024f..b087a7f027 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 b97beefc06..45db264777 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 b0ff8a4956..0f17e9ddce 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 3da8726f26..1b860e121c 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 4a8b6261c5..5249ac9a39 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 9edbd21f50..57842112b4 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 9a87ab34a4..c70f7651f0 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 530b97adf2..5b76c8e9d2 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 716ecc6cde..b9d8090376 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 5321ae63d1..d7baaab736 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 41d5087802..01cdf09517 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,7 +170,9 @@ 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); @@ -190,19 +193,130 @@ LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) : scale_width.param_set_digits(4); recusion_limit = 0; has_recursion = false; + displays = 0; + transforming = false; } LPEPowerStroke::~LPEPowerStroke() { - modified_connection.disconnect(); + 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); +} + +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) { - SPObject *obj = dynamic_cast(sp_lpe_item); - if (is_load && obj) { - modified_connection = obj->connectModified(sigc::mem_fun(*this, &LPEPowerStroke::modified)); + + 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) { @@ -218,31 +332,55 @@ LPEPowerStroke::createStroke(Geom::PathVector strokepv) if (!document || !sp_lpe_item|| !sp_lpe_item->getId()) { return; } - //we use the LPE object (defs defined) id to construct the unique id for the linked item - //we dont use sp_lpe_item (Current LPE Item) id because it can conflict - Glib::ustring lpobjid = this->lpeobj->getId(); - Glib::ustring strokeid = lpobjid + "_stroke"; - //We dalay to later style - Inkscape::XML::Document *xml_doc = document->getReprDoc(); - SPObject *elemref = nullptr; - //here we set the stroke - gchar *strokestr = sp_svg_write_path(strokepv); + 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 - if ((elemref = document->getObjectById(strokeid.c_str()))) { - Inkscape::XML::Node *stroke = elemref->getRepr(); - stroke->setAttribute("d", strokestr); - //we also lock the item to not be selectable - stroke->setAttribute("sodipodi:insensitive", "true"); + 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 { - Inkscape::XML::Node *stroke = xml_doc->createElement("svg:path"); - stroke->setAttribute("id", strokeid.c_str()); - stroke->setAttribute("d", strokestr); + 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"); - elemref = SP_OBJECT(sp_lpe_item->parent->appendChildRepr(stroke)); - Inkscape::GC::release(stroke); } - //we use the ID of the new element to add to extra items list - items.push_back(strokeid); + inicialize(); } void @@ -256,55 +394,108 @@ LPEPowerStroke::upgradeLegacy() { sp_lpe_item->updateRepr(); } + + +void +LPEPowerStroke::satellite_modified(SPObject *obj, guint flags) +{ + if ((flags & SP_OBJECT_MODIFIED_FLAG) && !transforming) { + //inicialize(); + } +} + + void -LPEPowerStroke::modified(SPObject *obj, guint flags) +LPEPowerStroke::parent_modified(SPObject *obj, guint flags) { - // Now we are going to link styles - // this function is executed each time the item style is changed - if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) { //style changed - //get the fill item + // TODO:not use flags directy + if (flags == 3) { SPDocument *document = getSPDoc(); - if (!document || !sp_lpe_item || !sp_lpe_item->getId()) { + sp_lpe_item = dynamic_cast(*getLPEObj()->hrefList.begin()); + if (!sp_lpe_item || !document) { return; } - Glib::ustring lpobjid = this->lpeobj->getId(); - Glib::ustring strokeid = lpobjid + "_stroke"; - SPObject * elemref = document->getObjectById(strokeid); + 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); + } + } +} + - if(elemref) { +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 - item_a->moveTo(item_b, false); - } else { - // Fill, then stroke (normal) - item_b->moveTo(item_a, false); + 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); } @@ -312,18 +503,29 @@ void LPEPowerStroke::doOnRemove (SPLPEItem const* /*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; } - processObjects(LPE_ERASE); - - //we see later how to handle remove SPShape *shape = dynamic_cast(sp_lpe_item); if (shape) { - lpe_shape_revert_stroke_and_fill(shape, offset_points.median_width()*2); + 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*/) @@ -541,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) { @@ -888,9 +1090,7 @@ LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in) } if (path_out.empty()) { return path_in; - // doEffect_path (path_in); } - createStroke(path_out); if (is_load && lpeversion.param_getSVGValue() < "1.1") { upgradeLegacy(); @@ -900,7 +1100,35 @@ LPEPowerStroke::doEffect_path (Geom::PathVector const & 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 afc3bcc5dd..e70ec8b83c 100644 --- a/src/live_effects/lpe-powerstroke.h +++ b/src/live_effects/lpe-powerstroke.h @@ -43,12 +43,19 @@ 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); @@ -56,6 +63,7 @@ public: PowerStrokePointArrayParam offset_points; BoolParam not_jump; private: + static int _enableforked(gpointer data); BoolParam sort_points; EnumParam interpolator_type; ScalarParam interpolator_beta; @@ -64,9 +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 13f1c54c31..90c9826e6f 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 30b93881c8..4a22cfb2be 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 99b081b659..94415066d0 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 863b08cc13..f6f27a6ee8 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 64c69cb6be..7a12484b34 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 4dc4f07ff2..6ddc6001b2 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 95bee2dcf4..185f063674 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 1d5df5ea37..c4235271e0 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 06ad319df1..7ec51fc19e 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 2da80966cd..e03507eee7 100644 --- a/src/ui/tools/freehand-base.cpp +++ b/src/ui/tools/freehand-base.cpp @@ -272,22 +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: - sp_desktop_apply_style_tool(desktop, item->getRepr(), tool_name(dc), false); - 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); + 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); } @@ -897,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 2c05fb3978..cde3940913 100644 --- a/testfiles/rendering_tests/test-powerstroke-join.svg +++ b/testfiles/rendering_tests/test-powerstroke-join.svg @@ -2,5 +2,5 @@ - + -- GitLab