summaryrefslogtreecommitdiffstats
path: root/src/live_effects/lpe-slice.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:24:48 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:24:48 +0000
commitcca66b9ec4e494c1d919bff0f71a820d8afab1fa (patch)
tree146f39ded1c938019e1ed42d30923c2ac9e86789 /src/live_effects/lpe-slice.cpp
parentInitial commit. (diff)
downloadinkscape-cca66b9ec4e494c1d919bff0f71a820d8afab1fa.tar.xz
inkscape-cca66b9ec4e494c1d919bff0f71a820d8afab1fa.zip
Adding upstream version 1.2.2.upstream/1.2.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/live_effects/lpe-slice.cpp')
-rw-r--r--src/live_effects/lpe-slice.cpp969
1 files changed, 969 insertions, 0 deletions
diff --git a/src/live_effects/lpe-slice.cpp b/src/live_effects/lpe-slice.cpp
new file mode 100644
index 0000000..50c7713
--- /dev/null
+++ b/src/live_effects/lpe-slice.cpp
@@ -0,0 +1,969 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** \file
+ * LPE <slice> implementation: slices a path with respect to a given line.
+ */
+/*
+ * Authors:
+ * Maximilian Albert
+ * Johan Engelen
+ * Abhishek Sharma
+ * Jabiertxof
+ *
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ * Copyright (C) Maximilin Albert 2008 <maximilian.albert@gmail.com>
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "live_effects/lpe-slice.h"
+
+#include <gtkmm.h>
+
+#include "2geom/affine.h"
+#include "2geom/path-intersection.h"
+#include "display/curve.h"
+#include "helper/geom.h"
+#include "live_effects/parameter/satellite-reference.h"
+#include "object/sp-defs.h"
+#include "object/sp-lpe-item.h"
+#include "object/sp-path.h"
+#include "object/sp-text.h"
+#include "path-chemistry.h"
+#include "path/path-boolop.h"
+#include "style.h"
+#include "svg/path-string.h"
+#include "svg/svg.h"
+#include "xml/sp-css-attr.h"
+
+// this is only to flatten nonzero fillrule
+#include "livarot/Path.h"
+#include "livarot/Shape.h"
+
+// TODO due to internal breakage in glibmm headers, this must be last:
+#include <glibmm/i18n.h>
+
+typedef FillRule FillRuleFlatten;
+
+namespace Inkscape {
+namespace LivePathEffect {
+LPESlice::LPESlice(LivePathEffectObject *lpeobject) :
+ Effect(lpeobject),
+ // do not change name of this parameter us used in oncommit
+ lpesatellites(_("lpesatellites"), _("Items satellites"), "lpesatellites", &wr, this, false),
+ allow_transforms(_("Allow Transforms"), _("Allow transforms"), "allow_transforms", &wr, this, true),
+ start_point(_("Slice line start"), _("Start point of slice line"), "start_point", &wr, this, _("Adjust start point of slice line")),
+ end_point(_("Slice line end"), _("End point of slice line"), "end_point", &wr, this, _("Adjust end point of slice line")),
+ center_point(_("Slice line mid"), _("Center point of slice line"), "center_point", &wr, this, _("Adjust center point of slice line"))
+{
+ registerParameter(&lpesatellites);
+ registerParameter(&allow_transforms);
+ registerParameter(&start_point);
+ registerParameter(&end_point);
+ registerParameter(&center_point);
+ show_orig_path = true;
+ apply_to_clippath_and_mask = false;
+ previous_center = Geom::Point(0,0);
+ center_point.param_widget_is_visible(false);
+ reset = false;
+ center_horiz = false;
+ center_vert = false;
+ allow_transforms_prev = allow_transforms;
+ on_remove_all = false;
+ container = nullptr;
+ satellitestoclipboard = true;
+}
+
+LPESlice::~LPESlice()
+{
+ keep_paths = false;
+ doOnRemove(nullptr);
+};
+
+bool
+LPESlice::doOnOpen(SPLPEItem const* lpeitem) {
+ bool fixed = false;
+ Glib::ustring version = lpeversion.param_getSVGValue();
+ if (version < "1.2") {
+ std::vector<SPLPEItem *> lpeitems = getCurrrentLPEItems();
+ if (lpeitems.size() >= 1) {
+ sp_lpe_item_update_patheffect(lpeitems[0], false, true);
+ }
+ lpeversion.param_setValue("1.2", true);
+ fixed = true;
+ lpesatellites.write_to_SVG();
+ }
+ lpesatellites.start_listening();
+ lpesatellites.connect_selection_changed();
+ return fixed;
+}
+
+Gtk::Widget *
+LPESlice::newWidget()
+{
+ // use manage here, because after deletion of Effect object, others might
+ // still be pointing to this widget.
+ Gtk::Box *vbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL));
+
+ vbox->set_border_width(5);
+ vbox->set_homogeneous(false);
+ vbox->set_spacing(2);
+ Gtk::Box *hbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 0));
+ Gtk::Button *center_vert_button = Gtk::manage(new Gtk::Button(Glib::ustring(_("Vertical"))));
+ center_vert_button->signal_clicked().connect(sigc::mem_fun(*this, &LPESlice::centerVert));
+ center_vert_button->set_size_request(110, 20);
+ Gtk::Button *center_horiz_button = Gtk::manage(new Gtk::Button(Glib::ustring(_("Horizontal"))));
+ center_horiz_button->signal_clicked().connect(sigc::mem_fun(*this, &LPESlice::centerHoriz));
+ center_horiz_button->set_size_request(110, 20);
+ Gtk::Button *reset_button = Gtk::manage(new Gtk::Button(Glib::ustring(_("Reset styles"))));
+ reset_button->signal_clicked().connect(sigc::mem_fun(*this, &LPESlice::resetStyles));
+ reset_button->set_size_request(110, 20);
+
+ vbox->pack_start(*hbox, true, true, 2);
+ hbox->pack_start(*reset_button, false, false, 2);
+ hbox->pack_start(*center_vert_button, false, false, 2);
+ hbox->pack_start(*center_horiz_button, false, false, 2);
+ std::vector<Parameter *>::iterator it = param_vector.begin();
+ while (it != param_vector.end()) {
+ if ((*it)->widget_is_visible) {
+ Parameter *param = *it;
+ Gtk::Widget *widg = dynamic_cast<Gtk::Widget *>(param->param_newWidget());
+ Glib::ustring *tip = param->param_getTooltip();
+ if (widg) {
+ vbox->pack_start(*widg, true, true, 2);
+ if (tip) {
+ widg->set_tooltip_text(*tip);
+ } else {
+ widg->set_tooltip_text("");
+ widg->set_has_tooltip(false);
+ }
+ }
+ }
+
+ ++it;
+ }
+ if (Gtk::Widget *widg = defaultParamSet()) {
+ vbox->pack_start(*widg, true, true, 2);
+ }
+ return dynamic_cast<Gtk::Widget *>(vbox);
+}
+
+
+void
+LPESlice::centerVert(){
+ center_vert = true;
+ refresh_widgets = true;
+ std::vector<SPLPEItem *> lpeitems = getCurrrentLPEItems();
+ if (lpeitems.size() == 1) {
+ sp_lpe_item = lpeitems[0];
+ sp_lpe_item_update_patheffect(sp_lpe_item, false, false);
+ }
+}
+
+void
+LPESlice::centerHoriz(){
+ center_horiz = true;
+ refresh_widgets = true;
+ std::vector<SPLPEItem *> lpeitems = getCurrrentLPEItems();
+ if (lpeitems.size() == 1) {
+ sp_lpe_item = lpeitems[0];
+ sp_lpe_item_update_patheffect(sp_lpe_item, false, false);
+ }
+}
+
+bool sp_has_path_data(SPItem *item, bool originald)
+{
+ SPGroup *group = dynamic_cast<SPGroup *>(item);
+ if (group) {
+ std::vector<SPObject *> childs = group->childList(true);
+ for (auto &child : childs) {
+ SPItem *item = dynamic_cast<SPItem *>(child);
+ if (sp_has_path_data(item, originald)) {
+ return true;
+ }
+ }
+ }
+ SPShape *shape = dynamic_cast<SPShape *>(item);
+ if (shape) {
+ SPCurve const *c = shape->curve();
+ if (c && !c->is_empty()) {
+ return true;
+ }
+ if (originald) {
+ if (shape->hasPathEffectRecursive()) {
+ SPCurve const *c = shape->curveBeforeLPE();
+ if (c && !c->is_empty()) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+/*
+ * Allow changing original-d to d to "reset" temporary the LPE
+ * when the slice doesn't pass through item till sp_lpe_item is crossed
+ */
+void
+LPESlice::originalDtoD(SPShape const *shape, SPCurve *curve)
+{
+ SPCurve const *c = shape->curveBeforeLPE();
+ if (c && !c->is_empty()) {
+ curve->set_pathvector(c->get_pathvector());
+ }
+}
+
+/*
+ * Allow changing original-d to d to "reset" temporary the LPE
+ * when the slice doesn't pass through item till sp_lpe_item is crossed
+ */
+void
+LPESlice::originalDtoD(SPItem *item)
+{
+ SPGroup *group = dynamic_cast<SPGroup *>(item);
+ if (group) {
+ std::vector<SPObject *> childs = group->childList(true);
+ for (auto &child : childs) {
+ SPItem *item = dynamic_cast<SPItem *>(child);
+ originalDtoD(item);
+ }
+ return;
+ }
+ SPShape *shape = dynamic_cast<SPShape *>(item);
+ if (shape) {
+ SPCurve const *c = shape->curveBeforeLPE();
+ if (c && !c->is_empty()) {
+ shape->bbox_vis_cache_is_valid = false;
+ shape->bbox_geom_cache_is_valid = false;
+ shape->setCurveInsync(std::move(c));
+ auto str = sp_svg_write_path(c->get_pathvector());
+ shape->setAttribute("d", str);
+ }
+ }
+}
+
+void
+LPESlice::doAfterEffect (SPLPEItem const* lpeitem, SPCurve *curve)
+{
+ Glib::ustring version = lpeversion.param_getSVGValue();
+ // this avoid regenerate fake satellites un undo after open a legacy LPE
+ if (!is_load && version < "1.2") {
+ return;
+ }
+ SPDocument *document = getSPDoc();
+ if (!document) {
+ return;
+ }
+ bool m_saved = DocumentUndo::getUndoSensitive(getSPDoc());
+ DocumentUndo::ScopedInsensitive _no_undo(document);
+ if (document->isPartial()) {
+ DocumentUndo::setUndoSensitive(document, m_saved);
+ } else if (document->isSeeking()) {
+ return;
+ }
+ bool is_applied_on = false;
+ if (is_applied) {
+ is_applied_on = true;
+ is_applied = false;
+ }
+ bool write = false;
+ bool active = !lpesatellites.data().size() || is_load;
+ for (auto lpereference : lpesatellites.data()) {
+ if (lpereference && lpereference->isAttached() && lpereference.get()->getObject() != nullptr) {
+ active = true;
+ }
+ }
+ if (!active && !is_load) {
+ lpesatellites.clear();
+ return;
+ }
+
+ LPESlice *nextslice = dynamic_cast<LPESlice *>(sp_lpe_item->getNextLPE(this));
+ if (is_visible && (!nextslice || !nextslice->is_visible)) {
+ LPESlice *prevslice = dynamic_cast<LPESlice *>(sp_lpe_item->getPrevLPE(this));
+ if (boundingbox_X.isSingular() || boundingbox_Y.isSingular()) {
+ for (auto & iter : lpesatellites.data()) {
+ SPObject *elemref;
+ if (iter && iter->isAttached() && (elemref = iter->getObject())) {
+ if (auto *splpeitem = dynamic_cast<SPLPEItem *>(elemref)) {
+ splpeitem->setHidden(true);
+ }
+ }
+ }
+ return;
+ }
+ //ungroup
+ if (!is_load && container && container != sp_lpe_item->parent && container != sp_lpe_item->parent->parent) {
+ processObjects(LPE_UPDATE);
+ } else if (!is_load && container && container != sp_lpe_item->parent) { // group
+ processObjects(LPE_UPDATE);
+ }
+ std::vector<std::pair<Geom::Line, size_t> > slicer = getSplitLines();
+ if (!slicer.size()) {
+ return;
+ }
+ container = lpeitem->parent;
+ objindex = 0;
+ legacy = false;
+ bool creation = write;
+ split(sp_lpe_item, curve, slicer, 0, creation);
+ bool connected = lpesatellites.is_connected();
+ if (lpesatellites.data().size() && (creation || !connected)) {
+ lpesatellites.write_to_SVG();
+ lpesatellites.start_listening();
+ lpesatellites.update_satellites(!connected);
+ }
+ bool maindata = sp_has_path_data(sp_lpe_item, true);
+ for (auto & iter : lpesatellites.data()) {
+ SPObject *elemref;
+ if (iter && iter->isAttached() && (elemref = iter->getObject())) {
+ SPLPEItem *splpeitem = dynamic_cast<SPLPEItem *>(elemref);
+ if (splpeitem || lpeitem->isHidden()) {
+ if (!maindata || lpeitem->isHidden()) {
+ splpeitem->setHidden(true);
+ }
+ sp_lpe_item_update_patheffect(splpeitem, false, false);
+ }
+ }
+ }
+ if (!maindata) {
+ if (!curve) { // group
+ originalDtoD(sp_lpe_item);
+ } else {
+ originalDtoD(getCurrentShape(), curve);
+ }
+ return;
+ }
+ reset = false;
+ if (is_applied_on && prevslice) {
+ sp_lpe_item_update_patheffect(sp_lpe_item, false, false);
+ for (auto link : prevslice->lpesatellites.data()) {
+ if (link && link->isAttached()) {
+ SPGroup *spgrp = dynamic_cast<SPGroup *>(link->getObject());
+ SPShape *spit = dynamic_cast<SPShape *>(link->getObject());
+ Glib::ustring transform = "";
+ Glib::ustring patheffects = "";
+ Geom::OptRect _gbbox = Geom::OptRect();
+ if (spgrp) {
+ if (spgrp->getAttribute("transform")) {
+ transform = spgrp->getAttribute("transform");
+ }
+ if (spgrp->getAttribute("inkscape:path-effect")) {
+ patheffects = spgrp->getAttribute("inkscape:path-effect");
+ }
+ spgrp->setAttribute("transform", nullptr);
+ spgrp->setAttribute("inkscape:path-effect", nullptr);
+ _gbbox = spgrp->geometricBounds();
+ }
+ if (spit || spgrp) {
+ for (auto link2 : lpesatellites.data()) {
+ if (link2 && link2->isAttached()) {
+ SPGroup *spgrp2 = dynamic_cast<SPGroup *>(link2->getObject());
+ SPShape *spit2 = dynamic_cast<SPShape *>(link2->getObject());
+ if (spit && spit2) {
+ auto edit = SPCurve::copy(spit->curveForEdit());
+ auto edit2 = SPCurve::copy(spit2->curveForEdit());
+ Geom::OptRect _bbox = edit->get_pathvector().boundsFast();
+ Geom::OptRect _bbox2 = edit2->get_pathvector().boundsFast();
+ if (_bbox && _bbox2) {
+ (*_bbox).expandBy(1);
+ if ((*_bbox).contains(*_bbox2)) {
+ spit2->setAttribute("transform", spit->getAttribute("transform"));
+ spit2->setAttribute("inkscape:path-effect", spit->getAttribute("inkscape:path-effect"));
+ spit2->setAttribute("style", spit->getAttribute("style"));
+ }
+ }
+ } else if (spgrp && spgrp2) {
+ Geom::OptRect _gbbox2 = spgrp2->geometricBounds();
+ if (_gbbox && _gbbox2) {
+ (*_gbbox).expandBy(1);
+ if ((*_gbbox).contains(*_gbbox2)) {
+ spgrp2->setAttribute("transform", transform);
+ spgrp2->setAttribute("inkscape:path-effect", patheffects);
+ cloneStyle(spgrp, spgrp2);
+ }
+ }
+ }
+ }
+ }
+ if (spgrp) {
+ spgrp->setAttribute("transform", transform);
+ spgrp->setAttribute("inkscape:path-effect", patheffects);
+ }
+ }
+ }
+ }
+
+ }
+ } else {
+ for (auto itemrf : lpesatellites.data()) {
+ if (itemrf && itemrf->isAttached()) {
+ SPLPEItem *splpeitem = dynamic_cast<SPLPEItem *>(itemrf->getObject());
+ if (splpeitem) {
+ splpeitem->setHidden(true);
+ sp_lpe_item_update_patheffect(splpeitem, false, false);
+ }
+ }
+ }
+ }
+}
+
+bool
+LPESlice::split(SPItem* item, SPCurve *curve, std::vector<std::pair<Geom::Line, size_t> > slicer, size_t splitindex, bool &creation) {
+ bool splited = false;
+ size_t nsplits = slicer.size();
+ SPDocument *document = getSPDoc();
+ if (!document) {
+ return splited;
+ }
+
+ SPObject *elemref = nullptr;
+ if (!is_load && container != sp_lpe_item->parent) {
+ lpesatellites.read_from_SVG();
+ return splited;
+ }
+ if (objindex < lpesatellites.data().size() && lpesatellites.data()[objindex]) {
+ elemref = lpesatellites.data()[objindex]->getObject();
+ }
+ bool prevreset = reset;
+
+ if (!elemref && item->getId()) {
+
+ Glib::ustring elemref_id = Glib::ustring("slice-");
+ elemref_id += Glib::ustring::format(slicer[splitindex].second);
+ elemref_id += "-";
+ Glib::ustring clean_id = item->getId();
+ //First check is to allow effects on "satellites"
+ SPLPEItem *lpeitem = dynamic_cast<SPLPEItem *>(item);
+ if (!lpeitem) {
+ return splited;
+ }
+ if (!lpeitem->hasPathEffectOfType(SLICE) && clean_id.find("slice-") != Glib::ustring::npos) {
+ clean_id = clean_id.replace(0,6,"");
+ elemref_id += clean_id;
+ } else {
+ elemref_id += clean_id;
+ }
+ creation = true;
+ if (is_load && (elemref = document->getObjectById(elemref_id))) {
+ legacy = true;
+ lpesatellites.link(elemref, objindex);
+ } else {
+ reset = true;
+ Inkscape::XML::Node *phantom = createPathBase(item);
+ if (!container) {
+ return splited;
+ }
+ elemref = container->appendChildRepr(phantom);
+ Inkscape::GC::release(phantom);
+ lpesatellites.link(elemref, objindex);
+ }
+ }
+ SPItem *other = dynamic_cast<SPItem *>(elemref);
+ if (other) {
+ objindex++;
+ other->setHidden(false);
+ if (nsplits) {
+ cloneD(item, other, false);
+ reset = prevreset;
+ splited = splititem(item, curve, slicer[splitindex], true);
+ splititem(other, nullptr, slicer[splitindex], false);
+ if (!splited) {
+ other->setHidden(true);
+ }
+ splitindex++;
+ if (nsplits > splitindex) {
+ SPLPEItem *splpeother = dynamic_cast<SPLPEItem *>(other);
+ SPLPEItem *splpeitem = dynamic_cast<SPLPEItem *>(item);
+ if (item == sp_lpe_item || !splpeitem->hasPathEffectOfType(SLICE)) {
+ split(item, curve, slicer, splitindex, creation);
+ if (other == sp_lpe_item || !splpeother->hasPathEffectOfType(SLICE)) {
+ split(other, nullptr, slicer, splitindex, creation);
+ }
+ }
+ }
+ }
+ }
+ return splited;
+}
+
+std::vector<std::pair<Geom::Line, size_t> >
+LPESlice::getSplitLines() {
+ std::vector<std::pair<Geom::Line, size_t> > splitlines;
+ std::vector<SPLPEItem *> lpeitems = getCurrrentLPEItems();
+ if (lpeitems.size() >= 1) {
+ sp_lpe_item = lpeitems[0];
+ } else {
+ return splitlines;
+ }
+ LPESlice *prevslice = dynamic_cast<LPESlice *>(sp_lpe_item->getPrevLPE(this));
+ if (prevslice) {
+ splitlines = prevslice->getSplitLines();
+ }
+ Geom::Line line_separation((Geom::Point)start_point, (Geom::Point)end_point);
+ size_t index = sp_lpe_item->getLPEIndex(this);
+ std::pair<Geom::Line, size_t> slice = std::make_pair(line_separation, index);
+ splitlines.push_back(slice);
+ return splitlines;
+}
+
+Inkscape::XML::Node *
+LPESlice::createPathBase(SPObject *elemref) {
+ SPDocument *document = getSPDoc();
+ if (!document) {
+ return nullptr;
+ }
+ Inkscape::XML::Document *xml_doc = getSPDoc()->getReprDoc();
+ Inkscape::XML::Node *prev = elemref->getRepr();
+ SPGroup *group = dynamic_cast<SPGroup *>(elemref);
+ if (group) {
+ Inkscape::XML::Node *container = xml_doc->createElement("svg:g");
+ container->setAttribute("transform", prev->attribute("transform"));
+ container->setAttribute("mask", prev->attribute("mask"));
+ container->setAttribute("clip-path", prev->attribute("clip-path"));
+ std::vector<SPItem*> const item_list = sp_item_group_item_list(group);
+ Inkscape::XML::Node *previous = nullptr;
+ for (auto sub_item : item_list) {
+ Inkscape::XML::Node *resultnode = createPathBase(sub_item);
+ container->addChild(resultnode, previous);
+ previous = resultnode;
+ }
+ return container;
+ }
+ Inkscape::XML::Node *resultnode = xml_doc->createElement("svg:path");
+ resultnode->setAttribute("transform", prev->attribute("transform"));
+ resultnode->setAttribute("mask", prev->attribute("mask"));
+ resultnode->setAttribute("clip-path", prev->attribute("clip-path"));
+ return resultnode;
+}
+
+void
+LPESlice::cloneD(SPObject *orig, SPObject *dest, bool is_original)
+{
+ if (!is_original && !g_strcmp0(sp_lpe_item->getId(), orig->getId())) {
+ is_original = true;
+ }
+ SPDocument *document = getSPDoc();
+ if (!document) {
+ return;
+ }
+ SPItem *originalitem = dynamic_cast<SPItem *>(orig);
+ if ( SP_IS_GROUP(orig) && SP_IS_GROUP(dest) && SP_GROUP(orig)->getItemCount() == SP_GROUP(dest)->getItemCount() ) {
+ if (reset) {
+ cloneStyle(orig, dest);
+ }
+ if (!allow_transforms) {
+ auto str = sp_svg_transform_write(originalitem->transform);
+ dest->setAttributeOrRemoveIfEmpty("transform", str);
+ }
+ std::vector< SPObject * > childs = orig->childList(true);
+ size_t index = 0;
+ for (auto &child : childs) {
+ SPObject *dest_child = dest->nthChild(index);
+ cloneD(child, dest_child, is_original);
+ index++;
+ }
+ return;
+ }
+
+ SPShape * shape = SP_SHAPE(orig);
+ SPPath * path = SP_PATH(dest);
+ SPLPEItem *splpeitem = dynamic_cast<SPLPEItem *>(path);
+ if (path && shape && splpeitem) {
+ SPCurve const *c = shape->curve();
+ if (c && !c->is_empty()) {
+ auto str = sp_svg_write_path(c->get_pathvector());
+ if (path->hasPathEffectRecursive()) {
+ sp_lpe_item_enable_path_effects(path, false);
+ dest->setAttribute("inkscape:original-d", str);
+ sp_lpe_item_enable_path_effects(path, true);
+ dest->setAttribute("d", str);
+ } else {
+ dest->setAttribute("d", str);
+ }
+ if (!allow_transforms) {
+ auto str = sp_svg_transform_write(originalitem->transform);
+ dest->setAttributeOrRemoveIfEmpty("transform", str);
+ }
+ if (reset) {
+ cloneStyle(orig, dest);
+ }
+ }
+ }
+}
+
+static fill_typ GetFillTyp(SPItem *item)
+{
+ SPCSSAttr *css = sp_repr_css_attr(item->getRepr(), "style");
+ gchar const *val = sp_repr_css_property(css, "fill-rule", nullptr);
+ if (val && strcmp(val, "nonzero") == 0) {
+ return fill_nonZero;
+ } else if (val && strcmp(val, "evenodd") == 0) {
+ return fill_oddEven;
+ } else {
+ return fill_nonZero;
+ }
+}
+
+bool
+LPESlice::splititem(SPItem* item, SPCurve * curve, std::pair<Geom::Line, size_t> slicer, bool toggle, bool is_original)
+{
+ bool splited = false;
+ if (!is_original && !g_strcmp0(sp_lpe_item->getId(), item->getId())) {
+ is_original = true;
+ }
+ Geom::Line line_separation = slicer.first;
+ Geom::Point s = line_separation.initialPoint();
+ Geom::Point e = line_separation.finalPoint();
+ Geom::Point center = Geom::middle_point(s, e);
+ SPGroup *group = dynamic_cast<SPGroup *>(item);
+ if (group) {
+ std::vector<SPObject *> childs = group->childList(true);
+ for (auto &child : childs) {
+ SPItem *dest_child = dynamic_cast<SPItem *>(child);
+ // groups not need update curve
+ splited = splititem(dest_child, nullptr, slicer, toggle, is_original) ? true : splited;
+ }
+ if (!is_original && group->hasPathEffectRecursive()) {
+ sp_lpe_item_update_patheffect(group, false, false);
+ }
+ return splited;
+ }
+ SPShape *shape = dynamic_cast<SPShape *>(item);
+ SPPath *path = dynamic_cast<SPPath *>(item);
+ if (shape) {
+ SPCurve const *c;
+ c = shape->curve();
+ if (c) {
+ Geom::PathVector original_pathv = pathv_to_linear_and_cubic_beziers(c->get_pathvector());
+ sp_flatten(original_pathv, GetFillTyp(shape));
+ Geom::PathVector path_out;
+ Geom::Affine t = shape->transform;
+ if (!dynamic_cast<SPGroup *>(sp_lpe_item)) {
+ t = Geom::identity();
+ }
+ for (auto & path_it : original_pathv) {
+ path_it *= t;
+ if (path_it.empty()) {
+ continue;
+ }
+ Geom::PathVector tmp_pathvector;
+ double time_start = 0.0;
+ int position = 0;
+ bool end_open = false;
+ if (path_it.closed()) {
+ const Geom::Curve &closingline = path_it.back_closed();
+ if (!are_near(closingline.initialPoint(), closingline.finalPoint())) {
+ end_open = true;
+ }
+ }
+ Geom::Path original = path_it;
+ if (end_open && path_it.closed()) {
+ original.close(false);
+ original.appendNew<Geom::LineSegment>( original.initialPoint() );
+ original.close(true);
+ }
+ double dir = line_separation.angle();
+ Geom::Ray ray = line_separation.ray(0);
+ double diagonal = Geom::distance(Geom::Point(boundingbox_X.min(),boundingbox_Y.min()),Geom::Point(boundingbox_X.max(),boundingbox_Y.max()));
+ Geom::Rect bbox(Geom::Point(boundingbox_X.min(),boundingbox_Y.min()),Geom::Point(boundingbox_X.max(),boundingbox_Y.max()));
+ double size_divider = Geom::distance(center, bbox) + diagonal;
+ s = Geom::Point::polar(dir,size_divider) + center;
+ e = Geom::Point::polar(dir + Geom::rad_from_deg(180),size_divider) + center;
+ Geom::Path divider = Geom::Path(s);
+ divider.appendNew<Geom::LineSegment>(e);
+ std::vector<double> crossed;
+ if (Geom::are_near(s,e)) {
+ continue;
+ }
+ Geom::Crossings cs = crossings(original, divider);
+ for(auto & c : cs) {
+ crossed.push_back(c.ta);
+ }
+ double angle = Geom::deg_from_rad(ray.angle());
+ bool toggleside = !(angle > 0 && angle < 180);
+ std::sort(crossed.begin(), crossed.end());
+ for (double time_end : crossed) {
+ if (time_start != time_end && time_end - time_start > Geom::EPSILON) {
+ Geom::Path portion = original.portion(time_start, time_end);
+ if (!portion.empty()) {
+ Geom::Point middle = portion.pointAt((double)portion.size()/2.0);
+ position = Geom::sgn(Geom::cross(e - s, middle - s));
+ if (toggleside) {
+ position *= -1;
+ }
+ if (toggle) {
+ position *= -1;
+ }
+ if (position == 1) {
+ tmp_pathvector.push_back(portion);
+ }
+ portion.clear();
+ }
+ }
+ time_start = time_end;
+ }
+ position = Geom::sgn(Geom::cross(e - s, original.finalPoint() - s));
+ if (toggleside) {
+ position *= -1;
+ }
+ if (toggle) {
+ position *= -1;
+ }
+ if (cs.size()!=0 && (position == 1)) {
+ if (time_start != original.size() && original.size() - time_start > Geom::EPSILON) {
+ Geom::Path portion = original.portion(time_start, original.size());
+ if (!portion.empty()) {
+ if (!original.closed()) {
+ tmp_pathvector.push_back(portion);
+ } else {
+ if (cs.size() > 1 && tmp_pathvector.size() > 0 && tmp_pathvector[0].size() > 0 ) {
+ tmp_pathvector[0] = tmp_pathvector[0].reversed();
+ portion = portion.reversed();
+ portion.setInitial(tmp_pathvector[0].finalPoint());
+ tmp_pathvector[0].append(portion);
+ tmp_pathvector[0] = tmp_pathvector[0].reversed();
+ } else {
+ tmp_pathvector.push_back(portion);
+ }
+ }
+ portion.clear();
+ }
+ }
+ }
+ if (cs.size() > 0 && original.closed()) {
+ for (auto &path : tmp_pathvector) {
+ if (!path.closed()) {
+ path.close();
+ }
+ }
+ }
+ if (cs.size() == 0 && position == 1) {
+ splited = false;
+ tmp_pathvector.push_back(original);
+ } else {
+ splited = true;
+ }
+ tmp_pathvector *= t.inverse();
+ path_out.insert(path_out.end(), tmp_pathvector.begin(), tmp_pathvector.end());
+
+ tmp_pathvector.clear();
+ }
+ if (curve && is_original) {
+ curve->set_pathvector(path_out);
+ }
+ auto cpro = SPCurve::copy(shape->curve());
+ if (cpro) {
+ shape->bbox_vis_cache_is_valid = false;
+ shape->bbox_geom_cache_is_valid = false;
+ cpro->set_pathvector(path_out);
+ shape->setCurveInsync(std::move(cpro));
+ auto str = sp_svg_write_path(path_out);
+ if (!is_original && shape->hasPathEffectRecursive()) {
+ sp_lpe_item_enable_path_effects(shape, false);
+ if (path) {
+ shape->setAttribute("inkscape:original-d", str);
+ } else {
+ shape->setAttribute("d", str);
+ }
+ sp_lpe_item_enable_path_effects(shape, true);
+ } else {
+ shape->setAttribute("d", str);
+ }
+ }
+ }
+ }
+ return splited;
+}
+
+void
+LPESlice::doBeforeEffect (SPLPEItem const* lpeitem)
+{
+ SPDocument *document = getSPDoc();
+ if (!document) {
+ return;
+ }
+ if (!lpesatellites.data().size()) {
+ lpesatellites.read_from_SVG();
+ if (lpesatellites.data().size()) {
+ lpesatellites.update_satellites();
+ }
+ }
+ using namespace Geom;
+ original_bbox(lpeitem, false, true);
+ Point point_a(boundingbox_X.max(), boundingbox_Y.min());
+ Point point_b(boundingbox_X.max(), boundingbox_Y.max());
+ Point point_c(boundingbox_X.middle(), boundingbox_Y.middle());
+ if (center_vert) {
+ double dista = std::abs(end_point[Geom::Y] - boundingbox_Y.min());
+ double distb = std::abs(start_point[Geom::Y] - boundingbox_Y.min());
+ previous_center = Geom::Point(Geom::infinity(), g_random_double_range(0, 1000));
+ end_point.param_setValue(
+ Geom::Point(center_point[Geom::X], dista <= distb ? boundingbox_Y.min() : boundingbox_Y.max()), true);
+ start_point.param_setValue(
+ Geom::Point(center_point[Geom::X], dista > distb ? boundingbox_Y.min() : boundingbox_Y.max()), true);
+ //force update
+ center_vert = false;
+ } else if (center_horiz) {
+ double dista = std::abs(end_point[Geom::X] - boundingbox_X.min());
+ double distb = std::abs(start_point[Geom::X] - boundingbox_X.min());
+ previous_center = Geom::Point(Geom::infinity(), g_random_double_range(0, 1000));
+ end_point.param_setValue(
+ Geom::Point(dista <= distb ? boundingbox_X.min() : boundingbox_X.max(), center_point[Geom::Y]), true);
+ start_point.param_setValue(
+ Geom::Point(dista > distb ? boundingbox_X.min() : boundingbox_X.max(), center_point[Geom::Y]), true);
+ //force update
+ center_horiz = false;
+ } else {
+ if ((Geom::Point)start_point == (Geom::Point)end_point) {
+ start_point.param_setValue(point_a);
+ end_point.param_setValue(point_b);
+ previous_center = Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point);
+ center_point.param_setValue(previous_center);
+ return;
+ }
+ if (are_near(previous_center, (Geom::Point)center_point, 0.001)) {
+ center_point.param_setValue(Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point));
+ } else {
+ Geom::Point trans = center_point - Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point);
+ start_point.param_setValue(start_point * trans);
+ end_point.param_setValue(end_point * trans);
+ }
+ }
+ if (allow_transforms_prev != allow_transforms) {
+ LPESlice *nextslice = dynamic_cast<LPESlice *>(sp_lpe_item->getNextLPE(this));
+ while (nextslice) {
+ if (nextslice->allow_transforms != allow_transforms) {
+ nextslice->allow_transforms_prev = allow_transforms;
+ nextslice->allow_transforms.param_setValue(allow_transforms);
+ }
+ nextslice = dynamic_cast<LPESlice *>(sp_lpe_item->getNextLPE(nextslice));
+ }
+ LPESlice *prevslice = dynamic_cast<LPESlice *>(sp_lpe_item->getPrevLPE(this));
+ while (prevslice) {
+ if (prevslice->allow_transforms != allow_transforms) {
+ prevslice->allow_transforms_prev = allow_transforms;
+ prevslice->allow_transforms.param_setValue(allow_transforms);
+ }
+ prevslice = dynamic_cast<LPESlice *>(sp_lpe_item->getNextLPE(prevslice));
+ }
+ }
+ allow_transforms_prev = allow_transforms;
+}
+
+void LPESlice::cloneStyle(SPObject *orig, SPObject *dest)
+{
+ for (auto iter : orig->style->properties()) {
+ if (iter->style_src != SPStyleSrc::UNSET) {
+ auto key = iter->id();
+ if (key != SPAttr::FONT && key != SPAttr::D && key != SPAttr::MARKER) {
+ const gchar *attr = orig->getAttribute(iter->name().c_str());
+ if (attr) {
+ dest->setAttribute(iter->name(), attr);
+ }
+ }
+ }
+ }
+ dest->setAttribute("style", orig->getAttribute("style"));
+}
+
+void
+LPESlice::resetStyles(){
+ std::vector<SPLPEItem *> lpeitems = getCurrrentLPEItems();
+ if (lpeitems.size() == 1) {
+ sp_lpe_item = lpeitems[0];
+ LPESlice *nextslice = dynamic_cast<LPESlice *>(sp_lpe_item->getNextLPE(this));
+ while (nextslice) {
+ nextslice->reset = true;
+ nextslice = dynamic_cast<LPESlice *>(sp_lpe_item->getNextLPE(nextslice));
+ }
+ reset = true;
+ sp_lpe_item_update_patheffect(sp_lpe_item, false, false);
+ }
+}
+
+void
+LPESlice::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/)
+{
+ if (!is_visible) {
+ for (auto itemrf : lpesatellites.data()) {
+ if (itemrf && itemrf->isAttached()) {
+ SPLPEItem *splpeitem = dynamic_cast<SPLPEItem *>(itemrf->getObject());
+ if (splpeitem) {
+ splpeitem->setHidden(true);
+ sp_lpe_item_update_patheffect(splpeitem, false, false);
+ }
+ }
+ }
+ }
+}
+
+
+void
+LPESlice::doOnRemove(SPLPEItem const* lpeitem)
+{
+ if (keep_paths) {
+ processObjects(LPE_TO_OBJECTS);
+ return;
+ }
+ processObjects(LPE_ERASE);
+}
+
+void
+LPESlice::doOnApply (SPLPEItem const* lpeitem)
+{
+ using namespace Geom;
+ original_bbox(lpeitem, false, true);
+ LPESlice *prevslice = dynamic_cast<LPESlice *>(sp_lpe_item->getPrevLPE(this));
+ if (prevslice) {
+ allow_transforms_prev = prevslice->allow_transforms;
+ allow_transforms.param_setValue(prevslice->allow_transforms);
+ }
+ Point point_a(boundingbox_X.middle(), boundingbox_Y.min());
+ Point point_b(boundingbox_X.middle(), boundingbox_Y.max());
+ Point point_c(boundingbox_X.middle(), boundingbox_Y.middle());
+ start_point.param_setValue(point_a, true);
+ start_point.param_update_default(point_a);
+ end_point.param_setValue(point_b, true);
+ end_point.param_update_default(point_b);
+ center_point.param_setValue(point_c, true);
+ end_point.param_update_default(point_c);
+ previous_center = center_point;
+ lpeversion.param_setValue("1.2", true);
+ lpesatellites.update_satellites(true);
+}
+
+
+Geom::PathVector
+LPESlice::doEffect_path (Geom::PathVector const & path_in)
+{
+ return path_in;
+}
+
+void
+LPESlice::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec)
+{
+ using namespace Geom;
+ hp_vec.clear();
+ Geom::Path path;
+ Geom::Point s = start_point;
+ Geom::Point e = end_point;
+ path.start( s );
+ path.appendNew<Geom::LineSegment>( e );
+ Geom::PathVector helper;
+ helper.push_back(path);
+ hp_vec.push_back(helper);
+}
+
+} //namespace LivePathEffect
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((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 :