Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
lpe-slice.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
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 "lpe-slice.h"
19
20#include <glibmm/i18n.h>
21#include <gtkmm/box.h>
22#include <gtkmm/button.h>
23
24#include <2geom/affine.h>
26
27#include "style.h"
28
29#include "display/curve.h"
30#include "helper/geom.h"
32#include "object/sp-defs.h"
34#include "object/sp-lpe-item.h"
35#include "object/sp-path.h"
36#include "object/sp-text.h"
37#include "path-chemistry.h"
38#include "path/path-boolop.h"
39#include "svg/path-string.h"
40#include "svg/svg.h"
41#include "ui/icon-names.h"
42#include "ui/pack.h"
44#include "xml/sp-css-attr.h"
45
46// this is only to flatten nonzero fillrule
47#include "livarot/Path.h"
48#include "livarot/Shape.h"
49
50namespace Inkscape {
51namespace LivePathEffect {
53 Effect(lpeobject),
54 // do not change name of this parameter us used in oncommit
55 lpesatellites(_("lpesatellites"), _("Items satellites"), "lpesatellites", &wr, this, false),
56 allow_transforms(_("Allow Transforms"), _("Allow transforms"), "allow_transforms", &wr, this, true),
57 start_point(_("Slice line start"), _("Start point of slice line"), "start_point", &wr, this, _("Adjust start point of slice line")),
58 end_point(_("Slice line end"), _("End point of slice line"), "end_point", &wr, this, _("Adjust end point of slice line")),
59 center_point(_("Slice line mid"), _("Center point of slice line"), "center_point", &wr, this, _("Adjust center point of slice line"))
60{
66 show_orig_path = true;
70 reset = false;
71 center_horiz = false;
72 center_vert = false;
74 on_remove_all = false;
75 container = nullptr;
77}
78
79LPESlice::~LPESlice() = default;
80
81bool
83 bool fixed = false;
84 Glib::ustring version = lpeversion.param_getSVGValue();
85 if (version < "1.2") {
86 std::vector<SPLPEItem *> lpeitems = getCurrrentLPEItems();
87 if (lpeitems.size() >= 1) {
88 sp_lpe_item_update_patheffect(lpeitems[0], false, true);
89 }
90 lpeversion.param_setValue("1.2", true);
91 fixed = true;
93 }
96 return fixed;
97}
98
99Gtk::Widget *
101{
102 // use manage here, because after deletion of Effect object, others might
103 // still be pointing to this widget.
104 auto const vbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 2);
105 vbox->set_margin(5);
106
107 auto const hbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 0);
108
109 auto const center_vert_button = Gtk::make_managed<Gtk::Button>(Glib::ustring(_("Vertical")));
110 center_vert_button->signal_clicked().connect(sigc::mem_fun(*this, &LPESlice::centerVert));
111 center_vert_button->set_size_request(110, 20);
112
113 auto const center_horiz_button = Gtk::make_managed<Gtk::Button>(Glib::ustring(_("Horizontal")));
114 center_horiz_button->signal_clicked().connect(sigc::mem_fun(*this, &LPESlice::centerHoriz));
115 center_horiz_button->set_size_request(110, 20);
116
117 auto const reset_button = Gtk::make_managed<Gtk::Button>(Glib::ustring(_("Reset styles")));
118 reset_button->signal_clicked().connect(sigc::mem_fun(*this, &LPESlice::resetStyles));
119 reset_button->set_size_request(110, 20);
120
121 UI::pack_start(*vbox, *hbox, true, true, 2);
122 UI::pack_start(*hbox, *reset_button, false, false, 2);
123 UI::pack_start(*hbox, *center_vert_button, false, false, 2);
124 UI::pack_start(*hbox, *center_horiz_button, false, false, 2);
125
126 std::vector<Parameter *>::iterator it = param_vector.begin();
127 while (it != param_vector.end()) {
128 if ((*it)->widget_is_visible) {
129 Parameter *param = *it;
130 auto const widg = param->param_newWidget();
131
132 if (widg) {
133 UI::pack_start(*vbox, *widg, true, true, 2);
134
135 if (auto const tip = param->param_getTooltip()) {
136 widg->set_tooltip_markup(*tip);
137 } else {
138 widg->set_tooltip_text("");
139 widg->set_has_tooltip(false);
140 }
141 }
142 }
143
144 ++it;
145 }
146
147 return vbox;
148}
149
150
151void
153 center_vert = true;
155 makeUndoDone(_("Center Vertical"));
156}
157
158void
160 center_horiz = true;
162 makeUndoDone(_("Center Horizontal"));
163}
164
165bool sp_has_path_data(SPItem *item, bool originald)
166{
167 auto group = cast<SPGroup>(item);
168 if (group) {
169 std::vector<SPObject *> childs = group->childList(true);
170 for (auto &child : childs) {
171 auto item = cast<SPItem>(child);
172 if (sp_has_path_data(item, originald)) {
173 return true;
174 }
175 }
176 }
177 auto shape = cast<SPShape>(item);
178 if (shape) {
179 SPCurve const *c = shape->curve();
180 if (c && !c->is_empty()) {
181 return true;
182 }
183 if (originald) {
184 if (shape->hasPathEffectRecursive()) {
185 SPCurve const *c = shape->curveBeforeLPE();
186 if (c && !c->is_empty()) {
187 return true;
188 }
189 }
190 }
191 }
192 return false;
193}
194/*
195 * Allow changing original-d to d to "reset" temporary the LPE
196 * when the slice doesn't pass through item till sp_lpe_item is crossed
197 */
198void
200{
201 SPCurve const *c = shape->curveBeforeLPE();
202 if (c && !c->is_empty()) {
203 curve->set_pathvector(c->get_pathvector());
204 }
205}
206
207/*
208 * Allow changing original-d to d to "reset" temporary the LPE
209 * when the slice doesn't pass through item till sp_lpe_item is crossed
210 */
211void
213{
214 auto group = cast<SPGroup>(item);
215 if (group) {
216 std::vector<SPObject *> childs = group->childList(true);
217 for (auto &child : childs) {
218 auto item = cast<SPItem>(child);
220 }
221 return;
222 }
223 auto shape = cast<SPShape>(item);
224 if (shape) {
225 SPCurve const *c = shape->curveBeforeLPE();
226 if (c && !c->is_empty()) {
227 shape->bbox_vis_cache_is_valid = false;
228 shape->bbox_geom_cache_is_valid = false;
229 shape->setCurveInsync(c);
230 auto str = sp_svg_write_path(c->get_pathvector());
231 shape->setAttribute("d", str);
232 }
233 }
234}
235
236void
238{
239 Glib::ustring version = lpeversion.param_getSVGValue();
240 // this avoid regenerate fake satellites un undo after open a legacy LPE
241 if (!is_load && version < "1.2") {
242 return;
243 }
244 SPDocument *document = getSPDoc();
245 if (!document) {
246 return;
247 }
248
249 if (document->isSeeking()) {
250 return;
251 }
252 bool is_applied_on = false;
253 if (is_applied) {
254 is_applied_on = true;
255 is_applied = false;
256 }
257 bool write = false;
258 bool active = !lpesatellites.data().size() || is_load;
259 for (auto const &lpereference : lpesatellites.data()) {
260 if (lpereference && lpereference->isAttached() && lpereference.get()->getObject() != nullptr) {
261 active = true;
262 }
263 }
264 if (!active && !is_load) {
266 return;
267 }
268
269 LPESlice *nextslice = dynamic_cast<LPESlice *>(sp_lpe_item->getNextLPE(this));
270 if (is_visible && (!nextslice || !nextslice->is_visible)) {
271 LPESlice *prevslice = dynamic_cast<LPESlice *>(sp_lpe_item->getPrevLPE(this));
273 for (auto & iter : lpesatellites.data()) {
274 SPObject *elemref;
275 if (iter && iter->isAttached() && (elemref = iter->getObject())) {
276 if (auto *splpeitem = cast<SPLPEItem>(elemref)) {
277 splpeitem->setHidden(true);
278 }
279 }
280 }
281 return;
282 }
283 //ungroup
286 } else if (!is_load && container && container != sp_lpe_item->parent) { // group
288 }
289 std::vector<std::pair<Geom::Line, size_t> > slicer = getSplitLines();
290 if (!slicer.size()) {
291 return;
292 }
293 container = lpeitem->parent;
294 objindex = 0;
295 legacy = false;
296 bool creation = write;
297 split(sp_lpe_item, curve, slicer, 0, creation);
298 bool connected = lpesatellites.is_connected();
299 if (lpesatellites.data().size() && (creation || !connected)) {
303 }
305 bool maindata = sp_has_path_data(sp_lpe_item, true);
306 for (auto & iter : lpesatellites.data()) {
307 SPObject *elemref;
308 if (iter && iter->isAttached() && (elemref = iter->getObject())) {
309 auto splpeitem = cast<SPLPEItem>(elemref);
310 if (splpeitem || lpeitem->isHidden()) {
311 if (!maindata || lpeitem->isHidden()) {
312 splpeitem->setHidden(true);
313 }
314 sp_lpe_item_update_patheffect(splpeitem, false, false);
315 }
316 }
317 }
318 if (!maindata) {
319 if (!curve) { // group
321 } else {
323 }
325 return;
326 }
327 reset = false;
328 if (is_applied_on && prevslice) {
330 for (auto const &link : prevslice->lpesatellites.data()) {
331 if (link && link->isAttached()) {
332 auto spgrp = cast<SPGroup>(link->getObject());
333 auto spit = cast<SPShape>(link->getObject());
334 Glib::ustring transform = "";
335 Glib::ustring patheffects = "";
336 Geom::OptRect _gbbox = Geom::OptRect();
337 if (spgrp) {
338 if (spgrp->getAttribute("transform")) {
339 transform = spgrp->getAttribute("transform");
340 }
341 if (spgrp->getAttribute("inkscape:path-effect")) {
342 patheffects = spgrp->getAttribute("inkscape:path-effect");
343 }
344 spgrp->setAttribute("transform", nullptr);
345 spgrp->setAttribute("inkscape:path-effect", nullptr);
346 _gbbox = spgrp->geometricBounds();
347 }
348 if (spit || spgrp) {
349 for (auto const &link2 : lpesatellites.data()) {
350 if (link2 && link2->isAttached()) {
351 auto spgrp2 = cast<SPGroup>(link2->getObject());
352 auto spit2 = cast<SPShape>(link2->getObject());
353 if (spit && spit2) {
354 Geom::OptRect _bbox = spit->curveForEdit()->get_pathvector().boundsFast();
355 Geom::OptRect _bbox2 = spit2->curveForEdit()->get_pathvector().boundsFast();
356 if (_bbox && _bbox2) {
357 (*_bbox).expandBy(1);
358 if ((*_bbox).contains(*_bbox2)) {
359 spit2->setAttribute("transform", spit->getAttribute("transform"));
360 spit2->setAttribute("inkscape:path-effect", spit->getAttribute("inkscape:path-effect"));
361 spit2->setAttribute("style", spit->getAttribute("style"));
362 }
363 }
364 } else if (spgrp && spgrp2) {
365 Geom::OptRect _gbbox2 = spgrp2->geometricBounds();
366 if (_gbbox && _gbbox2) {
367 (*_gbbox).expandBy(1);
368 if ((*_gbbox).contains(*_gbbox2)) {
369 spgrp2->setAttribute("transform", transform);
370 spgrp2->setAttribute("inkscape:path-effect", patheffects);
371 cloneStyle(spgrp, spgrp2);
372 }
373 }
374 }
375 }
376 }
377 if (spgrp) {
378 spgrp->setAttribute("transform", transform);
379 spgrp->setAttribute("inkscape:path-effect", patheffects);
380 }
381 }
382 }
383 }
384
385 }
386 } else {
387 for (auto const &itemrf : lpesatellites.data()) {
388 if (itemrf && itemrf->isAttached()) {
389 auto splpeitem = cast<SPLPEItem>(itemrf->getObject());
390 if (splpeitem) {
391 splpeitem->setHidden(true);
392 sp_lpe_item_update_patheffect(splpeitem, false, false);
393 }
394 }
395 }
396 }
398}
399
400bool
401LPESlice::split(SPItem* item, SPCurve *curve, std::vector<std::pair<Geom::Line, size_t> > slicer, size_t splitindex, bool &creation) {
402 bool splited = false;
403 size_t nsplits = slicer.size();
404 SPDocument *document = getSPDoc();
405 if (!document) {
406 return splited;
407 }
408
409 SPObject *elemref = nullptr;
410 if (!is_load && container != sp_lpe_item->parent) {
412 return splited;
413 }
414 if (objindex < lpesatellites.data().size() && lpesatellites.data()[objindex]) {
415 elemref = lpesatellites.data()[objindex]->getObject();
416 }
417 bool prevreset = reset;
418
419 if (!elemref && item->getId()) {
420
421 Glib::ustring elemref_id = Glib::ustring("slice-");
422 elemref_id += Inkscape::ustring::format_classic(slicer[splitindex].second);
423 elemref_id += "-";
424 Glib::ustring clean_id = item->getId();
425 //First check is to allow effects on "satellites"
426 auto lpeitem = cast<SPLPEItem>(item);
427 if (!lpeitem) {
428 return splited;
429 }
430 if (!lpeitem->hasPathEffectOfType(SLICE) && clean_id.find("slice-") != Glib::ustring::npos) {
431 clean_id = clean_id.replace(0,6,"");
432 elemref_id += clean_id;
433 } else {
434 elemref_id += clean_id;
435 }
436 creation = true;
437 if (is_load && (elemref = document->getObjectById(elemref_id))) {
438 legacy = true;
439 lpesatellites.link(elemref, objindex);
440 } else {
441 reset = true;
443 if (!container) {
444 return splited;
445 }
446 elemref = container->appendChildRepr(phantom);
447 Inkscape::GC::release(phantom);
448 lpesatellites.link(elemref, objindex);
449 }
450 }
451 auto other = cast<SPItem>(elemref);
452 if (other) {
453 objindex++;
454 other->setHidden(false);
455 if (nsplits) {
456 cloneD(item, other, false);
457 reset = prevreset;
458 splited = splititem(item, curve, slicer[splitindex], true);
459 splititem(other, nullptr, slicer[splitindex], false);
460 if (!splited) {
461 other->setHidden(true);
462 }
463 splitindex++;
464 if (nsplits > splitindex) {
465 auto splpeother = cast<SPLPEItem>(other);
466 auto splpeitem = cast<SPLPEItem>(item);
467 if (item == sp_lpe_item || !splpeitem->hasPathEffectOfType(SLICE)) {
468 split(item, curve, slicer, splitindex, creation);
469 if (other == sp_lpe_item || !splpeother->hasPathEffectOfType(SLICE)) {
470 split(other, nullptr, slicer, splitindex, creation);
471 }
472 }
473 }
474 }
475 }
476 return splited;
477}
478
479std::vector<std::pair<Geom::Line, size_t> >
481 std::vector<std::pair<Geom::Line, size_t> > splitlines;
482 std::vector<SPLPEItem *> lpeitems = getCurrrentLPEItems();
483 if (lpeitems.size() >= 1) {
484 sp_lpe_item = lpeitems[0];
485 } else {
486 return splitlines;
487 }
488 LPESlice *prevslice = dynamic_cast<LPESlice *>(sp_lpe_item->getPrevLPE(this));
489 if (prevslice) {
490 splitlines = prevslice->getSplitLines();
491 }
493 size_t index = sp_lpe_item->getLPEIndex(this);
494 std::pair<Geom::Line, size_t> slice = std::make_pair(line_separation, index);
495 splitlines.push_back(slice);
496 return splitlines;
497}
498
501 SPDocument *document = getSPDoc();
502 if (!document) {
503 return nullptr;
504 }
506 Inkscape::XML::Node *prev = elemref->getRepr();
507 auto group = cast<SPGroup>(elemref);
508 if (group) {
509 Inkscape::XML::Node *container = xml_doc->createElement("svg:g");
510 container->setAttribute("transform", prev->attribute("transform"));
511 container->setAttribute("mask", prev->attribute("mask"));
512 container->setAttribute("clip-path", prev->attribute("clip-path"));
513 std::vector<SPItem*> const item_list = group->item_list();
514 Inkscape::XML::Node *previous = nullptr;
515 for (auto sub_item : item_list) {
516 Inkscape::XML::Node *resultnode = createPathBase(sub_item);
517 container->addChild(resultnode, previous);
518 previous = resultnode;
519 }
520 return container;
521 }
522 Inkscape::XML::Node *resultnode = xml_doc->createElement("svg:path");
523 resultnode->setAttribute("transform", prev->attribute("transform"));
524 resultnode->setAttribute("mask", prev->attribute("mask"));
525 resultnode->setAttribute("clip-path", prev->attribute("clip-path"));
526 return resultnode;
527}
528
529void
530LPESlice::cloneD(SPObject *orig, SPObject *dest, bool is_original)
531{
532 if (!is_original && !g_strcmp0(sp_lpe_item->getId(), orig->getId())) {
533 is_original = true;
534 }
535 SPDocument *document = getSPDoc();
536 if (!document) {
537 return;
538 }
539 auto originalitem = cast<SPItem>(orig);
540 if ( is<SPGroup>(orig) && is<SPGroup>(dest) && cast<SPGroup>(orig)->getItemCount() == cast<SPGroup>(dest)->getItemCount() ) {
541 if (reset) {
542 cloneStyle(orig, dest);
543 }
544 if (!allow_transforms) {
545 auto str = sp_svg_transform_write(originalitem->transform);
546 dest->setAttributeOrRemoveIfEmpty("transform", str);
547 }
548 std::vector< SPObject * > childs = orig->childList(true);
549 size_t index = 0;
550 for (auto &child : childs) {
551 SPObject *dest_child = dest->nthChild(index);
552 cloneD(child, dest_child, is_original);
553 index++;
554 }
555 return;
556 }
557
558 auto shape = cast<SPShape>(orig);
559 auto path = cast<SPPath>(dest);
560 if (path && shape) {
561 SPCurve const *c = shape->curve();
562 if (c && !c->is_empty()) {
563 auto str = sp_svg_write_path(c->get_pathvector());
564 if (path->hasPathEffectRecursive()) {
566 dest->setAttribute("inkscape:original-d", str);
568 dest->setAttribute("d", str);
569 } else {
570 dest->setAttribute("d", str);
571 }
572 if (!allow_transforms) {
573 auto str = sp_svg_transform_write(originalitem->transform);
574 dest->setAttributeOrRemoveIfEmpty("transform", str);
575 }
576 if (reset) {
577 cloneStyle(orig, dest);
578 }
579 }
580 }
581}
582
584{
585 SPCSSAttr *css = sp_repr_css_attr(item->getRepr(), "style");
586 gchar const *val = sp_repr_css_property(css, "fill-rule", nullptr);
587 if (val && strcmp(val, "nonzero") == 0) {
588 return fill_nonZero;
589 } else if (val && strcmp(val, "evenodd") == 0) {
590 return fill_oddEven;
591 } else {
592 return fill_nonZero;
593 }
594}
595
596bool
597LPESlice::splititem(SPItem* item, SPCurve * curve, std::pair<Geom::Line, size_t> slicer, bool toggle, bool is_original, Geom::Affine tpass, bool top)
598{
599 bool splited = false;
600 if (!is_original && !g_strcmp0(sp_lpe_item->getId(), item->getId())) {
601 is_original = true;
602 }
603 Geom::Line line_separation = slicer.first;
604 Geom::Point s = line_separation.initialPoint();
605 Geom::Point e = line_separation.finalPoint();
606 Geom::Point center = Geom::middle_point(s, e);
607 auto group = cast<SPGroup>(item);
608 if (group) {
609 Geom::Affine t = group->transform * tpass;
610 if (top) {
611 t = Geom::identity();
612 }
613 std::vector<SPObject *> childs = group->childList(true);
614 for (auto &child : childs) {
615 auto dest_child = cast<SPItem>(child);
616 // groups not need update curve
617 splited = splititem(dest_child, nullptr, slicer, toggle, is_original, t, false) ? true : splited;
618 }
619 if (!is_original) {
620 sp_lpe_item_update_patheffect(group, false, false);
621 }
622 return splited;
623 }
624 auto shape = cast<SPShape>(item);
625 auto path = cast<SPPath>(item);
626 if (shape) {
627 SPCurve const *c;
628 c = shape->curve();
629 if (c) {
630 Geom::PathVector original_pathv = pathv_to_linear_and_cubic_beziers(c->get_pathvector());
631 flatten(original_pathv, GetFillTyp(shape));
632 Geom::PathVector path_out;
633 Geom::Affine t = shape->transform * tpass;
634 if (!is<SPGroup>(sp_lpe_item)) {
635 t = Geom::identity();
636 }
637 for (auto & path_it : original_pathv) {
638 path_it *= t;
639 if (path_it.empty()) {
640 continue;
641 }
642 Geom::PathVector tmp_pathvector;
643 double time_start = 0.0;
644 int position = 0;
645 bool end_open = false;
646 if (path_it.closed()) {
647 const Geom::Curve &closingline = path_it.back_closed();
648 if (!are_near(closingline.initialPoint(), closingline.finalPoint())) {
649 end_open = true;
650 }
651 }
652 Geom::Path original = path_it;
653 if (end_open && path_it.closed()) {
654 original.close(false);
655 original.appendNew<Geom::LineSegment>( original.initialPoint() );
656 original.close(true);
657 }
658 double dir = line_separation.angle();
659 Geom::Ray ray = line_separation.ray(0);
662 double size_divider = Geom::distance(center, bbox) + diagonal;
663 s = Geom::Point::polar(dir,size_divider) + center;
664 e = Geom::Point::polar(dir + Geom::rad_from_deg(180),size_divider) + center;
665 Geom::Path divider = Geom::Path(s);
666 divider.appendNew<Geom::LineSegment>(e);
667 std::vector<double> crossed;
668 if (Geom::are_near(s,e)) {
669 continue;
670 }
671 Geom::Crossings cs = crossings(original, divider);
672 for(auto & c : cs) {
673 crossed.push_back(c.ta);
674 }
675 double angle = Geom::deg_from_rad(ray.angle());
676 bool toggleside = !(angle > 0 && angle < 180);
677 std::sort(crossed.begin(), crossed.end());
678 for (double time_end : crossed) {
679 if (time_start != time_end && time_end - time_start > Geom::EPSILON) {
680 Geom::Path portion = original.portion(time_start, time_end);
681 if (!portion.empty()) {
682 Geom::Point middle = portion.pointAt((double)portion.size()/2.0);
683 position = Geom::sgn(Geom::cross(e - s, middle - s));
684 if (toggleside) {
685 position *= -1;
686 }
687 if (toggle) {
688 position *= -1;
689 }
690 if (position == 1) {
691 tmp_pathvector.push_back(portion);
692 }
693 portion.clear();
694 }
695 }
696 time_start = time_end;
697 }
698 position = Geom::sgn(Geom::cross(e - s, original.finalPoint() - s));
699 if (toggleside) {
700 position *= -1;
701 }
702 if (toggle) {
703 position *= -1;
704 }
705 if (cs.size()!=0 && (position == 1)) {
706 if (time_start != original.size() && original.size() - time_start > Geom::EPSILON) {
707 Geom::Path portion = original.portion(time_start, original.size());
708 if (!portion.empty()) {
709 if (!original.closed()) {
710 tmp_pathvector.push_back(portion);
711 } else {
712 if (cs.size() > 1 && tmp_pathvector.size() > 0 && tmp_pathvector[0].size() > 0 ) {
713 tmp_pathvector[0] = tmp_pathvector[0].reversed();
714 portion = portion.reversed();
715 portion.setInitial(tmp_pathvector[0].finalPoint());
716 tmp_pathvector[0].append(portion);
717 tmp_pathvector[0] = tmp_pathvector[0].reversed();
718 } else {
719 tmp_pathvector.push_back(portion);
720 }
721 }
722 portion.clear();
723 }
724 }
725 }
726 if (cs.size() > 0 && original.closed()) {
727 for (auto &path : tmp_pathvector) {
728 if (!path.closed()) {
729 path.close();
730 }
731 }
732 }
733 if (cs.size() == 0 && position == 1) {
734 splited = false;
735 tmp_pathvector.push_back(original);
736 } else {
737 splited = true;
738 }
739 tmp_pathvector *= t.inverse();
740 path_out.insert(path_out.end(), tmp_pathvector.begin(), tmp_pathvector.end());
741
742 tmp_pathvector.clear();
743 }
744 if (curve && is_original) {
745 curve->set_pathvector(path_out);
746 }
747 if (shape->curve()) {
748 shape->bbox_vis_cache_is_valid = false;
749 shape->bbox_geom_cache_is_valid = false;
750 shape->setCurveInsync(SPCurve(path_out));
751 auto str = sp_svg_write_path(path_out);
752 if (!is_original && shape->hasPathEffectRecursive()) {
754 if (path) {
755 shape->setAttribute("inkscape:original-d", str);
756 } else {
757 shape->setAttribute("d", str);
758 }
760 } else {
761 shape->setAttribute("d", str);
762 }
763 }
764 }
765 }
766 return splited;
767}
768
769void
771{
772 SPDocument *document = getSPDoc();
773 if (!document) {
774 return;
775 }
776 if (!lpesatellites.data().size()) {
778 if (lpesatellites.data().size()) {
780 }
781 }
782 using namespace Geom;
783 original_bbox(lpeitem, false, true);
787 if (center_vert) {
788 double dista = std::abs(end_point[Geom::Y] - boundingbox_Y.max());
789 double distb = std::abs(start_point[Geom::Y] - boundingbox_Y.max());
790 previous_center = Geom::Point(Geom::infinity(), g_random_double_range(0, 1000));
792 Geom::Point(center_point[Geom::X], dista <= distb ? boundingbox_Y.min() : boundingbox_Y.max()), true);
794 Geom::Point(center_point[Geom::X], dista > distb ? boundingbox_Y.min() : boundingbox_Y.max()), true);
796 //force update
797 center_vert = false;
798 } else if (center_horiz) {
799 double dista = std::abs(end_point[Geom::X] - boundingbox_X.max());
800 double distb = std::abs(start_point[Geom::X] - boundingbox_X.max());
801 previous_center = Geom::Point(Geom::infinity(), g_random_double_range(0, 1000));
803 Geom::Point(dista <= distb ? boundingbox_X.min() : boundingbox_X.max(), center_point[Geom::Y]), true);
805 Geom::Point(dista > distb ? boundingbox_X.min() : boundingbox_X.max(), center_point[Geom::Y]), true);
807 //force update
808 center_horiz = false;
809 } else {
811 start_point.param_setValue(point_a, true);
812 end_point.param_setValue(point_b, true);
815 return;
816 }
820 end_point.param_setValue(end_point + trans, true);
821 }
824 }
826 LPESlice *nextslice = dynamic_cast<LPESlice *>(sp_lpe_item->getNextLPE(this));
827 while (nextslice) {
828 if (nextslice->allow_transforms != allow_transforms) {
831 }
832 nextslice = dynamic_cast<LPESlice *>(sp_lpe_item->getNextLPE(nextslice));
833 }
834 LPESlice *prevslice = dynamic_cast<LPESlice *>(sp_lpe_item->getPrevLPE(this));
835 while (prevslice) {
836 if (prevslice->allow_transforms != allow_transforms) {
839 }
840 prevslice = dynamic_cast<LPESlice *>(sp_lpe_item->getNextLPE(prevslice));
841 }
842 }
844}
845
847{
848 for (auto iter : orig->style->properties()) {
849 if (iter->style_src != SPStyleSrc::UNSET) {
850 auto key = iter->id();
851 if (key != SPAttr::FONT && key != SPAttr::D && key != SPAttr::MARKER) {
852 const gchar *attr = orig->getAttribute(iter->name().c_str());
853 if (attr) {
854 dest->setAttribute(iter->name(), attr);
855 }
856 }
857 }
858 }
859 dest->setAttribute("style", orig->getAttribute("style"));
860}
861
862void
864 std::vector<SPLPEItem *> lpeitems = getCurrrentLPEItems();
865 if (lpeitems.size() == 1) {
866 sp_lpe_item = lpeitems[0];
867 LPESlice *nextslice = dynamic_cast<LPESlice *>(sp_lpe_item->getNextLPE(this));
868 while (nextslice) {
869 nextslice->reset = true;
870 nextslice = dynamic_cast<LPESlice *>(sp_lpe_item->getNextLPE(nextslice));
871 }
872 reset = true;
874 }
875}
876
877void
879{
880 if (!is_visible) {
881 for (auto const &itemrf : lpesatellites.data()) {
882 if (itemrf && itemrf->isAttached()) {
883 auto splpeitem = cast<SPLPEItem>(itemrf->getObject());
884 if (splpeitem) {
885 splpeitem->setHidden(true);
886 sp_lpe_item_update_patheffect(splpeitem, false, false);
887 }
888 }
889 }
890 }
891}
892void
894{
895 if (keep_paths) {
897 return;
898 }
900}
901
902void
904{
905 using namespace Geom;
906 original_bbox(lpeitem, false, true);
907 LPESlice *prevslice = dynamic_cast<LPESlice *>(sp_lpe_item->getPrevLPE(this));
908 if (prevslice) {
911 }
915 start_point.param_setValue(point_a, true);
917 end_point.param_setValue(point_b, true);
919 center_point.param_setValue(point_c, true);
922 lpeversion.param_setValue("1.2", true);
923 sp_lpe_item_update_patheffect(sp_lpe_item, false, false, true);
924}
925
926
929{
930 return path_in;
931}
932
933void
934LPESlice::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec)
935{
936 using namespace Geom;
937 hp_vec.clear();
938 Geom::Path path;
941 path.start( s );
942 path.appendNew<Geom::LineSegment>( e );
943 Geom::PathVector helper;
944 helper.push_back(path);
945 hp_vec.push_back(helper);
946}
947
948} //namespace LivePathEffect
949} /* namespace Inkscape */
950
951/*
952 Local Variables:
953 mode:c++
954 c-file-style:"stroustrup"
955 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
956 indent-tabs-mode:nil
957 fill-column:99
958 End:
959*/
960// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
FillRule
Definition LivarotDefs.h:67
@ fill_oddEven
Definition LivarotDefs.h:68
@ fill_nonZero
Definition LivarotDefs.h:69
TODO: insert short description here.
TODO: insert short description here.
3x3 affine transformation matrix.
3x3 matrix representing an affine transformation.
Definition affine.h:70
Affine inverse() const
Compute the inverse matrix.
Definition affine.cpp:388
void clear()
Definition bezier.h:248
unsigned size() const
Definition bezier.h:147
friend Bezier portion(const Bezier &a, Coord from, Coord to)
Definition bezier.cpp:250
Abstract continuous curve on a plane defined on [0,1].
Definition curve.h:78
virtual Point initialPoint() const =0
Retrieve the start of the curve.
virtual Point finalPoint() const =0
Retrieve the end of the curve.
constexpr bool isSingular() const
constexpr C min() const
constexpr C max() const
constexpr C middle() const
Infinite line on a plane.
Definition line.h:53
Ray ray(Coord t)
Create a ray starting at the specified time value.
Definition line.h:295
Point finalPoint() const
Definition line.h:228
Point initialPoint() const
Definition line.h:225
Coord angle() const
Angle the line makes with the X axis, in mathematical convention.
Definition line.h:137
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Sequence of subpaths.
Definition pathvector.h:122
size_type size() const
Get the number of paths in the vector.
Definition pathvector.h:147
void push_back(Path const &path)
Append a path at the end.
Definition pathvector.h:172
void clear()
Remove all paths from the vector.
Definition pathvector.h:195
iterator insert(iterator pos, Path const &p)
Definition pathvector.h:179
PathVector reversed(bool reverse_paths=true) const
Get a new vector with reversed direction of paths.
iterator begin()
Definition pathvector.h:151
iterator end()
Definition pathvector.h:152
Sequence of contiguous curves, aka spline.
Definition path.h:353
void close(bool closed=true)
Set whether the path is closed.
Definition path.cpp:322
void appendNew(Args &&... args)
Append a new curve to the path.
Definition path.h:804
void start(Point const &p)
Definition path.cpp:426
Two-dimensional point that doubles as a vector.
Definition point.h:66
Straight ray from a specific point to infinity.
Definition ray.h:53
Coord angle() const
Definition ray.h:73
Axis aligned, non-empty rectangle.
Definition rect.h:92
std::vector< StorageType > const & data() const
Definition array.h:48
void param_setValue(bool newvalue)
Definition bool.cpp:89
std::vector< Parameter * > param_vector
Definition effect.h:179
void registerParameter(Parameter *param)
Definition effect.cpp:1710
virtual void processObjects(LPEAction lpe_action)
Definition effect.cpp:1416
SPShape * getCurrentShape() const
Definition effect.h:112
std::vector< SPLPEItem * > getCurrrentLPEItems() const
Definition effect.cpp:1187
void makeUndoDone(Glib::ustring message)
Definition effect.cpp:1521
void original_bbox(SPLPEItem const *lpeitem, bool absolute=false, bool clip_mask=false, Geom::Affine base_transform=Geom::identity())
void param_setValue(Glib::ustring newvalue, bool write=false)
Definition hidden.cpp:71
Glib::ustring param_getSVGValue() const override
Definition hidden.cpp:53
void doAfterEffect(SPLPEItem const *lpeitem, SPCurve *curve) override
Is performed at the end of the LPE only one time per "lpeitem" in paths/shapes is called in middle of...
void cloneStyle(SPObject *orig, SPObject *dest)
void doOnApply(SPLPEItem const *lpeitem) override
Is performed a single time when the effect is freshly applied to a path.
void doOnVisibilityToggled(SPLPEItem const *) override
bool split(SPItem *item, SPCurve *curve, std::vector< std::pair< Geom::Line, size_t > > slicer, size_t splitindex, bool &creation)
Geom::PathVector doEffect_path(Geom::PathVector const &path_in) override
void originalDtoD(SPShape const *shape, SPCurve *curve)
Gtk::Widget * newWidget() override
This creates a managed widget.
void cloneD(SPObject *orig, SPObject *dest, bool is_original)
LPESlice(LivePathEffectObject *lpeobject)
Definition lpe-slice.cpp:52
void doBeforeEffect(SPLPEItem const *lpeitem) override
Is performed each time before the effect is updated.
void doOnRemove(SPLPEItem const *) override
bool splititem(SPItem *item, SPCurve *curve, std::pair< Geom::Line, size_t > slicer, bool toggle, bool is_original=false, Geom::Affine tpass=Geom::identity(), bool top=true)
bool doOnOpen(SPLPEItem const *lpeitem) override
Is performed on load document or revert If the item is fixed legacy return true.
Definition lpe-slice.cpp:82
Inkscape::XML::Node * createPathBase(SPObject *elemref)
SatelliteArrayParam lpesatellites
Definition lpe-slice.h:66
std::vector< std::pair< Geom::Line, size_t > > getSplitLines()
void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector< Geom::PathVector > &hp_vec) override
Add possible canvas indicators (i.e., helperpaths other than the original path) to hp_vec This functi...
Glib::ustring const * param_getTooltip() const
Definition parameter.h:81
virtual void param_widget_is_visible(bool is_visible)
Definition parameter.h:69
void setUpdating(bool updating)
Definition parameter.h:73
virtual Gtk::Widget * param_newWidget()=0
void param_update_default(Geom::Point default_point)
Definition point.cpp:64
void param_setValue(Geom::Point newpoint, bool write=false)
Definition point.cpp:100
void link(SPObject *to, size_t pos=Glib::ustring::npos)
Interface for refcounted XML nodes.
Definition node.h:80
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
Wrapper around a Geom::PathVector object.
Definition curve.h:26
Typed SVG document implementation.
Definition document.h:103
bool isSeeking() const
Definition document.h:361
SPObject * getObjectById(std::string const &id) const
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
Definition document.h:213
Base class for visual SVG elements.
Definition sp-item.h:109
bool isHidden() const
Definition sp-item.cpp:242
std::size_t getLPEIndex(Inkscape::LivePathEffect::Effect *lpe) const
bool hasPathEffectOfType(int const type, bool is_ready=true) const
Inkscape::LivePathEffect::Effect * getPrevLPE(Inkscape::LivePathEffect::Effect *lpe)
Inkscape::LivePathEffect::Effect * getNextLPE(Inkscape::LivePathEffect::Effect *lpe)
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
void setAttributeOrRemoveIfEmpty(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
Definition sp-object.h:785
void setAttribute(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
char const * getId() const
Returns the objects current ID string.
SPObject * parent
Definition sp-object.h:189
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
SPObject * appendChildRepr(Inkscape::XML::Node *repr)
Append repr as child of this object.
SPObject * nthChild(unsigned index)
void addChild(Inkscape::XML::Node *child, Inkscape::XML::Node *prev=nullptr)
Base class for shapes, including <path> element.
Definition sp-shape.h:38
SPCurve const * curveBeforeLPE() const
Return a borrowed pointer of the curve before LPE (if any exists) or NULL if there is no curve.
Definition sp-shape.cpp:978
std::shared_ptr< Css const > css
double c[8][4]
Geom::Point orig
constexpr Coord infinity()
Get a value representing infinity.
Definition coord.h:88
constexpr Coord EPSILON
Default "acceptably small" value.
Definition coord.h:84
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
Geom::PathVector pathv_to_linear_and_cubic_beziers(Geom::PathVector const &pathv)
Definition geom.cpp:588
Specific geometry functions for Inkscape, not provided my lib2geom.
Macro for icon names used in Inkscape.
SPItem * item
std::string original
LPE <mirror_symmetry> implementation: mirrors a path with respect to a given line.
Various utility functions.
Definition affine.h:22
int sgn(const T &x)
Sign function - indicates the sign of a numeric type.
Definition math-utils.h:51
Angle distance(Angle const &a, Angle const &b)
Definition angle.h:163
std::vector< Crossing > Crossings
Definition crossing.h:126
Affine identity()
Create an identity matrix.
Definition affine.h:210
Crossings crossings(Curve const &a, Curve const &b)
Bezier portion(const Bezier &a, double from, double to)
Definition bezier.cpp:250
Piecewise< SBasis > cross(Piecewise< D2< SBasis > > const &a, Piecewise< D2< SBasis > > const &b)
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
Point middle_point(LineSegment const &_segment)
static R & release(R &r)
Decrements the reference count of a anchored object.
bool sp_has_path_data(SPItem *item, bool originald)
static FillRule GetFillTyp(SPItem *item)
Definition lpe-bool.cpp:365
void pack_start(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill, unsigned const padding)
Adds child to box, packed with reference to the start of box.
Definition pack.cpp:141
Glib::ustring format_classic(T const &... args)
Helper class to stream background task notifications as a series of messages.
static cairo_user_data_key_t key
Helpers for using Gtk::Boxes, encapsulating large changes between GTK3 & GTK4.
void flatten(Geom::PathVector &pathv, FillRule fill_rule)
Boolean operations.
Path intersection.
Inkscape::SVG::PathString - builder for SVG path strings.
Ocnode * child[8]
Definition quantize.cpp:33
SPCSSAttr * sp_repr_css_attr(Node const *repr, gchar const *attr)
Creates a new SPCSSAttr with one attribute (i.e.
Definition repr-css.cpp:88
char const * sp_repr_css_property(SPCSSAttr *css, gchar const *name, gchar const *defval)
Returns a character string of the value of a given style property or a default value if the attribute...
Definition repr-css.cpp:147
TODO: insert short description here.
SPCSSAttr - interface for CSS Attributes.
void sp_lpe_item_update_patheffect(SPLPEItem *lpeitem, bool wholetree, bool write, bool with_satellites)
Calls any registered handlers for the update_patheffect action.
void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable)
Base class for live path effect items.
Interface for XML documents.
Definition document.h:43
virtual Node * createElement(char const *name)=0
Definition curve.h:24
SPStyle - a style object for SPItem objects.
std::string sp_svg_transform_write(Geom::Affine const &transform)
static void sp_svg_write_path(Inkscape::SVG::PathString &str, Geom::Path const &p, bool normalize=false)
Definition svg-path.cpp:109
int index