From c8c99676986ab27ce46c35b779c4b30d3a644cd7 Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Sun, 1 Oct 2023 10:52:05 +0200 Subject: [PATCH 1/2] Add LPE flatten --- src/helper/geom-pathstroke.cpp | 13 +- src/helper/geom-pathstroke.h | 6 + src/live_effects/CMakeLists.txt | 2 + src/live_effects/effect-enum.h | 1 + src/live_effects/effect.cpp | 19 +++ src/live_effects/lpe-flatten.cpp | 186 +++++++++++++++++++++++++++++ src/live_effects/lpe-flatten.h | 59 +++++++++ src/live_effects/lpe-offset.cpp | 21 +--- src/path/path-boolop.cpp | 12 +- src/path/path-boolop.h | 4 +- src/ui/tools/booleans-subitems.cpp | 5 - 11 files changed, 292 insertions(+), 36 deletions(-) create mode 100644 src/live_effects/lpe-flatten.cpp create mode 100644 src/live_effects/lpe-flatten.h diff --git a/src/helper/geom-pathstroke.cpp b/src/helper/geom-pathstroke.cpp index 2472c20e71..707b5bbd26 100644 --- a/src/helper/geom-pathstroke.cpp +++ b/src/helper/geom-pathstroke.cpp @@ -992,6 +992,11 @@ void peak_cap(Geom::PathBuilder& res, Geom::Path const& with_dir, Geom::Path con namespace Inkscape { +FillRule sp_to_livarot(SPWindRule fillrule) +{ + return fillrule == SP_WIND_RULE_NONZERO ? fill_nonZero : fill_oddEven; +} + Geom::PathVector outline( Geom::Path const& input, double width, @@ -1262,7 +1267,7 @@ do_offset(Geom::PathVector const & path_in // flatten order the direcions and remove self intersections // we use user fill rule to match original view // after flatten all elements has the same direction in his widding - sp_flatten(closed_pathv, fillrule); + sp_flatten(closed_pathv, fillrule, false); if (to_offset < 0) { Geom::OptRect bbox = closed_pathv.boundsFast(); if (bbox) { @@ -1292,7 +1297,7 @@ do_offset(Geom::PathVector const & path_in } } else { auto with_dir_pv = Geom::PathVector(with_dir); - sp_flatten(with_dir_pv, fill_positive); + sp_flatten(with_dir_pv, fill_positive, false); for (auto path : with_dir_pv) { auto bbox = path.boundsFast(); if (bbox) { @@ -1314,10 +1319,10 @@ do_offset(Geom::PathVector const & path_in if (to_offset > 0) { outline.insert(outline.end(), outline_tmp.begin(), outline_tmp.end()); // this make a union propely without calling boolops - sp_flatten(outline, fill_positive); + sp_flatten(outline, fill_positive, false); } else { // this flatten in a fill_positive way that allow us erase it from the otiginal outline alwais (smaller) - sp_flatten(outline_tmp, fill_positive); + sp_flatten(outline_tmp, fill_positive, false); // this can produce small satellites that become removed after new offset impletation work in 1.4 outline = sp_pathvector_boolop(outline_tmp, outline, bool_op_diff, fill_nonZero, fill_nonZero, false); } diff --git a/src/helper/geom-pathstroke.h b/src/helper/geom-pathstroke.h index ba252583d7..7a8937493b 100644 --- a/src/helper/geom-pathstroke.h +++ b/src/helper/geom-pathstroke.h @@ -17,6 +17,10 @@ #include <2geom/pathvector.h> #include "path/path-boolop.h" +// fill rule conversion ¿better place? +#include "livarot/LivarotDefs.h" +#include "style-enums.h" + namespace Inkscape { enum LineJoinType { @@ -37,6 +41,8 @@ enum LineCapType { BUTT_PEAK, // This is not a line ending supported by the SVG standard. }; +FillRule sp_to_livarot(SPWindRule fillrule); + /** * Strokes the path given by @a input. * Joins may behave oddly if the width is negative. diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt index 89703479cc..982124af0d 100644 --- a/src/live_effects/CMakeLists.txt +++ b/src/live_effects/CMakeLists.txt @@ -24,6 +24,7 @@ set(live_effects_SRC lpe-fill-between-many.cpp lpe-fill-between-strokes.cpp lpe-fillet-chamfer.cpp + lpe-flatten.cpp lpe-gears.cpp lpe-interpolate.cpp lpe-interpolate_points.cpp @@ -121,6 +122,7 @@ set(live_effects_SRC lpe-fill-between-many.h lpe-fill-between-strokes.h lpe-fillet-chamfer.h + lpe-flatten.h lpe-gears.h lpe-interpolate.h lpe-interpolate_points.h diff --git a/src/live_effects/effect-enum.h b/src/live_effects/effect-enum.h index 0ae38eb414..48ca80b67b 100644 --- a/src/live_effects/effect-enum.h +++ b/src/live_effects/effect-enum.h @@ -59,6 +59,7 @@ enum EffectType { BOOL_OP, SLICE, TILING, + FLATTEN, // PUT NEW LPE BEFORE EXPERIMENTAL IN THE SAME ORDER AS IN effect.cpp // Visible Experimental LPE's ANGLE_BISECTOR, diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index fe31b7daa4..38eef4e4cb 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -42,6 +42,7 @@ #include "live_effects/lpe-fill-between-many.h" #include "live_effects/lpe-fill-between-strokes.h" #include "live_effects/lpe-fillet-chamfer.h" +#include "live_effects/lpe-flatten.h" #include "live_effects/lpe-gears.h" #include "live_effects/lpe-interpolate.h" #include "live_effects/lpe-interpolate_points.h" @@ -679,6 +680,21 @@ const EnumEffectData LPETypeData[] = { false ,//on_text false ,//experimental }, + /* 1.3 */ + { + FLATTEN, + NC_("path effect", "Flatten") ,//label + "flatten" ,//key + "flatten" ,//icon + N_("Flatten the item, join all same colors into the same item or child") ,//description + LPECategory::EditTools ,//category + true ,//on_path + true ,//on_shape + true ,//on_group + false ,//on_image + false ,//on_text + false ,//experimental + }, // VISIBLE experimental LPEs { ANGLE_BISECTOR, @@ -1101,6 +1117,9 @@ Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj) case TILING: neweffect = static_cast ( new LPETiling(lpeobj) ); break; + case FLATTEN: + neweffect = static_cast ( new LPEFlatten(lpeobj) ); + break; default: g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr); neweffect = nullptr; diff --git a/src/live_effects/lpe-flatten.cpp b/src/live_effects/lpe-flatten.cpp new file mode 100644 index 0000000000..c5411923f1 --- /dev/null +++ b/src/live_effects/lpe-flatten.cpp @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** \file + * LPE implementation + */ +/* + * Authors: + * Maximilian Albert + * Jabiertxo Arraiza + * + * Copyright (C) Johan Engelen 2007 + * Copyright (C) Maximilian Albert 2008 + * Copyright (C) Jabierto Arraiza 2015 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "lpe-flatten.h" +#include "helper/geom.h" +#include "path/path-boolop.h" +#include "helper/geom-pathstroke.h" +#include "style.h" +#include "object/sp-item-group.h" +#include "object/sp-shape.h" +#include "style.h" + +// TODO due to internal breakage in glibmm headers, this must be last: +#include + +namespace Inkscape { +namespace LivePathEffect { + +static const Util::EnumData FillTypeData[] = { + { fill_oddEven, N_("even-odd"), "oddeven" }, + { fill_nonZero, N_("non-zero"), "nonzero" }, + { fill_positive, N_("positive"), "positive" }, + { fill_justDont, N_("take from object"), "from-curve" } +}; + +static const Util::EnumDataConverter FillTypeConverter(FillTypeData, sizeof(FillTypeData) / sizeof(*FillTypeData)); + + +LPEFlatten::LPEFlatten(LivePathEffectObject *lpeobject) : + Effect(lpeobject), + fillrule(_("Fill rule:"), _("Fill rule type (winding mode) for this item"), "fillrule", FillTypeConverter, &wr, this, fill_oddEven), + invert(_("Invert positive"), _("Invert fill"), "invert", &wr, this, false) +{ + show_orig_path = true; + registerParameter(&fillrule); + registerParameter(&invert); + apply_to_clippath_and_mask = true; +} + +void +LPEFlatten::addCanvasIndicators(SPLPEItem const *lpeitem, std::vector &hp_vec) +{ + hp_vec.push_back(helper_path); +} + +void +LPEFlatten::doBeforeEffect(SPLPEItem const *lpeitem) +{ + helper_path.clear(); +} + +Geom::PathVector +LPEFlatten::doEffect_path(Geom::PathVector const & path_in) +{ + Geom::PathVector open_pathv; + Geom::PathVector orig_pathv = pathv_to_linear_and_cubic_beziers(path_in); + Geom::PathVector pathv_out; // return path + helper_path.insert(helper_path.end(), orig_pathv.begin(), orig_pathv.end()); + // Store separated open/closed paths + for (auto &i : orig_pathv) { + // this improve offset in near closed paths + if (Geom::are_near(i.initialPoint(), i.finalPoint())) { + i.close(true); + } + if (i.closed()) { + pathv_out.push_back(i); + } else { + open_pathv.push_back(i); + } + } + sp_flatten(pathv_out, fillrule, invert); + pathv_out.insert(pathv_out.end(), open_pathv.begin(), open_pathv.end()); + return pathv_out; +} + +void sp_flatten_group(SPGroup * group, SPItem * item) { + auto prev_shape = cast(item); + //auto prev_group = cast(item); + Geom::Affine prev_trans = item->i2doc_affine(); + for (auto &child2 : group->item_list()) { + auto groupchild = cast(child2); + auto shape = cast(child2); + if (groupchild) { + sp_flatten_group(groupchild, item); + } else if (prev_shape && shape != prev_shape) { + auto prev_curve = prev_shape->curve(); + auto curve = shape->curve(); + Geom::Affine trans = shape->i2doc_affine(); + if (curve && prev_curve) { + FillRule prev_fillrule = Inkscape::sp_to_livarot(prev_shape->style->fill_rule.value); + FillRule fillrule = Inkscape::sp_to_livarot(shape->style->fill_rule.value); + auto pv = curve->get_pathvector(); + auto ppv = prev_curve->get_pathvector(); + pv *= trans; + ppv *= prev_trans; + pv = sp_pathvector_boolop(ppv, pv, bool_op_diff, fillrule, prev_fillrule, true); + auto c = new SPCurve(); + pv *= trans.inverse(); + c->set_pathvector(pv); + shape->setCurve(c); + } + } + } +} + +void sp_join(SPGroup * group, SPItem * item) { + auto prev_style = item->getAttribute("style"); + auto prev_shape = cast(item); + //auto prev_group = cast(item); + Geom::Affine prev_trans = item->i2doc_affine(); + for (auto &child2 : group->item_list()) { + auto groupchild = cast(child2); + auto shape = cast(child2); + if (groupchild) { + sp_join(groupchild, item); + } else if (prev_shape && shape != prev_shape) { + auto prev_curve = prev_shape->curve(); + auto style = shape->getAttribute("style"); + Geom::Affine trans = shape->i2doc_affine(); + if (!g_strcmp0(style, prev_style)) { + auto curve = shape->curve(); + if (curve && prev_curve) { + FillRule prev_fillrule = Inkscape::sp_to_livarot(prev_shape->style->fill_rule.value); + FillRule fillrule = Inkscape::sp_to_livarot(shape->style->fill_rule.value); + auto pv = curve->get_pathvector(); + auto ppv = prev_curve->get_pathvector(); + pv *= trans; + ppv *= prev_trans; + pv = sp_pathvector_boolop(pv, ppv, bool_op_union, fillrule, prev_fillrule, false); + auto c = new SPCurve(); + pv *= prev_trans.inverse(); + c->set_pathvector(pv); + prev_shape->setCurve(c); + shape->setCurve(SPCurve()); + } + } + } + } +} + +void +LPEFlatten::doAfterEffect (SPLPEItem const* lpeitem, SPCurve *curve) +{ + auto group = cast(sp_lpe_item); + if (group) { + for (auto &child : group->item_list()) { + auto prev_shape = cast(child); + if (prev_shape) { + sp_flatten_group(group, prev_shape); + } + } + for (auto &child : group->item_list()) { + auto prev_shape = cast(child); + if (prev_shape) { + //sp_join(group, prev_shape); + } + } + } +} + +} //namespace LivePathEffect +} /* namespace Inkscape */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-flattens:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/live_effects/lpe-flatten.h b/src/live_effects/lpe-flatten.h new file mode 100644 index 0000000000..5541b965ac --- /dev/null +++ b/src/live_effects/lpe-flatten.h @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#ifndef INKSCAPE_LPE_FLATTEN_H +#define INKSCAPE_LPE_FLATTEN_H + +/** \file + * LPE implementation, see lpe-flatten.cpp. + */ + +/* + * Authors: + * Maximilian Albert + * Jabiertxo Arraiza + * + * Copyright (C) Johan Engelen 2007 + * Copyright (C) Maximilian Albert 2008 + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "live_effects/effect.h" +#include "live_effects/parameter/enum.h" +#include "live_effects/parameter/bool.h" +#include "live_effects/parameter/parameter.h" +#include "livarot/Path.h" +#include "livarot/Shape.h" + +namespace Inkscape { +namespace LivePathEffect { + +class LPEFlatten : public Effect { +public: + LPEFlatten(LivePathEffectObject *lpeobject); + Geom::PathVector doEffect_path (Geom::PathVector const &path_in) override; + void doBeforeEffect(SPLPEItem const *lpeitem) override; + void doAfterEffect(SPLPEItem const* lpeitem, SPCurve *curve) override; + void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector &hp_vec) override; +private: + EnumParam fillrule; + BoolParam invert; + Geom::PathVector helper_path; + LPEFlatten(const LPEFlatten&); + LPEFlatten& operator=(const LPEFlatten&); +}; + +} //namespace LivePathEffect +} //namespace Inkscape + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-flattens:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/live_effects/lpe-offset.cpp b/src/live_effects/lpe-offset.cpp index a8d28e35a5..b5088be5f4 100644 --- a/src/live_effects/lpe-offset.cpp +++ b/src/live_effects/lpe-offset.cpp @@ -135,26 +135,13 @@ LPEOffset::doOnApply(SPLPEItem const* lpeitem) lpeversion.param_setValue("1.3", true); } -Glib::ustring -sp_get_fill_rule(SPObject *obj) { - SPCSSAttr *css; - css = sp_repr_css_attr (obj->getRepr() , "style"); - Glib::ustring val = sp_repr_css_property (css, "fill-rule", ""); - sp_repr_css_attr_unref(css); - return val; -} - void LPEOffset::modified(SPObject *obj, guint flags) { // we check for style changes and apply LPE to get appropiate fill rule on change if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG && obj) { // Get the used fillrule - Glib::ustring fr = sp_get_fill_rule(obj); - FillRule fillrule_chan = fill_nonZero; - if (fr == "evenodd") { - fillrule_chan = fill_oddEven; - } + FillRule fillrule_chan = Inkscape::sp_to_livarot(obj->style->fill_rule.value); if (fillrule != fillrule_chan && sp_lpe_item) { sp_lpe_item_update_patheffect(sp_lpe_item, true, true); } @@ -294,11 +281,7 @@ LPEOffset::doEffect_path(Geom::PathVector const & path_in) return path_in; } // Get the used fillrule - Glib::ustring fr = sp_get_fill_rule(item); - fillrule = fill_nonZero; - if (fr == "evenodd") { - fillrule = fill_oddEven; - } + fillrule = Inkscape::sp_to_livarot(item->style->fill_rule.value); // ouline operations faster on live editing knot, on release it, get updated to -1 // todo study remove/change value. Use text paths to test if is needed double tolerance = -1; diff --git a/src/path/path-boolop.cpp b/src/path/path-boolop.cpp index 247f0349d0..d0f16bb98f 100644 --- a/src/path/path-boolop.cpp +++ b/src/path/path-boolop.cpp @@ -105,13 +105,13 @@ static double get_threshold(Geom::PathVector const &pathv) * @param fill_rule The fill rule with which to flatten the path. * @param close_if_needed If the path is not closed, whether to add a closing segment. */ -static Shape make_shape(Path &path, int path_id = -1, FillRule fill_rule = fill_nonZero, bool close_if_needed = true) +static Shape make_shape(Path &path, int path_id = -1, FillRule fill_rule = fill_nonZero, bool close_if_needed = true, bool invert = false) { Shape result; Shape tmp; path.Fill(&tmp, path_id, false, close_if_needed); - result.ConvertToShape(&tmp, fill_rule); + result.ConvertToShape(&tmp, fill_rule, invert); return result; } @@ -142,10 +142,10 @@ static bool is_line(Path const &path) * Flattening */ -Geom::PathVector flattened(Geom::PathVector const &pathv, FillRule fill_rule) +Geom::PathVector flattened(Geom::PathVector const &pathv, FillRule fill_rule, bool invert) { auto path = make_path(pathv); - auto shape = make_shape(path, 0, fill_rule); + auto shape = make_shape(path, 0, fill_rule, true, invert); Path res; shape.ConvertToForme(&res, 1, std::begin({ &path })); @@ -153,9 +153,9 @@ Geom::PathVector flattened(Geom::PathVector const &pathv, FillRule fill_rule) return res.MakePathVector(); } -void sp_flatten(Geom::PathVector &pathv, FillRule fill_rule) +void sp_flatten(Geom::PathVector &pathv, FillRule fill_rule, bool invert) { - pathv = flattened(pathv, fill_rule); + pathv = flattened(pathv, fill_rule, invert); } /* diff --git a/src/path/path-boolop.h b/src/path/path-boolop.h index 1b283d3849..5f64339e52 100644 --- a/src/path/path-boolop.h +++ b/src/path/path-boolop.h @@ -17,8 +17,8 @@ #include "livarot/LivarotDefs.h" // FillRule, BooleanOp /// Flatten a pathvector according to the given fill rule. -Geom::PathVector flattened(Geom::PathVector const &pathv, FillRule fill_rule); -void sp_flatten(Geom::PathVector &pathv, FillRule fill_rule); +Geom::PathVector flattened(Geom::PathVector const &pathv, FillRule fill_rule, bool invert = false); +void sp_flatten(Geom::PathVector &pathv, FillRule fill_rule, bool invert = false); /// Cut a pathvector along a collection of lines into several smaller pathvectors. std::vector pathvector_cut(Geom::PathVector const &pathv, Geom::PathVector const &lines); diff --git a/src/ui/tools/booleans-subitems.cpp b/src/ui/tools/booleans-subitems.cpp index 98017f2e78..45b217a220 100644 --- a/src/ui/tools/booleans-subitems.cpp +++ b/src/ui/tools/booleans-subitems.cpp @@ -113,11 +113,6 @@ static void extract_pathvectors_recursive(SPItem *root, SPItem *item, Pathvector } } -static FillRule sp_to_livarot(SPWindRule fillrule) -{ - return fillrule == SP_WIND_RULE_NONZERO ? fill_nonZero : fill_oddEven; -} - /** * Take a list of items and fracture into a list of SubItems ready for * use inside the booleans interactive tool. -- GitLab From 1bd30068f8b7c5504ddf41315da8ff2028fad1c6 Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Mon, 2 Oct 2023 01:24:06 +0200 Subject: [PATCH 2/2] Add LPE flatten --- src/live_effects/lpe-flatten.cpp | 110 +++++++++++++++++-------------- 1 file changed, 61 insertions(+), 49 deletions(-) diff --git a/src/live_effects/lpe-flatten.cpp b/src/live_effects/lpe-flatten.cpp index c5411923f1..3b41bf1c55 100644 --- a/src/live_effects/lpe-flatten.cpp +++ b/src/live_effects/lpe-flatten.cpp @@ -90,48 +90,19 @@ void sp_flatten_group(SPGroup * group, SPItem * item) { auto prev_shape = cast(item); //auto prev_group = cast(item); Geom::Affine prev_trans = item->i2doc_affine(); - for (auto &child2 : group->item_list()) { - auto groupchild = cast(child2); - auto shape = cast(child2); - if (groupchild) { - sp_flatten_group(groupchild, item); - } else if (prev_shape && shape != prev_shape) { - auto prev_curve = prev_shape->curve(); - auto curve = shape->curve(); - Geom::Affine trans = shape->i2doc_affine(); - if (curve && prev_curve) { - FillRule prev_fillrule = Inkscape::sp_to_livarot(prev_shape->style->fill_rule.value); - FillRule fillrule = Inkscape::sp_to_livarot(shape->style->fill_rule.value); - auto pv = curve->get_pathvector(); - auto ppv = prev_curve->get_pathvector(); - pv *= trans; - ppv *= prev_trans; - pv = sp_pathvector_boolop(ppv, pv, bool_op_diff, fillrule, prev_fillrule, true); - auto c = new SPCurve(); - pv *= trans.inverse(); - c->set_pathvector(pv); - shape->setCurve(c); - } - } - } -} - -void sp_join(SPGroup * group, SPItem * item) { - auto prev_style = item->getAttribute("style"); - auto prev_shape = cast(item); - //auto prev_group = cast(item); - Geom::Affine prev_trans = item->i2doc_affine(); - for (auto &child2 : group->item_list()) { - auto groupchild = cast(child2); - auto shape = cast(child2); - if (groupchild) { - sp_join(groupchild, item); - } else if (prev_shape && shape != prev_shape) { - auto prev_curve = prev_shape->curve(); - auto style = shape->getAttribute("style"); - Geom::Affine trans = shape->i2doc_affine(); - if (!g_strcmp0(style, prev_style)) { + size_t index = item->getPosition(); + auto itemlist = group->item_list(); + std::reverse(itemlist.begin(),itemlist.end()); + for (auto &child2 : itemlist) { + if (index > child2->getPosition()) { + auto groupchild = cast(child2); + auto shape = cast(child2); + if (groupchild) { + sp_flatten_group(groupchild, item); + } else if (prev_shape && shape) { + auto prev_curve = prev_shape->curve(); auto curve = shape->curve(); + Geom::Affine trans = shape->i2doc_affine(); if (curve && prev_curve) { FillRule prev_fillrule = Inkscape::sp_to_livarot(prev_shape->style->fill_rule.value); FillRule fillrule = Inkscape::sp_to_livarot(shape->style->fill_rule.value); @@ -139,35 +110,76 @@ void sp_join(SPGroup * group, SPItem * item) { auto ppv = prev_curve->get_pathvector(); pv *= trans; ppv *= prev_trans; - pv = sp_pathvector_boolop(pv, ppv, bool_op_union, fillrule, prev_fillrule, false); + pv = sp_pathvector_boolop(ppv, pv, bool_op_diff, fillrule, prev_fillrule); auto c = new SPCurve(); - pv *= prev_trans.inverse(); + pv *= trans.inverse(); c->set_pathvector(pv); - prev_shape->setCurve(c); - shape->setCurve(SPCurve()); + shape->setCurve(c); } } } } } +/* void sp_join(SPGroup * group, SPItem * item) { + auto prev_style = item->getAttribute("style"); + auto prev_shape = cast(item); + //auto prev_group = cast(item); + Geom::Affine prev_trans = item->i2doc_affine(); + size_t index = item->getPosition(); + auto itemlist = group->item_list(); + std::reverse(itemlist.begin(),itemlist.end()); + for (auto &child2 : itemlist) { + if (index > child2->getPosition()) { + auto groupchild = cast(child2); + auto shape = cast(child2); + if (groupchild) { + sp_join(groupchild, item); + } else if (prev_shape && shape != prev_shape) { + auto prev_curve = prev_shape->curve(); + auto style = shape->getAttribute("style"); + Geom::Affine trans = shape->i2doc_affine(); + if (!g_strcmp0(style, prev_style)) { + auto curve = shape->curve(); + if (curve && prev_curve) { + FillRule prev_fillrule = Inkscape::sp_to_livarot(prev_shape->style->fill_rule.value); + FillRule fillrule = Inkscape::sp_to_livarot(shape->style->fill_rule.value); + auto pv = curve->get_pathvector(); + auto ppv = prev_curve->get_pathvector(); + pv *= trans; + ppv *= prev_trans; + pv = sp_pathvector_boolop(pv, ppv, bool_op_union, fillrule, prev_fillrule); + auto c = new SPCurve(); + pv *= prev_trans.inverse(); + c->set_pathvector(pv); + prev_shape->setCurve(c); + shape->setCurve(SPCurve()); + } + } + } + } + } +} */ + void LPEFlatten::doAfterEffect (SPLPEItem const* lpeitem, SPCurve *curve) { auto group = cast(sp_lpe_item); if (group) { - for (auto &child : group->item_list()) { + auto itemlist = group->item_list(); + std::reverse(itemlist.begin(),itemlist.end()); + for (auto &child : itemlist) { auto prev_shape = cast(child); if (prev_shape) { sp_flatten_group(group, prev_shape); } } - for (auto &child : group->item_list()) { + /* for (auto &child : itemlist) { auto prev_shape = cast(child); if (prev_shape) { - //sp_join(group, prev_shape); + sp_join(group, prev_shape); } - } + } */ } } -- GitLab