summaryrefslogtreecommitdiffstats
path: root/src/live_effects/lpe-mirror_symmetry.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/live_effects/lpe-mirror_symmetry.cpp')
-rw-r--r--src/live_effects/lpe-mirror_symmetry.cpp705
1 files changed, 705 insertions, 0 deletions
diff --git a/src/live_effects/lpe-mirror_symmetry.cpp b/src/live_effects/lpe-mirror_symmetry.cpp
new file mode 100644
index 0000000..5a71804
--- /dev/null
+++ b/src/live_effects/lpe-mirror_symmetry.cpp
@@ -0,0 +1,705 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** \file
+ * LPE <mirror_symmetry> implementation: mirrors 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-mirror_symmetry.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 "style.h"
+#include "svg/path-string.h"
+#include "svg/svg.h"
+#include "xml/sp-css-attr.h"
+#include "path/path-boolop.h"
+
+// TODO due to internal breakage in glibmm headers, this must be last:
+#include <glibmm/i18n.h>
+
+typedef FillRule FillRuleFlatten;
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+static const Util::EnumData<ModeType> ModeTypeData[] = {
+ { MT_V, N_("Vertical page center"), "vertical" },
+ { MT_H, N_("Horizontal page center"), "horizontal" },
+ { MT_FREE, N_("Freely defined mirror line"), "free" },
+ { MT_X, N_("X coordinate of mirror line midpoint"), "X" },
+ { MT_Y, N_("Y coordinate of mirror line midpoint"), "Y" }
+};
+static const Util::EnumDataConverter<ModeType>
+MTConverter(ModeTypeData, MT_END);
+
+
+LPEMirrorSymmetry::LPEMirrorSymmetry(LivePathEffectObject *lpeobject) :
+ Effect(lpeobject),
+ // do not change name of this parameter us used in oncommit
+ lpesatellites(_("lpesatellites"), _("Items satellites"), "lpesatellites", &wr, this, false),
+ mode(_("Mode"), _("Set mode of transformation. Either freely defined by mirror line or constrained to certain symmetry points."), "mode", MTConverter, &wr, this, MT_FREE),
+ discard_orig_path(_("Discard original path"), _("Only keep mirrored part of the path, remove the original."), "discard_orig_path", &wr, this, false),
+ fuse_paths(_("Fuse paths"), _("Fuse original path and mirror image into a single path"), "fuse_paths", &wr, this, false),
+ oposite_fuse(_("Fuse opposite sides"), _("Picks the part on the other side of the mirror line as the original."), "oposite_fuse", &wr, this, false),
+ split_items(_("Split elements"), _("Split original and mirror image into separate paths, so each can have its own style."), "split_items", &wr, this, false),
+ split_open(_("Keep open paths on split"), _("Do not automatically close paths along the split line."), "split_open", &wr, this, false),
+ start_point(_("Mirror line start"), _("Start point of mirror line"), "start_point", &wr, this, _("Adjust start point of mirror line")),
+ end_point(_("Mirror line end"), _("End point of mirror line"), "end_point", &wr, this, _("Adjust end point of mirror line")),
+ center_point(_("Mirror line mid"), _("Center point of mirror line"), "center_point", &wr, this, _("Adjust center point of mirror line")),
+ link_styles(_("Link styles"), _("Link styles on split mode"), "link_styles", &wr, this, false)
+{
+ registerParameter(&lpesatellites);
+ registerParameter(&mode);
+ registerParameter(&discard_orig_path);
+ registerParameter(&fuse_paths);
+ registerParameter(&oposite_fuse);
+ registerParameter(&split_items);
+ registerParameter(&split_open);
+ registerParameter(&link_styles);
+ registerParameter(&start_point);
+ registerParameter(&end_point);
+ registerParameter(&center_point);
+ show_orig_path = true;
+ apply_to_clippath_and_mask = true;
+ previous_center = Geom::Point(0,0);
+ center_point.param_widget_is_visible(false);
+ reset = link_styles;
+ center_horiz = false;
+ center_vert = false;
+ satellitestoclipboard = true;
+}
+
+LPEMirrorSymmetry::~LPEMirrorSymmetry()
+{
+ keep_paths = false;
+ doOnRemove(nullptr);
+};
+
+bool LPEMirrorSymmetry::doOnOpen(SPLPEItem const *lpeitem)
+{
+ bool fixed = false;
+ if (!is_load || is_applied || !split_items) {
+ return fixed;
+ }
+
+ Glib::ustring version = lpeversion.param_getSVGValue();
+ if (version < "1.2") {
+ lpesatellites.clear();
+ Glib::ustring id = Glib::ustring("mirror-");
+ id += getLPEObj()->getId();
+ SPObject *elemref = getSPDoc()->getObjectById(id.c_str());
+ if (elemref) {
+ lpesatellites.link(elemref, 0);
+ }
+ lpeversion.param_setValue("1.2", true);
+ fixed = true;
+ lpesatellites.write_to_SVG();
+ }
+ lpesatellites.start_listening();
+ lpesatellites.connect_selection_changed();
+ container = dynamic_cast<SPObject *>(lpeitem->parent);
+ return fixed;
+}
+
+void
+LPEMirrorSymmetry::doAfterEffect (SPLPEItem const* lpeitem, SPCurve *curve)
+{
+ SPDocument *document = getSPDoc();
+ if (!document) {
+ return;
+ }
+ container = dynamic_cast<SPObject *>(sp_lpe_item->parent);
+
+ if (split_items && !discard_orig_path) {
+ bool active = !lpesatellites.data().size() || is_load;
+ for (auto lpereference : lpesatellites.data()) {
+ if (lpereference && lpereference->isAttached() && lpereference.get()->getObject() != nullptr) {
+ active = true;
+ }
+ }
+ // we need to call this when the LPE is "mirrored 1 or + times in split mode"
+ // to prevent satellite hidden as in prev status
+ if (!active && !is_load && prev_split && !prev_discard_orig_path) {
+ lpesatellites.clear();
+ return;
+ }
+ Geom::Line ls((Geom::Point)start_point, (Geom::Point)end_point);
+ Geom::Affine m = Geom::reflection (ls.vector(), (Geom::Point)start_point);
+ m *= sp_lpe_item->transform;
+ toMirror(m);
+ }
+ prev_split = split_items;
+ prev_discard_orig_path = discard_orig_path;
+}
+
+Gtk::Widget *
+LPEMirrorSymmetry::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);
+ 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 && param->param_key != "split_open") {
+ 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;
+ }
+ 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"))));
+ center_vert_button->signal_clicked().connect(sigc::mem_fun (*this,&LPEMirrorSymmetry::centerVert));
+ center_vert_button->set_size_request(110,20);
+ Gtk::Button * center_horiz_button = Gtk::manage(new Gtk::Button(Glib::ustring(_("Horizontal center"))));
+ center_horiz_button->signal_clicked().connect(sigc::mem_fun (*this,&LPEMirrorSymmetry::centerHoriz));
+ center_horiz_button->set_size_request(110,20);
+ vbox->pack_start(*hbox, true,true,2);
+ hbox->pack_start(*center_vert_button, false, false,2);
+ hbox->pack_start(*center_horiz_button, false, false,2);
+ if(Gtk::Widget* widg = defaultParamSet()) {
+ vbox->pack_start(*widg, true, true, 2);
+ }
+ return dynamic_cast<Gtk::Widget *>(vbox);
+}
+
+void
+LPEMirrorSymmetry::centerVert(){
+ center_vert = true;
+ refresh_widgets = true;
+ writeParamsToSVG();
+}
+
+void
+LPEMirrorSymmetry::centerHoriz(){
+ center_horiz = true;
+ refresh_widgets = true;
+ writeParamsToSVG();
+}
+
+void
+LPEMirrorSymmetry::doBeforeEffect (SPLPEItem const* lpeitem)
+{
+ using namespace Geom;
+ if ((!split_items || discard_orig_path) && lpesatellites.data().size()) {
+ processObjects(LPE_ERASE);
+ }
+ if (link_styles) {
+ reset = true;
+ }
+ if (!lpesatellites.data().size()) {
+ lpesatellites.read_from_SVG();
+ if (lpesatellites.data().size()) {
+ lpesatellites.update_satellites();
+ }
+ }
+ 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) {
+ center_point.param_setValue(point_c);
+ end_point.param_setValue(Geom::Point(boundingbox_X.middle(), boundingbox_Y.min()));
+ //force update
+ start_point.param_setValue(Geom::Point(boundingbox_X.middle(), boundingbox_Y.max()),true);
+ center_vert = false;
+ } else if (center_horiz) {
+ center_point.param_setValue(point_c);
+ end_point.param_setValue(Geom::Point(boundingbox_X.max(), boundingbox_Y.middle()));
+ start_point.param_setValue(Geom::Point(boundingbox_X.min(), boundingbox_Y.middle()),true);
+ //force update
+ center_horiz = false;
+ } else {
+
+ if (mode == MT_Y) {
+ point_a = Geom::Point(boundingbox_X.min(),center_point[Y]);
+ point_b = Geom::Point(boundingbox_X.max(),center_point[Y]);
+ center_point.param_setValue(Geom::middle_point((Geom::Point)point_a, (Geom::Point)point_b));
+ }
+ if (mode == MT_X) {
+ point_a = Geom::Point(center_point[X],boundingbox_Y.min());
+ point_b = Geom::Point(center_point[X],boundingbox_Y.max());
+ center_point.param_setValue(Geom::middle_point((Geom::Point)point_a, (Geom::Point)point_b));
+ }
+ 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 ( mode == MT_X || mode == MT_Y ) {
+ if (!are_near(previous_center, (Geom::Point)center_point, 0.01)) {
+ center_point.param_setValue(Geom::middle_point(point_a, point_b));
+ end_point.param_setValue(point_b);
+ start_point.param_setValue(point_a);
+ } else {
+ if ( mode == MT_X ) {
+ if (!are_near(start_point[X], point_a[X], 0.01)) {
+ start_point.param_setValue(point_a);
+ }
+ if (!are_near(end_point[X], point_b[X], 0.01)) {
+ end_point.param_setValue(point_b);
+ }
+ } else { //MT_Y
+ if (!are_near(start_point[Y], point_a[Y], 0.01)) {
+ start_point.param_setValue(point_a);
+ }
+ if (!are_near(end_point[Y], point_b[Y], 0.01)) {
+ end_point.param_setValue(point_b);
+ }
+ }
+ }
+ } else if ( mode == MT_FREE) {
+ if (are_near(previous_center, (Geom::Point)center_point, 0.01)) {
+ 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);
+ }
+ } else if ( mode == MT_V){
+ SPDocument *document = getSPDoc();
+ if (document) {
+ Geom::Affine transform = i2anc_affine(lpeitem, nullptr).inverse();
+ Geom::Point sp = Geom::Point(document->getWidth().value("px")/2.0, 0) * transform;
+ start_point.param_setValue(sp);
+ Geom::Point ep = Geom::Point(document->getWidth().value("px")/2.0, document->getHeight().value("px")) * transform;
+ end_point.param_setValue(ep);
+ center_point.param_setValue(Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point));
+ }
+ } else { //horizontal page
+ SPDocument *document = getSPDoc();
+ if (document) {
+ Geom::Affine transform = i2anc_affine(lpeitem, nullptr).inverse();
+ Geom::Point sp = Geom::Point(0, document->getHeight().value("px")/2.0) * transform;
+ start_point.param_setValue(sp);
+ Geom::Point ep = Geom::Point(document->getWidth().value("px"), document->getHeight().value("px")/2.0) * transform;
+ end_point.param_setValue(ep);
+ center_point.param_setValue(Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point));
+ }
+ }
+ }
+ previous_center = center_point;
+}
+
+void LPEMirrorSymmetry::cloneStyle(SPObject *orig, SPObject *dest)
+{
+ dest->setAttribute("transform", orig->getAttribute("transform"));
+ dest->setAttribute("mask", orig->getAttribute("mask"));
+ dest->setAttribute("clip-path", orig->getAttribute("clip-path"));
+ dest->setAttribute("class", orig->getAttribute("class"));
+ dest->setAttribute("style", orig->getAttribute("style"));
+ 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);
+ }
+ }
+ }
+ }
+}
+
+void LPEMirrorSymmetry::cloneD(SPObject *orig, SPObject *dest)
+{
+ SPDocument *document = getSPDoc();
+ if (!document) {
+ return;
+ }
+ if ( SP_IS_GROUP(orig) && SP_IS_GROUP(dest) && SP_GROUP(orig)->getItemCount() == SP_GROUP(dest)->getItemCount() ) {
+ if (reset) {
+ cloneStyle(orig, dest);
+ }
+ 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);
+ index++;
+ }
+ return;
+ } else if( SP_IS_GROUP(orig) && SP_IS_GROUP(dest) && SP_GROUP(orig)->getItemCount() != SP_GROUP(dest)->getItemCount()) {
+ split_items.param_setValue(false);
+ return;
+ }
+
+ if (SP_IS_TEXT(orig) && SP_IS_TEXT(dest) && SP_TEXT(orig)->children.size() == SP_TEXT(dest)->children.size()) {
+ if (reset) {
+ cloneStyle(orig, dest);
+ }
+ size_t index = 0;
+ for (auto &child : SP_TEXT(orig)->children) {
+ SPObject *dest_child = dest->nthChild(index);
+ cloneD(&child, dest_child);
+ index++;
+ }
+ }
+
+ SPShape * shape = SP_SHAPE(orig);
+ SPPath * path = SP_PATH(dest);
+ if (shape) {
+ SPCurve const *c = shape->curve();
+ if (c) {
+ auto str = sp_svg_write_path(c->get_pathvector());
+ if (shape && !path) {
+ const char *id = dest->getAttribute("id");
+ const char *style = dest->getAttribute("style");
+ Inkscape::XML::Document *xml_doc = dest->document->getReprDoc();
+ Inkscape::XML::Node *dest_node = xml_doc->createElement("svg:path");
+ dest_node->setAttribute("id", id);
+ dest_node->setAttribute("style", style);
+ dest->updateRepr(xml_doc, dest_node, SP_OBJECT_WRITE_ALL);
+ path = SP_PATH(dest);
+ }
+ path->setAttribute("d", str);
+ } else {
+ path->removeAttribute("d");
+ }
+ }
+ if (reset) {
+ cloneStyle(orig, dest);
+ }
+}
+
+Inkscape::XML::Node *
+LPEMirrorSymmetry::createPathBase(SPObject *elemref) {
+ SPDocument *document = getSPDoc();
+ if (!document) {
+ return nullptr;
+ }
+ Inkscape::XML::Document *xml_doc = document->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"));
+ container->setAttribute("class", prev->attribute("class"));
+ 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"));
+ resultnode->setAttribute("class", prev->attribute("class"));
+ return resultnode;
+}
+
+void
+LPEMirrorSymmetry::toMirror(Geom::Affine transform)
+{
+ SPDocument *document = getSPDoc();
+ if (!document) {
+ return;
+ }
+ //Inkscape::XML::Document *xml_doc = document->getReprDoc();
+ SPObject *elemref = nullptr;
+ if (!is_load && container != sp_lpe_item->parent) {
+ lpesatellites.read_from_SVG();
+ return;
+ }
+ if (lpesatellites.data().size() && lpesatellites.data()[0]) {
+ elemref = lpesatellites.data()[0]->getObject();
+ }
+ Inkscape::XML::Node *phantom = nullptr;
+ bool creation = false;
+ if (elemref) {
+ phantom = elemref->getRepr();
+ } else {
+ creation = true;
+ phantom = createPathBase(sp_lpe_item);
+ reset = true;
+ elemref = container->appendChildRepr(phantom);
+ Inkscape::GC::release(phantom);
+ }
+ cloneD(sp_lpe_item, elemref);
+ reset = link_styles;
+ elemref->getRepr()->setAttributeOrRemoveIfEmpty("transform", sp_svg_transform_write(transform));
+ // Alow work in clones
+ /* if (elemref->parent != container) {
+ if (!creation) {
+ lpesatellites.unlink(elemref);
+ }
+ Inkscape::XML::Node *copy = phantom->duplicate(xml_doc);
+ copy->setAttribute("id", elemref->getId());
+ lpesatellites.link(container->appendChildRepr(copy), 0);
+ Inkscape::GC::release(copy);
+ elemref->deleteObject();
+ lpesatellites.write_to_SVG();
+ lpesatellites.update_satellites();
+ } else */
+ if (creation) {
+ lpesatellites.clear();
+ lpesatellites.link(elemref, 0);
+ lpesatellites.write_to_SVG();
+ if (lpesatellites.is_connected()) {
+ lpesatellites.update_satellites();
+ }
+ }
+ if (!lpesatellites.is_connected()) {
+ if (!creation) {
+ lpesatellites.write_to_SVG();
+ }
+ lpesatellites.start_listening();
+ lpesatellites.update_satellites(true);
+ }
+}
+
+
+//TODO: Migrate the tree next function to effect.cpp/h to avoid duplication
+void
+LPEMirrorSymmetry::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/)
+{
+ processObjects(LPE_VISIBILITY);
+}
+
+void
+LPEMirrorSymmetry::doOnRemove (SPLPEItem const* /*lpeitem*/)
+{
+ if (keep_paths) {
+ processObjects(LPE_TO_OBJECTS);
+ return;
+ }
+ processObjects(LPE_ERASE);
+}
+
+void
+LPEMirrorSymmetry::doOnApply (SPLPEItem const* lpeitem)
+{
+ 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.max(), 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);
+ previous_center = center_point;
+ //we bump to 1.1 because previous 1.0.2 take no effect because a bug on 1.0.2
+ lpeversion.param_setValue("1.2", true);
+ lpesatellites.update_satellites();
+}
+
+Geom::PathVector
+LPEMirrorSymmetry::doEffect_path (Geom::PathVector const & path_in)
+{
+ if (split_items && !fuse_paths) {
+ return path_in;
+ }
+ Geom::PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(path_in);
+ Geom::PathVector path_out;
+
+ if (!discard_orig_path && !fuse_paths) {
+ path_out = pathv_to_linear_and_cubic_beziers(path_in);
+ }
+
+ Geom::Line line_separation((Geom::Point)start_point, (Geom::Point)end_point);
+ Geom::Affine m = Geom::reflection (line_separation.vector(), (Geom::Point)start_point);
+ if (fuse_paths && !discard_orig_path) {
+ for (const auto & path_it : original_pathv)
+ {
+ 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);
+ }
+ Geom::Point s = start_point;
+ Geom::Point e = end_point;
+ double dir = line_separation.angle();
+ 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_point, bbox) + diagonal;
+ s = Geom::Point::polar(dir,size_divider) + center_point;
+ e = Geom::Point::polar(dir + Geom::rad_from_deg(180),size_divider) + center_point;
+ Geom::Path divider = Geom::Path(s);
+ divider.appendNew<Geom::LineSegment>(e);
+ Geom::Crossings cs = crossings(original, divider);
+ std::vector<double> crossed;
+ for(auto & c : cs) {
+ crossed.push_back(c.ta);
+ }
+ std::sort(crossed.begin(), crossed.end());
+ for (unsigned int i = 0; i < crossed.size(); i++) {
+ double time_end = crossed[i];
+ 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 (!oposite_fuse) {
+ position *= -1;
+ }
+ if (position == 1) {
+ if (!split_items) {
+ Geom::Path mirror = portion.reversed() * m;
+ mirror.setInitial(portion.finalPoint());
+ portion.append(mirror);
+ if(i != 0) {
+ portion.setFinal(portion.initialPoint());
+ portion.close();
+ }
+ }
+ tmp_pathvector.push_back(portion);
+ }
+ portion.clear();
+ }
+ }
+ time_start = time_end;
+ }
+ position = Geom::sgn(Geom::cross(e - s, original.finalPoint() - s));
+ if (!oposite_fuse) {
+ 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()) {
+ portion = portion.reversed();
+ if (!split_items) {
+ Geom::Path mirror = portion.reversed() * m;
+ mirror.setInitial(portion.finalPoint());
+ portion.append(mirror);
+ }
+ portion = portion.reversed();
+ if (!original.closed()) {
+ tmp_pathvector.push_back(portion);
+ } else {
+ if (cs.size() > 1 && tmp_pathvector.size() > 0 && tmp_pathvector[0].size() > 0 ) {
+ if (!split_items) {
+ portion.setFinal(tmp_pathvector[0].initialPoint());
+ portion.setInitial(tmp_pathvector[0].finalPoint());
+ } else {
+ tmp_pathvector[0] = tmp_pathvector[0].reversed();
+ portion = portion.reversed();
+ portion.setInitial(tmp_pathvector[0].finalPoint());
+ }
+ tmp_pathvector[0].append(portion);
+ } else {
+ tmp_pathvector.push_back(portion);
+ }
+ if (lpeversion.param_getSVGValue() < "1.1") {
+ tmp_pathvector[0].close();
+ }
+ }
+ portion.clear();
+ }
+ }
+ }
+ if (!split_open && lpeversion.param_getSVGValue() >= "1.1" && original.closed()) {
+ for (auto &path : tmp_pathvector) {
+ if (!path.closed()) {
+ path.close();
+ }
+ }
+ sp_flatten(tmp_pathvector, fill_oddEven);
+ }
+ if (cs.size() == 0 && position == 1) {
+ tmp_pathvector.push_back(original);
+ if (!split_items) {
+ tmp_pathvector.push_back(original * m);
+ }
+ }
+ path_out.insert(path_out.end(), tmp_pathvector.begin(), tmp_pathvector.end());
+ tmp_pathvector.clear();
+ }
+ } else if (!fuse_paths || discard_orig_path) {
+ for (const auto & i : original_pathv) {
+ path_out.push_back(i * m);
+ }
+ }
+ return path_out;
+}
+
+void
+LPEMirrorSymmetry::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 :