Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
lpe-powerclip.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
4 */
5
6#include "lpe-powerclip.h"
7
8#include <glibmm/i18n.h>
9
12
13#include "preferences.h"
14#include "selection.h"
15#include "style.h"
16
17#include "inkscape.h"
20#include "object/sp-clippath.h"
21#include "object/sp-defs.h"
23#include "object/sp-item.h"
24#include "object/sp-path.h"
25#include "object/sp-shape.h"
26#include "object/sp-use.h"
27#include "svg/svg.h"
28
29namespace Inkscape {
30namespace LivePathEffect {
31
33 : Effect(lpeobject)
34 , hide_clip(_("Hide clip"), _("Hide clip"), "hide_clip", &wr, this, false)
35 , inverse(_("Inverse clip"), _("Inverse clip"), "inverse", &wr, this, true)
36 , flatten(_("Flatten clip"), _("Flatten clip, see fill rule once convert to paths"), "flatten", &wr, this, false)
37 , message(
38 _("Info Box"), _("Important messages"), "message", &wr, this,
39 _("Use fill-rule evenodd on <b>fill and stroke</b> dialog if no flatten result after convert clip to paths."))
40{
46 message.write_to_SVG(); // resert old legacy uneeded data
47 _updating = false;
48 _legacy = false;
49 // legazy fix between 0.92.4 launch and 1.0beta1
50 if (this->getRepr()->attribute("is_inverse")) {
51 this->getRepr()->removeAttribute("is_inverse");
52 _legacy = true;
53 }
54}
55
57
59{
60 Geom::OptRect bbox = lpeitem->visualBounds(Geom::identity(), true, false, true);
61 if (bbox) {
62 (*bbox).expandBy(5);
63 return Geom::Path(*bbox);
64 }
65 return Geom::Path();
66}
67
69{
70 auto group = cast<SPGroup>(item);
71 if (group) {
72 std::vector<SPItem *> item_list = group->item_list();
73 for (auto child : item_list) {
74 if (child) {
75 auto childitem = cast<SPLPEItem>(child);
76 if (childitem) {
77 res = sp_get_recursive_pathvector(childitem, res, dir, inverse);
78 }
79 }
80 }
81 }
82 auto shape = cast<SPShape>(item);
83 if (shape && shape->curve()) {
84 for (auto path : *shape->curve()) {
85 if (!path.empty()) {
86 bool pathdir = Geom::path_direction(path);
87 if (pathdir == dir && inverse) {
88 path = path.reversed();
89 }
90 res.push_back(path);
91 }
92 }
93 }
94 return res;
95}
96
98{
100 Geom::PathVector res_hlp;
101 if (!sp_lpe_item) {
102 return res;
103 }
104
105 SPObject *clip_path = sp_lpe_item->getClipObject();
106 if (clip_path) {
107 std::vector<SPObject*> clip_path_list = clip_path->childList(true);
108 clip_path_list.pop_back();
109 if (clip_path_list.size()) {
110 for (auto clip : clip_path_list) {
111 auto childitem = cast<SPLPEItem>(clip);
112 if (childitem) {
113 res_hlp = sp_get_recursive_pathvector(childitem, res_hlp, false, inverse);
114 if (is_load && _legacy) {
115 childitem->doWriteTransform(Geom::Translate(0, -999999));
116 }
117 if (!childitem->style || !childitem->style->display.set ||
118 childitem->style->display.value != SP_CSS_DISPLAY_NONE) {
119 childitem->style->display.set = TRUE;
120 childitem->style->display.value = SP_CSS_DISPLAY_NONE;
121 childitem->updateRepr(SP_OBJECT_WRITE_NO_CHILDREN | SP_OBJECT_WRITE_EXT);
122 }
123 }
124 }
125 if (is_load && _legacy) {
126 res_hlp *= Geom::Translate(0, -999999);
127 _legacy = false;
128 }
129 }
130 }
132 if (hide_clip) {
133 return bbox;
134 }
135 if (inverse && isVisible()) {
136 res.push_back(bbox);
137 }
138 for (auto path : res_hlp) {
139 res.push_back(path);
140 }
141 return res;
142}
143
145{
146 SPDocument *document = getSPDoc();
147 if (!document || !sp_lpe_item) {
148 return;
149 }
150 SPObject *clip_path = sp_lpe_item->getClipObject();
151 SPObject *elemref = NULL;
152 if (clip_path) {
153 Inkscape::XML::Document *xml_doc = document->getReprDoc();
154 Inkscape::XML::Node *parent = clip_path->getRepr();
155 auto childitems = clip_path->childList(true);
156 auto childitem = cast<SPLPEItem>(childitems.empty() ? nullptr : childitems.back());
157 if (childitem) {
158 if (const gchar *powerclip = childitem->getRepr()->attribute("class")) {
159 if (!strcmp(powerclip, "powerclip")) {
160 Glib::ustring newclip = Glib::ustring("clipath_") + getId();
161 Glib::ustring uri = Glib::ustring("url(#") + newclip + Glib::ustring(")");
162 parent = clip_path->getRepr()->duplicate(xml_doc);
163 parent->setAttribute("id", newclip);
164 clip_path = document->getDefs()->appendChildRepr(parent);
166 sp_lpe_item->setAttribute("clip-path", uri);
167 auto childitems2 = clip_path->childList(true);
168 if (auto childitemdel = cast<SPLPEItem>(childitems2.empty() ? nullptr : childitems2.back())) {
169 childitemdel->setAttribute("id", getId());
170 return;
171 }
172 }
173 }
174 }
175 Inkscape::XML::Node *clip_path_node = xml_doc->createElement("svg:path");
176 parent->appendChild(clip_path_node);
177 Inkscape::GC::release(clip_path_node);
178 elemref = document->getObjectByRepr(clip_path_node);
179 if (elemref) {
180 if (childitem) {
181 elemref->setAttribute("style", childitem->getAttribute("style"));
182 } else {
183 elemref->setAttribute("style", "fill-rule:evenodd");
184 }
185 elemref->setAttribute("class", "powerclip");
186 elemref->setAttribute("id", getId());
188 } else {
190 }
191 } else {
193 }
194}
195
196Glib::ustring LPEPowerClip::getId() { return Glib::ustring("lpe_") + Glib::ustring(getLPEObj()->getId()); }
197
199{
200 SPDocument *document = getSPDoc();
201 if (!document || !sp_lpe_item) {
202 return;
203 }
204 SPObject *elemref = document->getObjectById(getId().c_str());
205 if (elemref && sp_lpe_item) {
207 elemref->updateRepr(SP_OBJECT_WRITE_NO_CHILDREN | SP_OBJECT_WRITE_EXT);
208 } else {
209 add();
210 }
211}
212
213
215{
216 if (!_updating) {
217 upd();
218 }
219}
220
221void
223{
224 SPDocument *document = getSPDoc();
225 if (!document) {
226 return;
227 }
229 if (keep_paths || prefs->getBool("/options/onungroup", false)) {
230 SPObject *clip_path = sp_lpe_item->getClipObject();
231 if (clip_path) {
232 auto childitem = cast<SPLPEItem>(clip_path->childList(true).front());
233 childitem->deleteObject();
234 }
235 return;
236 }
237 _updating = true;
238 SPObject *elemref = document->getObjectById(getId().c_str());
239 if (elemref) {
240 elemref->deleteObject();
241 }
242 SPObject *clip_path = sp_lpe_item->getClipObject();
243 if (clip_path) {
244 std::vector<SPObject *> clip_path_list = clip_path->childList(true);
245 for (auto clip : clip_path_list) {
246 auto childitem = cast<SPLPEItem>(clip);
247 if (childitem) {
248 if (!childitem->style || childitem->style->display.set ||
249 childitem->style->display.value == SP_CSS_DISPLAY_NONE) {
250 childitem->style->display.set = TRUE;
251 childitem->style->display.value = SP_CSS_DISPLAY_BLOCK;
252 childitem->updateRepr(SP_OBJECT_WRITE_NO_CHILDREN | SP_OBJECT_WRITE_EXT);
253 }
254 }
255 }
256 }
257}
258
261 Geom::PathVector path_out = path_in;
262 if (flatten) {
264 std::unique_ptr<Geom::PathIntersectionGraph> pig(new Geom::PathIntersectionGraph(c_pv, path_out));
265 if (pig && !c_pv.empty() && !path_out.empty()) {
266 path_out = pig->getIntersection();
267 }
268 }
269 return path_out;
270}
271
273
275{
276 if (!sel->isEmpty()) {
277 auto selList = sel->items();
278 for (auto i = boost::rbegin(selList); i != boost::rend(selList); ++i) {
279 auto lpeitem = cast<SPLPEItem>(*i);
280 if (lpeitem) {
281 if (lpeitem->hasPathEffect() && lpeitem->pathEffectsEnabled()) {
282 PathEffectList path_effect_list(*lpeitem->path_effect_list);
283 for (auto &lperef : path_effect_list) {
284 LivePathEffectObject *lpeobj = lperef->lpeobject;
285 if (!lpeobj) {
290 g_warning("SPLPEItem::performPathEffect - NULL lpeobj in list!");
291 return;
292 }
293 if (LPETypeConverter.get_key(lpeobj->effecttype) == "powerclip") {
294 lpeitem->setCurrentPathEffect(lperef);
295 lpeitem->removeCurrentPathEffect(false);
296 break;
297 }
298 }
299 }
300 }
301 }
302 }
303}
304
306 if (!sel->isEmpty()) {
307 auto selList = sel->items();
308 for(auto i = boost::rbegin(selList); i != boost::rend(selList); ++i) {
309 auto lpeitem = cast<SPLPEItem>(*i);
310 if (lpeitem) {
311 SPClipPath *clip_path = lpeitem->getClipObject();
312 if(clip_path) {
313 std::vector<SPObject*> clip_path_list = clip_path->childList(true);
314 for (auto iter : clip_path_list) {
315 auto use = cast<SPUse>(iter);
316 if (use) {
317 g_warning("We can`t add inverse clip on clones");
318 return;
319 }
320 }
321 Effect::createAndApply(POWERCLIP, SP_ACTIVE_DOCUMENT, lpeitem);
322 Effect* lpe = lpeitem->getCurrentLPE();
323 if (lpe) {
324 lpe->getRepr()->setAttribute("inverse", "true");
325 }
326 }
327 }
328 }
329 }
330}
331
332}; //namespace LivePathEffect
333}; /* namespace Inkscape */
334
335/*
336 Local Variables:
337 mode:c++
338 c-file-style:"stroustrup"
339 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
340 indent-tabs-mode:nil
341 fill-column:99
342 End:
343*/
344// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Intermediate data for computing Boolean operations on paths.
Sequence of subpaths.
Definition pathvector.h:122
void push_back(Path const &path)
Append a path at the end.
Definition pathvector.h:172
bool empty() const
Check whether the vector contains any paths.
Definition pathvector.h:145
Sequence of contiguous curves, aka spline.
Definition path.h:353
Translation by a vector.
Definition transforms.h:115
void registerParameter(Parameter *param)
Definition effect.cpp:1704
static void createAndApply(const char *name, SPDocument *doc, SPItem *item)
Definition effect.cpp:1118
Inkscape::XML::Node * getRepr()
Definition effect.cpp:1928
LivePathEffectObject * getLPEObj()
Definition effect.h:150
Geom::PathVector doEffect_path(Geom::PathVector const &path_in) override
void doOnRemove(SPLPEItem const *) override
LPEPowerClip(LivePathEffectObject *lpeobject)
void doBeforeEffect(SPLPEItem const *lpeitem) override
Is performed each time before the effect is updated.
void doOnVisibilityToggled(SPLPEItem const *lpeitem) override
SPItemRange items()
Returns a range of selected SPItems.
Definition object-set.h:230
bool isEmpty()
Returns true if no items are selected.
Preference storage class.
Definition preferences.h:61
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
static Preferences * get()
Access the singleton Preferences object.
The set of selected SPObjects for a given document and layer model.
Definition selection.h:80
Interface for refcounted XML nodes.
Definition node.h:80
virtual void appendChild(Node *child)=0
Append a node as the last child of this node.
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
virtual Node * duplicate(Document *doc) const =0
Create a duplicate of this node.
void removeAttribute(Inkscape::Util::const_char_ptr key)
Remove an attribute of this node.
Definition node.h:280
Inkscape::LivePathEffect::EffectType effecttype
Definition lpeobject.h:39
Typed SVG document implementation.
Definition document.h:101
SPObject * getObjectById(std::string const &id) const
SPDefs * getDefs()
Return the main defs object for the document.
Definition document.cpp:247
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
Definition document.h:211
SPObject * getObjectByRepr(Inkscape::XML::Node *repr) const
Geom::OptRect visualBounds(Geom::Affine const &transform=Geom::identity(), bool wfilter=true, bool wclip=true, bool wmask=true) const
Get item's visual bounding box in this item's coordinate system.
Definition sp-item.cpp:924
SPClipPath * getClipObject() const
Definition sp-item.cpp:102
SPLPEItem * removeCurrentPathEffect(bool keep_paths)
If keep_path is true, the item should not be updated, effectively 'flattening' the LPE.
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
void setAttribute(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
std::vector< SPObject * > childList(bool add_ref, Action action=ActionGeneral)
Retrieves the children as a std vector object, optionally ref'ing the children in the process,...
Inkscape::XML::Node * updateRepr(unsigned int flags=SP_OBJECT_WRITE_EXT)
Updates the object's repr based on the object's state.
void deleteObject(bool propagate, bool propagate_descendants)
Deletes an object, unparenting it from its parent.
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
SPObject * appendChildRepr(Inkscape::XML::Node *repr)
Append repr as child of this object.
static char const *const parent
Definition dir-util.cpp:70
SPItem * item
Path intersection graph.
SBasisN< n > inverse(SBasisN< n > a, int k)
bool path_direction(Path const &p)
This function should only be applied to simple paths (regions), as otherwise a boolean winding direct...
Affine identity()
Create an identity matrix.
Definition affine.h:210
bool clip(std::vector< RatQuad > &rq, const xAx &cs, const Rect &R)
static R & release(R &r)
Decrements the reference count of a anchored object.
void sp_remove_powerclip(Inkscape::Selection *sel)
const EnumEffectDataConverter< EffectType > LPETypeConverter
defined in effect.cpp
void sp_inverse_powerclip(Inkscape::Selection *sel)
Geom::Path sp_bbox_without_clip(SPLPEItem *lpeitem)
Geom::PathVector sp_get_recursive_pathvector(SPLPEItem *item, Geom::PathVector res, bool dir, bool inverse)
Helper class to stream background task notifications as a series of messages.
void flatten(Geom::PathVector &pathv, FillRule fill_rule)
Path intersection.
Singleton class to access the preferences file in a convenient way.
Ocnode * child[8]
Definition quantize.cpp:33
Some things pertinent to all visible shapes: SPItem, SPItemView, SPItemCtx.
std::list< PathEffectSharedPtr > PathEffectList
Definition sp-lpe-item.h:45
Interface for XML documents.
Definition document.h:43
virtual Node * createElement(char const *name)=0
@ SP_CSS_DISPLAY_NONE
@ SP_CSS_DISPLAY_BLOCK
SPStyle - a style object for SPItem objects.
static void sp_svg_write_path(Inkscape::SVG::PathString &str, Geom::Path const &p, bool normalize=false)
Definition svg-path.cpp:109