1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /** \file
3  * LPE <slice> implementation: slices a path with respect to a given line.
4  */
5 /*
6  * Authors:
7  *   Maximilian Albert
8  *   Johan Engelen
9  *   Abhishek Sharma
10  *   Jabiertxof
11  *
12  * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
13  * Copyright (C) Maximilin Albert 2008 <maximilian.albert@gmail.com>
14  *
15  * Released under GNU GPL v2+, read the file 'COPYING' for more information.
16  */
17 
18 #include "live_effects/lpe-slice.h"
19 #include "2geom/affine.h"
20 #include "2geom/path-intersection.h"
21 #include "display/curve.h"
22 #include "helper/geom.h"
23 #include "path-chemistry.h"
24 #include "style.h"
25 #include "svg/path-string.h"
26 #include "svg/svg.h"
27 #include <gtkmm.h>
28 #include "path/path-boolop.h"
29 
30 #include "object/sp-defs.h"
31 #include "object/sp-lpe-item.h"
32 #include "object/sp-path.h"
33 #include "object/sp-text.h"
34 
35 #include "xml/sp-css-attr.h"
36 
37 // this is only to flatten nonzero fillrule
38 #include "livarot/Path.h"
39 #include "livarot/Shape.h"
40 
41 // TODO due to internal breakage in glibmm headers, this must be last:
42 #include <glibmm/i18n.h>
43 
44 typedef FillRule FillRuleFlatten;
45 
46 namespace Inkscape {
47 namespace LivePathEffect {
LPESlice(LivePathEffectObject * lpeobject)48 LPESlice::LPESlice(LivePathEffectObject *lpeobject) :
49     Effect(lpeobject),
50     allow_transforms(_("Allow Transforms"), _("Allow transforms"), "allow_transforms", &wr, this, true),
51     start_point(_("Slice line start"), _("Start point of slice line"), "start_point", &wr, this, _("Adjust start point of slice line")),
52     end_point(_("Slice line end"), _("End point of slice line"), "end_point", &wr, this, _("Adjust end point of slice line")),
53     center_point(_("Slice line mid"), _("Center point of slice line"), "center_point", &wr, this, _("Adjust center point of slice line"))
54 {
55     show_orig_path = true;
56     registerParameter(&allow_transforms);
57     registerParameter(&start_point);
58     registerParameter(&end_point);
59     registerParameter(&center_point);
60     apply_to_clippath_and_mask = false;
61     previous_center = Geom::Point(0,0);
62     center_point.param_widget_is_visible(false);
63     reset = false;
64     center_horiz = false;
65     center_vert = false;
66     allow_transforms_prev = allow_transforms;
67     on_remove_all = false;
68     parentlpe = nullptr;
69 }
70 
71 LPESlice::~LPESlice()
72 = default;
73 
74 
75 Gtk::Widget *
newWidget()76 LPESlice::newWidget()
77 {
78     // use manage here, because after deletion of Effect object, others might
79     // still be pointing to this widget.
80     Gtk::Box *vbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL));
81 
82     vbox->set_border_width(5);
83     vbox->set_homogeneous(false);
84     vbox->set_spacing(2);
85     Gtk::Box *hbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 0));
86     Gtk::Button *center_vert_button = Gtk::manage(new Gtk::Button(Glib::ustring(_("Vertical"))));
87     center_vert_button->signal_clicked().connect(sigc::mem_fun(*this, &LPESlice::centerVert));
88     center_vert_button->set_size_request(110, 20);
89     Gtk::Button *center_horiz_button = Gtk::manage(new Gtk::Button(Glib::ustring(_("Horizontal"))));
90     center_horiz_button->signal_clicked().connect(sigc::mem_fun(*this, &LPESlice::centerHoriz));
91     center_horiz_button->set_size_request(110, 20);
92     Gtk::Button *reset_button = Gtk::manage(new Gtk::Button(Glib::ustring(_("Reset styles"))));
93     reset_button->signal_clicked().connect(sigc::mem_fun(*this, &LPESlice::resetStyles));
94     reset_button->set_size_request(110, 20);
95 
96     vbox->pack_start(*hbox, true, true, 2);
97     hbox->pack_start(*reset_button, false, false, 2);
98     hbox->pack_start(*center_vert_button, false, false, 2);
99     hbox->pack_start(*center_horiz_button, false, false, 2);
100     std::vector<Parameter *>::iterator it = param_vector.begin();
101     while (it != param_vector.end()) {
102         if ((*it)->widget_is_visible) {
103             Parameter *param = *it;
104             Gtk::Widget *widg = dynamic_cast<Gtk::Widget *>(param->param_newWidget());
105             Glib::ustring *tip = param->param_getTooltip();
106             if (widg) {
107                 vbox->pack_start(*widg, true, true, 2);
108                 if (tip) {
109                     widg->set_tooltip_text(*tip);
110                 } else {
111                     widg->set_tooltip_text("");
112                     widg->set_has_tooltip(false);
113                 }
114             }
115         }
116 
117         ++it;
118     }
119     if (Gtk::Widget *widg = defaultParamSet()) {
120         vbox->pack_start(*widg, true, true, 2);
121     }
122     return dynamic_cast<Gtk::Widget *>(vbox);
123 }
124 
125 
126 void
centerVert()127 LPESlice::centerVert(){
128     center_vert = true;
129     refresh_widgets = true;
130     std::vector<SPLPEItem *> lpeitems = getCurrrentLPEItems();
131     if (lpeitems.size() == 1) {
132         sp_lpe_item = lpeitems[0];
133         sp_lpe_item_update_patheffect(sp_lpe_item, false, false);
134     }
135 }
136 
137 void
centerHoriz()138 LPESlice::centerHoriz(){
139     center_horiz = true;
140     refresh_widgets = true;
141     std::vector<SPLPEItem *> lpeitems = getCurrrentLPEItems();
142     if (lpeitems.size() == 1) {
143         sp_lpe_item = lpeitems[0];
144         sp_lpe_item_update_patheffect(sp_lpe_item, false, false);
145     }
146 }
147 
sp_has_path_data(SPItem * item,bool originald)148 bool sp_has_path_data(SPItem *item, bool originald)
149 {
150     SPGroup *group = dynamic_cast<SPGroup *>(item);
151     if (group) {
152         std::vector<SPObject *> childs = group->childList(true);
153         for (auto &child : childs) {
154             SPItem *item = dynamic_cast<SPItem *>(child);
155             if (sp_has_path_data(item, originald)) {
156                 return true;
157             }
158         }
159     }
160     SPShape *shape = dynamic_cast<SPShape *>(item);
161     if (shape) {
162         SPCurve const *c = shape->curve();
163         if (c && !c->is_empty()) {
164             return true;
165         }
166         if (originald) {
167             if (shape->hasPathEffectRecursive()) {
168                 SPCurve const *c = shape->curveBeforeLPE();
169                 if (c && !c->is_empty()) {
170                     return true;
171                 }
172             }
173         }
174     }
175     return false;
176 }
177 /*
178  * Allow chage original-d to d to "reset" temporary the LPE
179  * when the slice dont pass trought item till sp_lpe_item is crossed
180  */
181 void
originalDtoD(SPShape const * shape,SPCurve * curve)182 LPESlice::originalDtoD(SPShape const *shape, SPCurve *curve)
183 {
184     SPCurve const *c = shape->curveBeforeLPE();
185     if (c && !c->is_empty()) {
186         curve->set_pathvector(c->get_pathvector());
187     }
188 }
189 
190 /*
191  * Allow chage original-d to d to "reset" temporary the LPE
192  * when the slice dont pass trought item till sp_lpe_item is crossed
193  */
194 void
originalDtoD(SPItem * item)195 LPESlice::originalDtoD(SPItem *item)
196 {
197     SPGroup *group = dynamic_cast<SPGroup *>(item);
198     if (group) {
199         std::vector<SPObject *> childs = group->childList(true);
200         for (auto &child : childs) {
201             SPItem *item = dynamic_cast<SPItem *>(child);
202             originalDtoD(item);
203         }
204         return;
205     }
206     SPShape *shape = dynamic_cast<SPShape *>(item);
207     if (shape) {
208         SPCurve const *c = shape->curveBeforeLPE();
209         if (c && !c->is_empty()) {
210             shape->bbox_vis_cache_is_valid = false;
211             shape->bbox_geom_cache_is_valid = false;
212             shape->setCurveInsync(std::move(c));
213             auto str = sp_svg_write_path(c->get_pathvector());
214             shape->setAttribute("d", str);
215         }
216     }
217 }
218 
reloadOriginal(SPLPEItem const * lpeitem)219 void LPESlice::reloadOriginal(SPLPEItem const* lpeitem)
220 {
221     SPLPEItem *originallpeitem = getOriginal(lpeitem);
222     if (originallpeitem) {
223         is_applied = false;
224         sp_lpe_item_update_patheffect(originallpeitem, false, true);
225     }
226 }
227 
getOriginal(SPLPEItem const * lpeitem)228 SPLPEItem *LPESlice::getOriginal(SPLPEItem const* lpeitem)
229 {
230     SPLPEItem *lpeparent = nullptr;
231     if (lpeitem->getAttribute("class")) {
232         gchar **strarray = g_strsplit(lpeitem->getAttribute("class"), " ", 0);
233         gchar **iter = strarray;
234         while (*iter != nullptr) {
235             Glib::ustring classsplited = *iter;
236             size_t pos = classsplited.rfind("-slice");
237             if (pos != std::string::npos) {
238                 classsplited = classsplited.replace(pos, 6, "");
239                 lpeparent = dynamic_cast<SPLPEItem *>(getSPDoc()->getObjectById(classsplited));
240                 if (lpeparent && lpeitem != lpeparent) {
241                     g_strfreev(strarray);
242                     return lpeparent;
243                 }
244             }
245             iter++;
246         }
247         g_strfreev(strarray);
248     }
249     return lpeparent;
250 }
251 
allowreset(gpointer data)252 gboolean allowreset(gpointer data)
253 {
254     LPESlice *slice = reinterpret_cast<LPESlice *>(data);
255     sp_lpe_item_update_patheffect(slice->sp_lpe_item, false, false);
256     return FALSE;
257 }
258 
delayupdate(gpointer data)259 gboolean delayupdate(gpointer data)
260 {
261     LPESlice *slice = reinterpret_cast<LPESlice *>(data);
262     sp_lpe_item_update_patheffect(slice->sp_lpe_item, false, false);
263     return FALSE;
264 }
265 
266 void
doAfterEffect(SPLPEItem const * lpeitem,SPCurve * curve)267 LPESlice::doAfterEffect (SPLPEItem const* lpeitem, SPCurve *curve)
268 {
269     bool cleanup = is_load;
270     if (is_applied) {
271         cleanup = true;
272         reloadOriginal(lpeitem);
273     }
274     LPESlice *nextslice = dynamic_cast<LPESlice *>(sp_lpe_item->getNextLPE(this));
275     if (!nextslice || !nextslice->is_visible) {
276         if (boundingbox_X.isSingular() || boundingbox_Y.isSingular()) {
277             return;
278         }
279         std::vector<SPLPEItem *> lpeitems = getCurrrentLPEItems();
280         if (lpeitems.size() != 1) {
281             return;
282         }
283         Glib::ustring theclass = lpeitem->getId();
284         theclass += "-slice";
285         //ungroup
286         if (!is_load && parentlpe && parentlpe != sp_lpe_item->parent && parentlpe != sp_lpe_item->parent->parent) {
287             parentlpe = sp_lpe_item->parent;
288             g_timeout_add(250, &delayupdate, this);
289             return;
290         } else if (!is_load && parentlpe && parentlpe != sp_lpe_item->parent) { // group
291             g_timeout_add(250, &allowreset, this);
292             cleanup = true;
293         }
294         parentlpe = sp_lpe_item->parent;
295         items.clear();
296         std::vector<std::pair<Geom::Line, size_t> > slicer = getSplitLines();
297         if (!slicer.size()) {
298             return;
299         }
300         for (auto item : getSPDoc()->getObjectsByClass(theclass)) {
301             SPItem *extraitem = dynamic_cast<SPItem *>(item);
302             if (extraitem) {
303                 extraitem->setHidden("true");
304             }
305         }
306         split(sp_lpe_item, curve, slicer, 0);
307         std::vector<Glib::ustring> newitemstmp;
308         newitemstmp.assign(items.begin(), items.end());
309         items.clear();
310         bool maindata = sp_has_path_data(sp_lpe_item, false);
311         if (!maindata) {
312             Glib::ustring theclass = lpeitem->getId();
313             theclass += "-slice";
314             for (auto item : getSPDoc()->getObjectsByClass(theclass)) {
315                 SPLPEItem *splpeitem = dynamic_cast<SPLPEItem *>(item);
316                 splpeitem->setHidden(true);
317                 sp_lpe_item_update_patheffect(splpeitem, false, false);
318             }
319             if (!curve) { // group
320                 originalDtoD(sp_lpe_item);
321             } else {
322                 originalDtoD(getCurrentShape(), curve);
323             }
324             return;
325         }
326         bool hidden = sp_lpe_item->isHidden();
327         for (auto item: newitemstmp) {
328             SPItem *spitem = dynamic_cast<SPItem *>(getSPDoc()->getObjectById(item.c_str()));
329             SPLPEItem *splpeitem = dynamic_cast<SPLPEItem *>(spitem);
330             if (hidden) {
331                 splpeitem->setHidden("true");
332             }
333             if (spitem && sp_has_path_data(spitem, false)) {
334                 items.push_back(item);
335                 sp_lpe_item_update_patheffect(splpeitem, false, false);
336             }
337         }
338         for (auto item : getSPDoc()->getObjectsByClass(theclass)) {
339             SPItem *extraitem = dynamic_cast<SPItem *>(item);
340             if (extraitem) {
341                 SPLPEItem *spitem = dynamic_cast<SPLPEItem *>(extraitem);
342                 if (spitem && !sp_has_path_data(spitem, false)) {
343                     if (cleanup) {
344                         sp_lpe_item_update_patheffect(spitem, false, false);
345                         spitem->deleteObject(true);
346                     } else {
347                         originalDtoD(spitem);
348                     }
349                 } else {
350                     SPLPEItem *splpeitem = dynamic_cast<SPLPEItem *>(extraitem);
351                     if (splpeitem && splpeitem->hasPathEffectOfType(SLICE)) {
352                         sp_lpe_item_update_patheffect(splpeitem, false, false);
353                     }
354                 }
355             }
356         }
357         reset = false;
358     }
359 }
360 
361 void
split(SPItem * item,SPCurve * curve,std::vector<std::pair<Geom::Line,size_t>> slicer,size_t splitindex)362 LPESlice::split(SPItem* item, SPCurve *curve, std::vector<std::pair<Geom::Line, size_t> > slicer, size_t splitindex) {
363     SPDocument *document = getSPDoc();
364     if (!document) {
365         return;
366     }
367     Glib::ustring elemref_id = Glib::ustring("slice-");
368     elemref_id += Glib::ustring::format(slicer[splitindex].second);
369     elemref_id += "-";
370     Glib::ustring clean_id = item->getId();
371     SPLPEItem *lpeitem = dynamic_cast<SPLPEItem *>(item);
372     if (!lpeitem) {
373         return;
374     }
375     //First check is to allow effects on "satellittes"
376     if (!lpeitem->hasPathEffectOfType(SLICE) && clean_id.find("slice-") != Glib::ustring::npos) {
377         clean_id = clean_id.replace(0,6,"");
378         elemref_id += clean_id;
379     } else {
380         elemref_id += clean_id;
381     }
382 
383     items.push_back(elemref_id);
384 
385     SPObject *elemref = getSPDoc()->getObjectById(elemref_id.c_str());
386     bool prevreset = reset;
387     if (!elemref) {
388         reset = true;
389         Inkscape::XML::Node *phantom = createPathBase(item);
390         phantom->setAttribute("id", elemref_id);
391         Glib::ustring classes = sp_lpe_item->getId();
392         classes += "-slice UnoptimicedTransforms";
393         phantom->setAttribute("class", classes.c_str());
394         elemref = parentlpe->appendChildRepr(phantom);
395         Inkscape::GC::release(phantom);
396         parentlpe->reorder(elemref, sp_lpe_item);
397     }
398     Inkscape::XML::Document *xml_doc = getSPDoc()->getReprDoc();
399     if (elemref && elemref->parent != parentlpe) {
400         Inkscape::XML::Node *repr = elemref->getRepr();
401         Inkscape::XML::Node *copy = repr->duplicate(xml_doc);
402         if (copy) {
403             parentlpe->addChild(copy, sp_lpe_item->getRepr());
404             // Retrieve the SPItem of the resulting repr.
405             SPObject *sucessor = document->getObjectByRepr(copy);
406             if (sucessor) {
407                 sp_object_ref(elemref);
408                 Inkscape::GC::anchor(repr);
409                 elemref->deleteObject(false);
410                 sucessor->setAttribute("id", elemref_id);
411                 Inkscape::GC::release(repr);
412                 elemref->setSuccessor(sucessor);
413                 sp_object_unref(elemref);
414                 elemref = dynamic_cast<SPItem *>(sucessor);
415                 g_assert(item != nullptr);
416             }
417         }
418     }
419     SPItem *other = dynamic_cast<SPItem *>(elemref);
420     if (other) {
421         other->setHidden(false);
422         size_t nsplits = slicer.size();
423         if (nsplits) {
424             cloneD(item, other, false);
425             reset = prevreset;
426             splititem(item, curve, slicer[splitindex], true);
427             splititem(other, nullptr, slicer[splitindex], false);
428             splitindex++;
429             if (nsplits > splitindex) {
430                 SPLPEItem *splpeother = dynamic_cast<SPLPEItem *>(other);
431                 SPLPEItem *splpeitem = dynamic_cast<SPLPEItem *>(item);
432                 if (item == sp_lpe_item || !splpeitem->hasPathEffectOfType(SLICE)) {
433                     split(item, curve, slicer, splitindex);
434                     if (other == sp_lpe_item || !splpeother->hasPathEffectOfType(SLICE)) {
435                         split(other, nullptr, slicer, splitindex);
436                     }
437                 }
438             }
439         }
440     }
441 }
442 
443 std::vector<std::pair<Geom::Line, size_t> >
getSplitLines()444 LPESlice::getSplitLines() {
445     std::vector<std::pair<Geom::Line, size_t> > splitlines;
446     LPESlice *prevslice = dynamic_cast<LPESlice *>(sp_lpe_item->getPrevLPE(this));
447     if (prevslice) {
448         splitlines = prevslice->getSplitLines();
449     }
450     if (isVisible()) {
451         Geom::Line line_separation((Geom::Point)start_point, (Geom::Point)end_point);
452         size_t index = sp_lpe_item->getLPEIndex(this);
453         std::pair<Geom::Line, size_t> slice = std::make_pair(line_separation, index);
454         splitlines.push_back(slice);
455     }
456     return splitlines;
457 }
458 
459 Inkscape::XML::Node *
createPathBase(SPObject * elemref)460 LPESlice::createPathBase(SPObject *elemref) {
461     SPDocument *document = getSPDoc();
462     if (!document) {
463         return nullptr;
464     }
465     Inkscape::XML::Document *xml_doc = getSPDoc()->getReprDoc();
466     Inkscape::XML::Node *prev = elemref->getRepr();
467     SPGroup *group = dynamic_cast<SPGroup *>(elemref);
468     if (group) {
469         Inkscape::XML::Node *container = xml_doc->createElement("svg:g");
470         container->setAttribute("transform", prev->attribute("transform"));
471         container->setAttribute("mask", prev->attribute("mask"));
472         container->setAttribute("clip-path", prev->attribute("clip-path"));
473         std::vector<SPItem*> const item_list = sp_item_group_item_list(group);
474         Inkscape::XML::Node *previous = nullptr;
475         for (auto sub_item : item_list) {
476             Inkscape::XML::Node *resultnode = createPathBase(sub_item);
477             container->addChild(resultnode, previous);
478             previous = resultnode;
479         }
480         return container;
481     }
482     Inkscape::XML::Node *resultnode = xml_doc->createElement("svg:path");
483     resultnode->setAttribute("transform", prev->attribute("transform"));
484     resultnode->setAttribute("mask", prev->attribute("mask"));
485     resultnode->setAttribute("clip-path", prev->attribute("clip-path"));
486     return resultnode;
487 }
488 
489 void
cloneD(SPObject * orig,SPObject * dest,bool is_original)490 LPESlice::cloneD(SPObject *orig, SPObject *dest, bool is_original)
491 {
492     if (!is_original && !g_strcmp0(sp_lpe_item->getId(), orig->getId())) {
493         is_original = true;
494     }
495     SPDocument *document = getSPDoc();
496     if (!document) {
497         return;
498     }
499     SPItem *originalitem = dynamic_cast<SPItem *>(orig);
500     if ( SP_IS_GROUP(orig) && SP_IS_GROUP(dest) && SP_GROUP(orig)->getItemCount() == SP_GROUP(dest)->getItemCount() ) {
501         if (reset) {
502             cloneStyle(orig, dest);
503         }
504         if (!allow_transforms) {
505             auto str = sp_svg_transform_write(originalitem->transform);
506             dest->setAttributeOrRemoveIfEmpty("transform", str);
507         }
508         std::vector< SPObject * > childs = orig->childList(true);
509         size_t index = 0;
510         for (auto &child : childs) {
511             SPObject *dest_child = dest->nthChild(index);
512             cloneD(child, dest_child, is_original);
513             index++;
514         }
515         return;
516     }
517 
518     SPShape * shape =  SP_SHAPE(orig);
519     SPPath * path =  SP_PATH(dest);
520     SPLPEItem *splpeitem = dynamic_cast<SPLPEItem *>(path);
521     if (path && shape && splpeitem) {
522         SPCurve const *c;
523         if (!is_original && shape->hasPathEffectRecursive()) {
524             c = shape->curve();
525         } else {
526             c = shape->curve();
527         }
528         if (c && !c->is_empty()) {
529             auto str = sp_svg_write_path(c->get_pathvector());
530             if (path->hasPathEffectRecursive()) {
531                 sp_lpe_item_enable_path_effects(path, false);
532                 dest->setAttribute("inkscape:original-d", str);
533                 sp_lpe_item_enable_path_effects(path, true);
534                 dest->setAttribute("d", str);
535             } else {
536                 dest->setAttribute("d", str);
537             }
538             if (!allow_transforms) {
539                 auto str = sp_svg_transform_write(originalitem->transform);
540                 dest->setAttributeOrRemoveIfEmpty("transform", str);
541             }
542             if (reset) {
543                 cloneStyle(orig, dest);
544             }
545         }
546     }
547 }
548 
549 static void
sp_flatten(Geom::PathVector & pathvector,FillRuleFlatten fillkind)550 sp_flatten(Geom::PathVector &pathvector, FillRuleFlatten fillkind)
551 {
552     Path *orig = new Path;
553     orig->LoadPathVector(pathvector);
554     Shape *theShape = new Shape;
555     Shape *theRes = new Shape;
556     orig->ConvertWithBackData (1.0);
557     orig->Fill (theShape, 0);
558     theRes->ConvertToShape (theShape, FillRule(fillkind));
559     Path *originaux[1];
560     originaux[0] = orig;
561     Path *res = new Path;
562     theRes->ConvertToForme (res, 1, originaux, true);
563 
564     delete theShape;
565     delete theRes;
566     char *res_d = res->svg_dump_path ();
567     delete res;
568     delete orig;
569     pathvector  = sp_svg_read_pathv(res_d);
570 }
571 
GetFillTyp(SPItem * item)572 static fill_typ GetFillTyp(SPItem *item)
573 {
574     SPCSSAttr *css = sp_repr_css_attr(item->getRepr(), "style");
575     gchar const *val = sp_repr_css_property(css, "fill-rule", nullptr);
576     if (val && strcmp(val, "nonzero") == 0) {
577         return fill_nonZero;
578     } else if (val && strcmp(val, "evenodd") == 0) {
579         return fill_oddEven;
580     } else {
581         return fill_nonZero;
582     }
583 }
584 
585 void
splititem(SPItem * item,SPCurve * curve,std::pair<Geom::Line,size_t> slicer,bool toggle,bool is_original)586 LPESlice::splititem(SPItem* item, SPCurve * curve, std::pair<Geom::Line, size_t> slicer, bool toggle, bool is_original)
587 {
588     if (!is_original && !g_strcmp0(sp_lpe_item->getId(), item->getId())) {
589         is_original = true;
590     }
591     Geom::Line line_separation = slicer.first;
592     // check top level split/sp_lpe_item item
593     SPObject *top = sp_lpe_item->parent;
594     SPObject *other = item;
595     while (other && other->parent) {
596         if (other->parent != top) {
597             other = other->parent;
598         } else {
599             break;
600         }
601     }
602     SPItem *topitem = dynamic_cast<SPItem *>(other);
603     if (topitem && topitem != item) {
604         Geom::Affine ptransform = item->getRelativeTransform(topitem);
605         ptransform *= item->document->doc2dt();
606         line_separation *= ptransform.inverse();
607     }
608     Geom::Point s = line_separation.initialPoint();
609     Geom::Point e = line_separation.finalPoint();
610     Geom::Point center = Geom::middle_point(s, e);
611     SPGroup *group = dynamic_cast<SPGroup *>(item);
612     if (group) {
613         std::vector<SPObject *> childs = group->childList(true);
614         for (auto &child : childs) {
615             SPItem *dest_child = dynamic_cast<SPItem *>(child);
616             // groups not need update curve
617             splititem(dest_child, nullptr, slicer, toggle, is_original);
618         }
619         if (!is_original && group->hasPathEffectRecursive()) {
620             sp_lpe_item_update_patheffect(group, false, false);
621         }
622         return;
623     }
624     SPShape *shape = dynamic_cast<SPShape *>(item);
625     if (shape) {
626         SPCurve const *c;
627         c = shape->curve();
628         if (c) {
629             Geom::PathVector original_pathv = pathv_to_linear_and_cubic_beziers(c->get_pathvector());
630             sp_flatten(original_pathv, GetFillTyp(shape));
631             Geom::PathVector path_out;
632             for (const auto & path_it : original_pathv) {
633                 if (path_it.empty()) {
634                     continue;
635                 }
636                 Geom::PathVector tmp_pathvector;
637                 double time_start = 0.0;
638                 int position = 0;
639                 bool end_open = false;
640                 if (path_it.closed()) {
641                     const Geom::Curve &closingline = path_it.back_closed();
642                     if (!are_near(closingline.initialPoint(), closingline.finalPoint())) {
643                         end_open = true;
644                     }
645                 }
646                 Geom::Path original = path_it;
647                 if (end_open && path_it.closed()) {
648                     original.close(false);
649                     original.appendNew<Geom::LineSegment>( original.initialPoint() );
650                     original.close(true);
651                 }
652                 double dir = line_separation.angle();
653                 Geom::Ray ray = line_separation.ray(0);
654                 double diagonal = Geom::distance(Geom::Point(boundingbox_X.min(),boundingbox_Y.min()),Geom::Point(boundingbox_X.max(),boundingbox_Y.max()));
655                 Geom::Rect bbox(Geom::Point(boundingbox_X.min(),boundingbox_Y.min()),Geom::Point(boundingbox_X.max(),boundingbox_Y.max()));
656                 double size_divider = Geom::distance(center, bbox) + diagonal;
657                 s = Geom::Point::polar(dir,size_divider) + center;
658                 e = Geom::Point::polar(dir + Geom::rad_from_deg(180),size_divider) + center;
659                 Geom::Path divider = Geom::Path(s);
660                 divider.appendNew<Geom::LineSegment>(e);
661                 std::vector<double> crossed;
662                 if (Geom::are_near(s,e)) {
663                     continue;
664                 }
665                 Geom::Crossings cs = crossings(original, divider);
666                 for(auto & c : cs) {
667                     crossed.push_back(c.ta);
668                 }
669                 double angle = Geom::deg_from_rad(ray.angle());
670                 bool toggleside = !(angle > 0 && angle < 180);
671                 std::sort(crossed.begin(), crossed.end());
672                 for (double time_end : crossed) {
673                     if (time_start != time_end && time_end - time_start > Geom::EPSILON) {
674                         Geom::Path portion = original.portion(time_start, time_end);
675                         if (!portion.empty()) {
676                             Geom::Point middle = portion.pointAt((double)portion.size()/2.0);
677                             position = Geom::sgn(Geom::cross(e - s, middle - s));
678                             if (toggleside) {
679                                 position *= -1;
680                             }
681                             if (toggle) {
682                                 position *= -1;
683                             }
684                             if (position == 1) {
685                                 tmp_pathvector.push_back(portion);
686                             }
687                             portion.clear();
688                         }
689                     }
690                     time_start = time_end;
691                 }
692                 position = Geom::sgn(Geom::cross(e - s, original.finalPoint() - s));
693                 if (toggleside) {
694                     position *= -1;
695                 }
696                 if (toggle) {
697                     position *= -1;
698                 }
699                 if (cs.size()!=0 && (position == 1)) {
700                     if (time_start != original.size() && original.size() - time_start > Geom::EPSILON) {
701                         Geom::Path portion = original.portion(time_start, original.size());
702                         if (!portion.empty()) {
703                             if (!original.closed()) {
704                                 tmp_pathvector.push_back(portion);
705                             } else {
706                                 if (cs.size() > 1 && tmp_pathvector.size() > 0 && tmp_pathvector[0].size() > 0 ) {
707                                     tmp_pathvector[0] = tmp_pathvector[0].reversed();
708                                     portion = portion.reversed();
709                                     portion.setInitial(tmp_pathvector[0].finalPoint());
710                                     tmp_pathvector[0].append(portion);
711                                     tmp_pathvector[0] = tmp_pathvector[0].reversed();
712                                 } else {
713                                     tmp_pathvector.push_back(portion);
714                                 }
715                             }
716                             portion.clear();
717                         }
718                     }
719                 }
720                 if (cs.size() > 0 && original.closed()) {
721                     for (auto &path : tmp_pathvector) {
722                         if (!path.closed()) {
723                             path.close();
724                         }
725                     }
726                 }
727                 if (cs.size() == 0 && position == 1) {
728                    tmp_pathvector.push_back(original);
729                 }
730                 path_out.insert(path_out.end(), tmp_pathvector.begin(), tmp_pathvector.end());
731                 tmp_pathvector.clear();
732             }
733             if (curve && is_original) {
734                 curve->set_pathvector(path_out);
735             }
736             auto cpro = SPCurve::copy(shape->curve());
737             if (cpro) {
738                 shape->bbox_vis_cache_is_valid = false;
739                 shape->bbox_geom_cache_is_valid = false;
740                 cpro->set_pathvector(path_out);
741                 shape->setCurveInsync(std::move(cpro));
742                 auto str = sp_svg_write_path(path_out);
743                 if (!is_original && shape->hasPathEffectRecursive()) {
744                     sp_lpe_item_enable_path_effects(shape, false);
745                     shape->setAttribute("inkscape:original-d", str);
746                     sp_lpe_item_enable_path_effects(shape, true);
747                 } else {
748                     shape->setAttribute("d", str);
749                 }
750             }
751         }
752     }
753 }
754 
755 void
doBeforeEffect(SPLPEItem const * lpeitem)756 LPESlice::doBeforeEffect (SPLPEItem const* lpeitem)
757 {
758     SPDocument *document = getSPDoc();
759     if (!document) {
760         return;
761     }
762     using namespace Geom;
763     original_bbox(lpeitem, false, true);
764     Point point_a(boundingbox_X.max(), boundingbox_Y.min());
765     Point point_b(boundingbox_X.max(), boundingbox_Y.max());
766     Point point_c(boundingbox_X.middle(), boundingbox_Y.middle());
767     if (center_vert) {
768         double dista = std::abs(end_point[Geom::Y] - boundingbox_Y.min());
769         double distb = std::abs(start_point[Geom::Y] - boundingbox_Y.min());
770         previous_center = Geom::Point(Geom::infinity(), g_random_double_range(0, 1000));
771         end_point.param_setValue(
772             Geom::Point(center_point[Geom::X], dista <= distb ? boundingbox_Y.min() : boundingbox_Y.max()), true);
773         start_point.param_setValue(
774             Geom::Point(center_point[Geom::X], dista > distb ? boundingbox_Y.min() : boundingbox_Y.max()), true);
775         //force update
776         center_vert = false;
777     } else if (center_horiz) {
778         double dista = std::abs(end_point[Geom::X] - boundingbox_X.min());
779         double distb = std::abs(start_point[Geom::X] - boundingbox_X.min());
780         previous_center = Geom::Point(Geom::infinity(), g_random_double_range(0, 1000));
781         end_point.param_setValue(
782             Geom::Point(dista <= distb ? boundingbox_X.min() : boundingbox_X.max(), center_point[Geom::Y]), true);
783         start_point.param_setValue(
784             Geom::Point(dista > distb ? boundingbox_X.min() : boundingbox_X.max(), center_point[Geom::Y]), true);
785         //force update
786         center_horiz = false;
787     } else {
788         if ((Geom::Point)start_point == (Geom::Point)end_point) {
789             start_point.param_setValue(point_a);
790             end_point.param_setValue(point_b);
791             previous_center = Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point);
792             center_point.param_setValue(previous_center);
793             return;
794         }
795         if (are_near(previous_center, (Geom::Point)center_point, 0.01)) {
796             center_point.param_setValue(Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point));
797         } else {
798             Geom::Point trans = center_point - Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point);
799             start_point.param_setValue(start_point * trans);
800             end_point.param_setValue(end_point * trans);
801         }
802     }
803     if (allow_transforms_prev != allow_transforms) {
804         LPESlice *nextslice = dynamic_cast<LPESlice *>(sp_lpe_item->getNextLPE(this));
805         while (nextslice) {
806             if (nextslice->allow_transforms != allow_transforms) {
807                 nextslice->allow_transforms_prev = allow_transforms;
808                 nextslice->allow_transforms.param_setValue(allow_transforms);
809             }
810             nextslice = dynamic_cast<LPESlice *>(sp_lpe_item->getNextLPE(nextslice));
811         }
812         LPESlice *prevslice = dynamic_cast<LPESlice *>(sp_lpe_item->getPrevLPE(this));
813         while (prevslice) {
814             if (prevslice->allow_transforms != allow_transforms) {
815                 prevslice->allow_transforms_prev = allow_transforms;
816                 prevslice->allow_transforms.param_setValue(allow_transforms);
817             }
818             prevslice = dynamic_cast<LPESlice *>(sp_lpe_item->getNextLPE(prevslice));
819         }
820     }
821     allow_transforms_prev = allow_transforms;
822 }
823 
cloneStyle(SPObject * orig,SPObject * dest)824 void LPESlice::cloneStyle(SPObject *orig, SPObject *dest)
825 {
826     for (auto iter : orig->style->properties()) {
827         if (iter->style_src != SPStyleSrc::UNSET) {
828             auto key = iter->id();
829             if (key != SPAttr::FONT && key != SPAttr::D && key != SPAttr::MARKER) {
830                 const gchar *attr = orig->getAttribute(iter->name().c_str());
831                 if (attr) {
832                     dest->setAttribute(iter->name(), attr);
833                 }
834             }
835         }
836     }
837     dest->setAttribute("style", orig->getAttribute("style"));
838 }
839 
840 void
resetStyles()841 LPESlice::resetStyles(){
842     std::vector<SPLPEItem *> lpeitems = getCurrrentLPEItems();
843     if (lpeitems.size() == 1) {
844         sp_lpe_item = lpeitems[0];
845         LPESlice *nextslice = dynamic_cast<LPESlice *>(sp_lpe_item->getNextLPE(this));
846         while (nextslice) {
847             nextslice->reset = true;
848             nextslice = dynamic_cast<LPESlice *>(sp_lpe_item->getNextLPE(nextslice));
849         }
850         reset = true;
851         sp_lpe_item_update_patheffect(sp_lpe_item, false, false);
852     }
853 }
854 
855 //TODO: Migrate the tree next function to effect.cpp/h to avoid duplication
856 void
doOnVisibilityToggled(SPLPEItem const *)857 LPESlice::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/)
858 {
859     processObjects(LPE_VISIBILITY);
860 }
861 
862 
863 void
doOnRemove(SPLPEItem const * lpeitem)864 LPESlice::doOnRemove(SPLPEItem const* lpeitem)
865 {
866     items.clear();
867     std::vector<SPLPEItem *> lpeitems = getCurrrentLPEItems();
868     if (lpeitems.size() == 1) {
869         sp_lpe_item = lpeitems[0];
870         if (!sp_lpe_item->path_effects_enabled) {
871             return;
872         }
873         Glib::ustring theclass = sp_lpe_item->getId();
874         theclass += "-slice";
875         for (auto item : getSPDoc()->getObjectsByClass(theclass)) {
876             items.emplace_back(item->getId());
877         }
878         if (keep_paths) {
879             processObjects(LPE_TO_OBJECTS);
880             items.clear();
881             return;
882         }
883         if (sp_lpe_item->countLPEOfType(SLICE) == 1 || on_remove_all) {
884             processObjects(LPE_ERASE);
885         } else {
886             sp_lpe_item_update_patheffect(sp_lpe_item, false, false);
887         }
888     }
889 }
890 
891 void
doOnApply(SPLPEItem const * lpeitem)892 LPESlice::doOnApply (SPLPEItem const* lpeitem)
893 {
894     using namespace Geom;
895     original_bbox(lpeitem, false, true);
896     LPESlice *prevslice = dynamic_cast<LPESlice *>(sp_lpe_item->getPrevLPE(this));
897     if (prevslice) {
898         allow_transforms_prev = prevslice->allow_transforms;
899         allow_transforms.param_setValue(prevslice->allow_transforms);
900     }
901     Point point_a(boundingbox_X.middle(), boundingbox_Y.min());
902     Point point_b(boundingbox_X.middle(), boundingbox_Y.max());
903     Point point_c(boundingbox_X.middle(), boundingbox_Y.middle());
904     start_point.param_setValue(point_a, true);
905     start_point.param_update_default(point_a);
906     end_point.param_setValue(point_b, true);
907     end_point.param_update_default(point_b);
908     center_point.param_setValue(point_c, true);
909     end_point.param_update_default(point_c);
910     previous_center = center_point;
911 }
912 
913 
914 Geom::PathVector
doEffect_path(Geom::PathVector const & path_in)915 LPESlice::doEffect_path (Geom::PathVector const & path_in)
916 {
917     return path_in;
918 }
919 
920 void
addCanvasIndicators(SPLPEItem const *,std::vector<Geom::PathVector> & hp_vec)921 LPESlice::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec)
922 {
923     using namespace Geom;
924     hp_vec.clear();
925     Geom::Path path;
926     Geom::Point s = start_point;
927     Geom::Point e = end_point;
928     path.start( s );
929     path.appendNew<Geom::LineSegment>( e );
930     Geom::PathVector helper;
931     helper.push_back(path);
932     hp_vec.push_back(helper);
933 }
934 
935 } //namespace LivePathEffect
936 } /* namespace Inkscape */
937 
938 /*
939   Local Variables:
940   mode:c++
941   c-file-style:"stroustrup"
942   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
943   indent-tabs-mode:nil
944   fill-column:99
945   End:
946 */
947 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
948