Merge lp:~inkscape.dev/inkscape/rotatecopies_improvements into lp:~inkscape.dev/inkscape/trunk

Proposed by Jabiertxof
Status: Merged
Merged at revision: 15436
Proposed branch: lp:~inkscape.dev/inkscape/rotatecopies_improvements
Merge into: lp:~inkscape.dev/inkscape/trunk
Diff against target: 1185 lines (+508/-120)
16 files modified
src/2geom/intersection-graph.cpp (+3/-3)
src/helper/geom.cpp (+7/-0)
src/helper/geom.h (+1/-1)
src/live_effects/effect.cpp (+84/-8)
src/live_effects/effect.h (+15/-5)
src/live_effects/lpe-copy_rotate.cpp (+296/-10)
src/live_effects/lpe-copy_rotate.h (+14/-0)
src/live_effects/lpe-measure-line.cpp (+7/-59)
src/live_effects/lpe-measure-line.h (+0/-8)
src/live_effects/parameter/bool.cpp (+2/-1)
src/live_effects/parameter/text.cpp (+2/-1)
src/sp-item-group.cpp (+31/-12)
src/sp-lpe-item.cpp (+25/-10)
src/sp-lpe-item.h (+3/-2)
src/sp-object.cpp (+15/-0)
src/sp-object.h (+3/-0)
To merge this branch: bzr merge lp:~inkscape.dev/inkscape/rotatecopies_improvements
Reviewer Review Type Date Requested Status
Martin Owens Needs Fixing
Review via email: mp+315306@code.launchpad.net

Description of the change

With this improvements you can separate each piece of a fused to add own styles, for example rotating with gradients, adding diferent styles to each pieze....

To post a comment you must log in.
Revision history for this message
Martin Owens (doctormo) wrote :

There is a todo in src/2geom/intersection-graph.cpp:9 which should be dealt with

File src/document-undo.cpp shouldn't change in this merge.

I don't like the list of phantom attribute creators, it seems like it could be a loop instead.

Another ToDo in lpe-copy-rotate

Everything else looks good.

review: Needs Fixing
15399. By Jabiertxof <jtx@jtx>

Add fixes sugested by Martin Owens

15400. By Jabiertxof <jtx@jtx>

Fixing to merge

15401. By Jabiertxof <jtx@jtx>

Some bug fix to prepare merge

15402. By Jabiertxof <jtx@jtx>

Fixes some compiling bug

15403. By Jabiertxof <jtx@jtx>

Bug fixes

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/2geom/intersection-graph.cpp'
2--- src/2geom/intersection-graph.cpp 2016-02-08 07:32:51 +0000
3+++ src/2geom/intersection-graph.cpp 2017-01-24 07:18:19 +0000
4@@ -410,10 +410,10 @@
5 assert(!result.back().empty());
6 }
7
8- /*if (n_processed != size() * 2) {
9+ if (n_processed != size() * 2) {
10 std::cerr << "Processed " << n_processed << " intersections, expected " << (size() * 2) << std::endl;
11- }*/
12- assert(n_processed == size() * 2);
13+ }
14+ //assert(n_processed == size() * 2);
15
16 return result;
17 }
18
19=== modified file 'src/helper/geom.cpp'
20--- src/helper/geom.cpp 2016-08-03 13:29:38 +0000
21+++ src/helper/geom.cpp 2017-01-24 07:18:19 +0000
22@@ -843,6 +843,13 @@
23 recursive_bezier4(x1234, y1234, x234, y234, x34, y34, x4, y4, m_points, level + 1);
24 }
25
26+void
27+swap(Geom::Point &A, Geom::Point &B){
28+ Geom::Point tmp = A;
29+ A = B;
30+ B = tmp;
31+}
32+
33 /*
34 Local Variables:
35 mode:c++
36
37=== modified file 'src/helper/geom.h'
38--- src/helper/geom.h 2015-05-08 15:40:38 +0000
39+++ src/helper/geom.h 2017-01-24 07:18:19 +0000
40@@ -32,7 +32,7 @@
41 const double x3, const double y3, const double x4, const double y4,
42 std::vector<Geom::Point> &pointlist,
43 int level);
44-
45+void swap(Geom::Point &A, Geom::Point &B);
46 #endif // INKSCAPE_HELPER_GEOM_H
47
48 /*
49
50=== modified file 'src/live_effects/effect.cpp'
51--- src/live_effects/effect.cpp 2016-12-19 20:54:42 +0000
52+++ src/live_effects/effect.cpp 2017-01-24 07:18:19 +0000
53@@ -70,6 +70,8 @@
54 #include "ui/tools/node-tool.h"
55 #include "ui/tools-switch.h"
56 #include "knotholder.h"
57+#include "path-chemistry.h"
58+#include "xml/sp-css-attr.h"
59 #include "live_effects/lpeobject.h"
60 #include "display/curve.h"
61
62@@ -356,6 +358,7 @@
63 sp_lpe_item(NULL),
64 current_zoom(1),
65 upd_params(true),
66+ sp_shape(NULL),
67 sp_curve(NULL),
68 provides_own_flash_paths(true), // is automatically set to false if providesOwnFlashPaths() is not overridden
69 is_ready(false) // is automatically set to false if providesOwnFlashPaths() is not overridden
70@@ -392,17 +395,17 @@
71 }
72
73 void
74+Effect::setCurrentZoom(double cZ)
75+{
76+ current_zoom = cZ;
77+}
78+
79+void
80 Effect::setSelectedNodePoints(std::vector<Geom::Point> sNP)
81 {
82 selectedNodesPoints = sNP;
83 }
84
85-void
86-Effect::setCurrentZoom(double cZ)
87-{
88- current_zoom = cZ;
89-}
90-
91 bool
92 Effect::isNodePointSelected(Geom::Point const &nodePoint) const
93 {
94@@ -423,6 +426,74 @@
95 return false;
96 }
97
98+void
99+Effect::processObjects(LpeAction lpe_action)
100+{
101+ SPDocument * document = SP_ACTIVE_DOCUMENT;
102+ for (std::vector<const char *>::iterator el_it = items.begin();
103+ el_it != items.end(); ++el_it) {
104+ const char * id = *el_it;
105+ if (!id || strlen(id) == 0) {
106+ return;
107+ }
108+ SPObject *elemref = NULL;
109+ if (elemref = document->getObjectById(id)) {
110+ Inkscape::XML::Node * elemnode = elemref->getRepr();
111+ std::vector<SPItem*> item_list;
112+ item_list.push_back(SP_ITEM(elemref));
113+ std::vector<Inkscape::XML::Node*> item_to_select;
114+ std::vector<SPItem*> item_selected;
115+ SPCSSAttr *css;
116+ Glib::ustring css_str;
117+ switch (lpe_action){
118+ case LPE_TO_OBJECTS:
119+ if (SP_ITEM(elemref)->isHidden()) {
120+ elemref->deleteObject();
121+ } else {
122+ if (elemnode->attribute("inkscape:path-effect")) {
123+ sp_item_list_to_curves(item_list, item_selected, item_to_select);
124+ }
125+ elemnode->setAttribute("sodipodi:insensitive", NULL);
126+ }
127+ break;
128+
129+ case LPE_ERASE:
130+ elemref->deleteObject();
131+ break;
132+
133+ case LPE_VISIBILITY:
134+ css = sp_repr_css_attr_new();
135+ sp_repr_css_attr_add_from_string(css, elemref->getRepr()->attribute("style"));
136+ if (!this->isVisible()/* && std::strcmp(elemref->getId(),sp_lpe_item->getId()) != 0*/) {
137+ css->setAttribute("display", "none");
138+ } else {
139+ css->setAttribute("display", NULL);
140+ }
141+ sp_repr_css_write_string(css,css_str);
142+ elemnode->setAttribute("style", css_str.c_str());
143+ break;
144+
145+ default:
146+ break;
147+ }
148+ }
149+ }
150+ if (lpe_action == LPE_ERASE || lpe_action == LPE_TO_OBJECTS) {
151+ items.clear();
152+ }
153+}
154+
155+void Effect::setCurrentShape(SPShape * shape){
156+ if(shape){
157+ sp_shape = shape;
158+ if (!(sp_curve = sp_shape->getCurve())) {
159+ // oops
160+ return;
161+ }
162+ pathvector_before_effect = sp_curve->get_pathvector();
163+ }
164+}
165+
166 /**
167 * Is performed each time before the effect is updated.
168 */
169@@ -446,8 +517,12 @@
170 void Effect::doOnApply_impl(SPLPEItem const* lpeitem)
171 {
172 sp_lpe_item = const_cast<SPLPEItem *>(lpeitem);
173- /*sp_curve = SP_SHAPE(sp_lpe_item)->getCurve();
174- pathvector_before_effect = sp_curve->get_pathvector();*/
175+ sp_curve = SP_SHAPE(sp_lpe_item)->getCurve();
176+ pathvector_before_effect = sp_curve->get_pathvector();
177+ SPShape * shape = dynamic_cast<SPShape *>(sp_lpe_item);
178+ if(shape){
179+ setCurrentShape(shape);
180+ }
181 doOnApply(lpeitem);
182 }
183
184@@ -457,6 +532,7 @@
185 //printf("(SPLPEITEM*) %p\n", sp_lpe_item);
186 SPShape * shape = dynamic_cast<SPShape *>(sp_lpe_item);
187 if(shape){
188+ setCurrentShape(shape);
189 sp_curve = shape->getCurve();
190 pathvector_before_effect = sp_curve->get_pathvector();
191 }
192
193=== modified file 'src/live_effects/effect.h'
194--- src/live_effects/effect.h 2016-12-19 20:54:42 +0000
195+++ src/live_effects/effect.h 2017-01-24 07:18:19 +0000
196@@ -19,7 +19,7 @@
197 class SPDocument;
198 class SPDesktop;
199 class SPItem;
200-class LivePathEffectObject;
201+class LivePathEffectObject;
202 class SPLPEItem;
203 class KnotHolder;
204 class KnotHolderEntity;
205@@ -44,6 +44,12 @@
206 DEFAULT
207 };
208
209+enum LpeAction {
210+ LPE_ERASE = 0,
211+ LPE_TO_OBJECTS,
212+ LPE_VISIBILITY
213+};
214+
215 class Effect {
216 public:
217 static Effect* New(EffectType lpenr, LivePathEffectObject *lpeobj);
218@@ -73,6 +79,9 @@
219 static int acceptsNumClicks(EffectType type);
220 int acceptsNumClicks() const { return acceptsNumClicks(effectType()); }
221 void doAcceptPathPreparations(SPLPEItem *lpeitem);
222+ SPShape * getCurrentShape(){ return sp_shape; };
223+ void setCurrentShape(SPShape * shape);
224+ void processObjects(LpeAction lpe_action);
225
226 /*
227 * isReady() indicates whether all preparations which are necessary to apply the LPE are done,
228@@ -125,7 +134,9 @@
229 bool apply_to_clippath_and_mask;
230 bool erase_extra_objects; // set this to false allow retain extra generated objects, see measure line LPE
231 bool upd_params;
232-
233+ BoolParam is_visible;
234+ SPCurve * sp_curve;
235+ Geom::PathVector pathvector_before_effect;
236 protected:
237 Effect(LivePathEffectObject *lpeobject);
238
239@@ -150,7 +161,6 @@
240 bool _provides_knotholder_entities;
241
242 int oncanvasedit_it;
243- BoolParam is_visible;
244
245 bool show_orig_path; // set this to true in derived effects to automatically have the original
246 // path displayed as helperpath
247@@ -164,10 +174,10 @@
248 bool concatenate_before_pwd2;
249
250 SPLPEItem * sp_lpe_item; // these get stored in doBeforeEffect_impl, and derived classes may do as they please with them.
251+ SPShape * sp_shape; // these get stored in doBeforeEffect_impl before doEffect chain, or in performPathEffects on groups, and derived classes may do as they please with them.
252+ std::vector<const char *> items;
253 double current_zoom;
254 std::vector<Geom::Point> selectedNodesPoints;
255- SPCurve * sp_curve;
256- Geom::PathVector pathvector_before_effect;
257 private:
258 bool provides_own_flash_paths; // if true, the standard flash path is suppressed
259
260
261=== modified file 'src/live_effects/lpe-copy_rotate.cpp'
262--- src/live_effects/lpe-copy_rotate.cpp 2017-01-01 03:08:02 +0000
263+++ src/live_effects/lpe-copy_rotate.cpp 2017-01-24 07:18:19 +0000
264@@ -17,6 +17,13 @@
265 #include <2geom/sbasis-to-bezier.h>
266 #include <2geom/intersection-graph.h>
267 #include "live_effects/lpe-copy_rotate.h"
268+#include "display/curve.h"
269+#include "svg/path-string.h"
270+#include "svg/svg.h"
271+#include "style.h"
272+#include "helper/geom.h"
273+#include "xml/sp-css-attr.h"
274+#include "path-chemistry.h"
275 // TODO due to internal breakage in glibmm headers, this must be last:
276 #include <glibmm/i18n.h>
277
278@@ -45,9 +52,11 @@
279 starting_angle(_("Starting angle"), _("Angle of the first copy"), "starting_angle", &wr, this, 0.0),
280 rotation_angle(_("Rotation angle"), _("Angle between two successive copies"), "rotation_angle", &wr, this, 60.0),
281 num_copies(_("Number of copies"), _("Number of copies of the original path"), "num_copies", &wr, this, 6),
282+ split_gap(_("Gap on split"), _("Gap on split"), "split_gap", &wr, this, -0.001),
283 copies_to_360(_("360º Copies"), _("No rotation angle, fixed to 360º"), "copies_to_360", &wr, this, true),
284 fuse_paths(_("Kaleidoskope"), _("Kaleidoskope by helper line, use fill-rule: evenodd for best result"), "fuse_paths", &wr, this, false),
285 join_paths(_("Join paths"), _("Join paths, use fill-rule: evenodd for best result"), "join_paths", &wr, this, false),
286+ split_items(_("Split elements"), _("Split elements, this allow gradients and other paints."), "split_items", &wr, this, false),
287 dist_angle_handle(100.0)
288 {
289 show_orig_path = true;
290@@ -56,15 +65,21 @@
291 registerParameter(&copies_to_360);
292 registerParameter(&fuse_paths);
293 registerParameter(&join_paths);
294+ registerParameter(&split_items);
295 registerParameter(&starting_angle);
296 registerParameter(&starting_point);
297 registerParameter(&rotation_angle);
298 registerParameter(&num_copies);
299+ registerParameter(&split_gap);
300 registerParameter(&origin);
301-
302+ split_gap.param_set_range(-999999.0, 999999.0);
303+ split_gap.param_set_increments(0.1, 0.1);
304+ split_gap.param_set_digits(5);
305+ num_copies.param_set_range(0, 999999);
306 num_copies.param_make_integer(true);
307- num_copies.param_set_range(0, 1000);
308 apply_to_clippath_and_mask = true;
309+ previous_num_copies = num_copies;
310+ reset = false;
311 }
312
313 LPECopyRotate::~LPECopyRotate()
314@@ -72,6 +87,207 @@
315
316 }
317
318+void
319+LPECopyRotate::doAfterEffect (SPLPEItem const* lpeitem)
320+{
321+ if (split_items) {
322+ SPDocument * document = SP_ACTIVE_DOCUMENT;
323+ items.clear();
324+ container = dynamic_cast<SPObject *>(sp_lpe_item->parent);
325+ SPDocument * doc = SP_ACTIVE_DOCUMENT;
326+ Inkscape::XML::Node *root = sp_lpe_item->document->getReprRoot();
327+ Inkscape::XML::Node *root_origin = doc->getReprRoot();
328+ if (root_origin != root) {
329+ return;
330+ }
331+ if (previous_num_copies != num_copies) {
332+ gint numcopies_gap = previous_num_copies - num_copies;
333+ if (numcopies_gap > 0 && num_copies != 0) {
334+ guint counter = num_copies - 1;
335+ while (numcopies_gap > 0) {
336+ const char * id = g_strdup(Glib::ustring("rotated-").append(std::to_string(counter)).append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str());
337+ if (!id || strlen(id) == 0) {
338+ return;
339+ }
340+ SPObject *elemref = NULL;
341+ if (elemref = document->getObjectById(id)) {
342+ SP_ITEM(elemref)->setHidden(true);
343+ }
344+ counter++;
345+ numcopies_gap--;
346+ }
347+ }
348+ previous_num_copies = num_copies;
349+ }
350+ SPObject *elemref = NULL;
351+ char * id = g_strdup(Glib::ustring("rotated-").append("1").append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str());
352+ guint counter = 0;
353+ while(elemref = document->getObjectById(id)) {
354+ if (SP_ITEM(elemref)->isHidden()) {
355+ items.push_back(id);
356+ }
357+ id = g_strdup(Glib::ustring("rotated-").append(std::to_string(counter)).append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str());
358+ counter++;
359+ }
360+ g_free(id);
361+ double diagonal = Geom::distance(Geom::Point(boundingbox_X.min(),boundingbox_Y.min()),Geom::Point(boundingbox_X.max(),boundingbox_Y.max()));
362+ Geom::Rect bbox(Geom::Point(boundingbox_X.min(),boundingbox_Y.min()),Geom::Point(boundingbox_X.max(),boundingbox_Y.max()));
363+ double size_divider = Geom::distance(origin,bbox) + (diagonal * 2);
364+ Geom::Point line_start = origin + dir * Geom::Rotate(-(Geom::rad_from_deg(starting_angle))) * size_divider;
365+ Geom::Point line_end = origin + dir * Geom::Rotate(-(Geom::rad_from_deg(rotation_angle + starting_angle))) * size_divider;
366+ Geom::Affine m = Geom::Translate(-origin) * Geom::Rotate(-(Geom::rad_from_deg(starting_angle)));
367+ if (fuse_paths) {
368+ size_t rest = 0;
369+ for (size_t i = 1; i < num_copies; ++i) {
370+ Geom::Affine r = Geom::identity();
371+ Geom::Point dir = unit_vector((Geom::Point)origin - Geom::middle_point(line_start,line_end));
372+ if( rest%2 == 0) {
373+ r *= Geom::Rotate(Geom::Angle(dir)).inverse();
374+ r *= Geom::Scale(1, -1);
375+ r *= Geom::Rotate(Geom::Angle(dir));
376+ }
377+ Geom::Rotate rot(-(Geom::rad_from_deg(rotation_angle * i)));
378+ Geom::Affine t = m * r * rot * Geom::Rotate(Geom::rad_from_deg(starting_angle)) * Geom::Translate(origin);
379+ if( rest%2 == 0) {
380+ t = m * r * rot * Geom::Rotate(Geom::rad_from_deg(starting_angle)).inverse() * Geom::Translate(origin);
381+ }
382+ t *= sp_lpe_item->transform;
383+ toItem(t, i-1, reset);
384+ rest ++;
385+ }
386+ } else {
387+ for (size_t i = 1; i < num_copies; ++i) {
388+ Geom::Rotate rot(-(Geom::rad_from_deg(rotation_angle * i)));
389+ Geom::Affine t = m * rot * Geom::Rotate(Geom::rad_from_deg(starting_angle)) * Geom::Translate(origin);
390+ t *= sp_lpe_item->transform;
391+ toItem(t, i - 1, reset);
392+ }
393+ }
394+ reset = false;
395+ } else {
396+ processObjects(LPE_ERASE);
397+ items.clear();
398+ }
399+
400+ std::cout << previous_num_copies << "previous_num_copies\n";
401+ std::cout << num_copies << "num_copies\n";
402+}
403+
404+void
405+LPECopyRotate::cloneD(SPObject *origin, SPObject *dest, bool root, bool reset)
406+{
407+ SPDocument * document = SP_ACTIVE_DOCUMENT;
408+ Inkscape::XML::Document *xml_doc = document->getReprDoc();
409+ if ( SP_IS_GROUP(origin) && SP_IS_GROUP(dest) && SP_GROUP(origin)->getItemCount() == SP_GROUP(dest)->getItemCount() ) {
410+ std::vector< SPObject * > childs = origin->childList(true);
411+ size_t index = 0;
412+ for (std::vector<SPObject * >::iterator obj_it = childs.begin();
413+ obj_it != childs.end(); ++obj_it) {
414+ SPObject *dest_child = dest->nthChild(index);
415+ cloneD(*obj_it, dest_child, false, reset);
416+ index++;
417+ }
418+ }
419+ SPShape * shape = SP_SHAPE(origin);
420+ SPPath * path = SP_PATH(dest);
421+ if (!path && !SP_IS_GROUP(dest)) {
422+ Inkscape::XML::Node *dest_node = sp_selected_item_to_curved_repr(SP_ITEM(dest), 0);
423+ dest->updateRepr(xml_doc, dest_node, SP_OBJECT_WRITE_ALL);
424+ path = SP_PATH(dest);
425+ }
426+ if (path && shape) {
427+ SPCurve *c = NULL;
428+ if (root) {
429+ c = new SPCurve();
430+ c->set_pathvector(pathvector_before_effect);
431+ } else {
432+ c = shape->getCurve();
433+ }
434+ if (c) {
435+ path->setCurve(c, TRUE);
436+ c->unref();
437+ } else {
438+ dest->getRepr()->setAttribute("d", NULL);
439+ }
440+ if (reset) {
441+ dest->getRepr()->setAttribute("style", shape->getRepr()->attribute("style"));
442+ }
443+ }
444+}
445+
446+void
447+LPECopyRotate::toItem(Geom::Affine transform, size_t i, bool reset)
448+{
449+ SPDocument * document = SP_ACTIVE_DOCUMENT;
450+ Inkscape::XML::Document *xml_doc = document->getReprDoc();
451+ const char * elemref_id = g_strdup(Glib::ustring("rotated-").append(std::to_string(i)).append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str());
452+ items.push_back(elemref_id);
453+ SPObject *elemref= NULL;
454+ Inkscape::XML::Node *phantom = NULL;
455+ if (elemref = document->getObjectById(elemref_id)) {
456+ phantom = elemref->getRepr();
457+ } else {
458+ phantom = sp_lpe_item->getRepr()->duplicate(xml_doc);
459+ std::vector<const char *> attrs;
460+ attrs.push_back("inkscape:path-effect");
461+ attrs.push_back("inkscape:original-d");
462+ attrs.push_back("sodipodi:type");
463+ attrs.push_back("sodipodi:rx");
464+ attrs.push_back("sodipodi:ry");
465+ attrs.push_back("sodipodi:cx");
466+ attrs.push_back("sodipodi:cy");
467+ attrs.push_back("sodipodi:end");
468+ attrs.push_back("sodipodi:start");
469+ attrs.push_back("inkscape:flatsided");
470+ attrs.push_back("inkscape:randomized");
471+ attrs.push_back("inkscape:rounded");
472+ attrs.push_back("sodipodi:arg1");
473+ attrs.push_back("sodipodi:arg2");
474+ attrs.push_back("sodipodi:r1");
475+ attrs.push_back("sodipodi:r2");
476+ attrs.push_back("sodipodi:sides");
477+ attrs.push_back("inkscape:randomized");
478+ attrs.push_back("sodipodi:argument");
479+ attrs.push_back("sodipodi:expansion");
480+ attrs.push_back("sodipodi:radius");
481+ attrs.push_back("sodipodi:revolution");
482+ attrs.push_back("sodipodi:t0");
483+ attrs.push_back("inkscape:randomized");
484+ attrs.push_back("inkscape:randomized");
485+ attrs.push_back("inkscape:randomized");
486+ attrs.push_back("x");
487+ attrs.push_back("y");
488+ attrs.push_back("rx");
489+ attrs.push_back("ry");
490+ attrs.push_back("width");
491+ attrs.push_back("height");
492+ phantom->setAttribute("id", elemref_id);
493+ for(const char * attr : attrs) {
494+ phantom->setAttribute(attr, NULL);
495+ }
496+ }
497+ if (!elemref) {
498+ elemref = container->appendChildRepr(phantom);
499+ Inkscape::GC::release(phantom);
500+ }
501+ cloneD(SP_OBJECT(sp_lpe_item), elemref, true, reset);
502+ elemref->getRepr()->setAttribute("transform" , sp_svg_transform_write(transform));
503+ SP_ITEM(elemref)->setHidden(false);
504+ if (elemref->parent != container) {
505+ Inkscape::XML::Node *copy = phantom->duplicate(xml_doc);
506+ copy->setAttribute("id", elemref_id);
507+ container->appendChildRepr(copy);
508+ Inkscape::GC::release(copy);
509+ elemref->deleteObject();
510+ }
511+}
512+
513+void
514+LPECopyRotate::resetStyles(){
515+ reset = true;
516+ doAfterEffect(sp_lpe_item);
517+}
518+
519 Gtk::Widget * LPECopyRotate::newWidget()
520 {
521 // use manage here, because after deletion of Effect object, others might
522@@ -81,7 +297,15 @@
523 vbox->set_border_width(5);
524 vbox->set_homogeneous(false);
525 vbox->set_spacing(2);
526-
527+ Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false,0));
528+ Gtk::VBox * vbox_expander = Gtk::manage( new Gtk::VBox(Effect::newWidget()) );
529+ vbox_expander->set_border_width(0);
530+ vbox_expander->set_spacing(2);
531+ Gtk::Button * reset_button = Gtk::manage(new Gtk::Button(Glib::ustring(_("Reset styles"))));
532+ reset_button->signal_clicked().connect(sigc::mem_fun (*this,&LPECopyRotate::resetStyles));
533+ reset_button->set_size_request(140,30);
534+ vbox->pack_start(*hbox, true,true,2);
535+ hbox->pack_start(*reset_button, false, false,2);
536 std::vector<Parameter *>::iterator it = param_vector.begin();
537 while (it != param_vector.end()) {
538 if ((*it)->widget_is_visible) {
539@@ -125,11 +349,11 @@
540 LPECopyRotate::transform_multiply(Geom::Affine const& postmul, bool set)
541 {
542 // cycle through all parameters. Most parameters will not need transformation, but path and point params do.
543-
544 for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); ++it) {
545 Parameter * param = *it;
546 param->param_transform_multiply(postmul, set);
547 }
548+ sp_lpe_item_update_patheffect(sp_lpe_item, false, false);
549 }
550
551 void
552@@ -140,17 +364,17 @@
553 if (copies_to_360) {
554 rotation_angle.param_set_value(360.0/(double)num_copies);
555 }
556- if (fuse_paths && rotation_angle * num_copies > 360 && rotation_angle > 0) {
557+ if (fuse_paths && rotation_angle * num_copies > 360.1 && rotation_angle > 0) {
558 num_copies.param_set_value(floor(360/rotation_angle));
559 }
560 if (fuse_paths && copies_to_360) {
561- num_copies.param_set_increments(2,2);
562+ num_copies.param_set_increments(2.0,10.0);
563 if ((int)num_copies%2 !=0) {
564 num_copies.param_set_value(num_copies+1);
565 rotation_angle.param_set_value(360.0/(double)num_copies);
566 }
567 } else {
568- num_copies.param_set_increments(1,1);
569+ num_copies.param_set_increments(1.0, 10.0);
570 }
571
572 if (dist_angle_handle < 1.0) {
573@@ -352,12 +576,57 @@
574 tmp_path.clear();
575 }
576
577+Geom::PathVector
578+LPECopyRotate::doEffect_path (Geom::PathVector const & path_in)
579+{
580+ Geom::PathVector path_out;
581+ if (split_items && (fuse_paths || join_paths)) {
582+ if (num_copies == 0) {
583+ return path_out;
584+ }
585+ path_out = pathv_to_linear_and_cubic_beziers(path_in);
586+ double diagonal = Geom::distance(Geom::Point(boundingbox_X.min(),boundingbox_Y.min()),Geom::Point(boundingbox_X.max(),boundingbox_Y.max()));
587+ Geom::OptRect bbox = sp_lpe_item->geometricBounds();
588+ double size_divider = Geom::distance(origin,bbox) + (diagonal * 2);
589+ Geom::Point line_start = origin + dir * Geom::Rotate(-(Geom::rad_from_deg(starting_angle))) * size_divider;
590+ Geom::Point line_end = origin + dir * Geom::Rotate(-(Geom::rad_from_deg(rotation_angle + starting_angle))) * size_divider;
591+ Geom::Path divider = Geom::Path(line_start);
592+ divider.appendNew<Geom::LineSegment>((Geom::Point)origin);
593+ divider.appendNew<Geom::LineSegment>(line_end);
594+ divider.close();
595+ Geom::PathVector triangle;
596+ triangle.push_back(divider);
597+ Geom::PathIntersectionGraph *pig = new Geom::PathIntersectionGraph(triangle, path_out);
598+ if (pig && ! path_out.empty() && !triangle.empty()) {
599+ //TODO: Here can produce a crash because some knows problems in new boolops code
600+ path_out = pig->getIntersection();
601+ }
602+ Geom::Affine r = Geom::identity();
603+ Geom::Point dir = unit_vector(Geom::middle_point(line_start,line_end) - (Geom::Point)origin);
604+ Geom::Point gap = dir * split_gap;
605+ r *= Geom::Translate(gap);
606+ path_out *= r;
607+ } else {
608+ // default behavior
609+ for (unsigned int i=0; i < path_in.size(); i++) {
610+ Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in = path_in[i].toPwSb();
611+ Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
612+ Geom::PathVector path = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
613+ // add the output path vector to the already accumulated vector:
614+ for (unsigned int j=0; j < path.size(); j++) {
615+ path_out.push_back(path[j]);
616+ }
617+ }
618+ }
619+ return pathv_to_linear_and_cubic_beziers(path_out);
620+}
621+
622 Geom::Piecewise<Geom::D2<Geom::SBasis> >
623 LPECopyRotate::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
624 {
625 using namespace Geom;
626
627- if (num_copies == 1 && !fuse_paths) {
628+ if ((num_copies == 1 && !fuse_paths) || split_items) {
629 return pwd2_in;
630 }
631
632@@ -373,10 +642,10 @@
633 divider.appendNew<Geom::LineSegment>(line_end);
634 Piecewise<D2<SBasis> > output;
635 Affine pre = Translate(-origin) * Rotate(-rad_from_deg(starting_angle));
636+ PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(path_from_piecewise(remove_short_cuts(pwd2_in, 0.1), 0.001));
637 if (fuse_paths) {
638 Geom::PathVector path_out;
639 Geom::PathVector tmp_path;
640- PathVector const original_pathv = path_from_piecewise(remove_short_cuts(pwd2_in, 0.1), 0.001);
641 for (Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) {
642 if (path_it->empty()) {
643 continue;
644@@ -403,7 +672,7 @@
645 output = paths_to_pw(path_out);
646 }
647 } else {
648- Geom::PathVector output_pv = path_from_piecewise(output , 0.01);
649+ Geom::PathVector output_pv;
650 for (int i = 0; i < num_copies; ++i) {
651 Rotate rot(-rad_from_deg(rotation_angle * i));
652 Affine t = pre * rot * Translate(origin);
653@@ -449,6 +718,23 @@
654 original_bbox(SP_LPE_ITEM(item));
655 }
656
657+void
658+LPECopyRotate::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/)
659+{
660+ processObjects(LPE_VISIBILITY);
661+}
662+
663+void
664+LPECopyRotate::doOnRemove (SPLPEItem const* /*lpeitem*/)
665+{
666+ //unset "erase_extra_objects" hook on sp-lpe-item.cpp
667+ if (!erase_extra_objects) {
668+ processObjects(LPE_TO_OBJECTS);
669+ return;
670+ }
671+ processObjects(LPE_ERASE);
672+}
673+
674 } //namespace LivePathEffect
675 } /* namespace Inkscape */
676
677
678=== modified file 'src/live_effects/lpe-copy_rotate.h'
679--- src/live_effects/lpe-copy_rotate.h 2017-01-01 03:08:02 +0000
680+++ src/live_effects/lpe-copy_rotate.h 2017-01-24 07:18:19 +0000
681@@ -15,6 +15,8 @@
682 */
683
684 #include "live_effects/effect.h"
685+#include "live_effects/parameter/parameter.h"
686+#include "live_effects/parameter/text.h"
687 #include "live_effects/parameter/point.h"
688 #include "live_effects/lpegroupbbox.h"
689
690@@ -26,13 +28,20 @@
691 LPECopyRotate(LivePathEffectObject *lpeobject);
692 virtual ~LPECopyRotate();
693 virtual void doOnApply (SPLPEItem const* lpeitem);
694+ virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in);
695 virtual Geom::Piecewise<Geom::D2<Geom::SBasis> > doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in);
696 virtual void doBeforeEffect (SPLPEItem const* lpeitem);
697+ virtual void doAfterEffect (SPLPEItem const* lpeitem);
698 virtual void setFusion(Geom::PathVector &path_in, Geom::Path divider, double sizeDivider);
699 virtual void split(Geom::PathVector &path_in, Geom::Path const &divider);
700 virtual void resetDefaults(SPItem const* item);
701 virtual void transform_multiply(Geom::Affine const& postmul, bool set);
702+ virtual void doOnRemove (SPLPEItem const* /*lpeitem*/);
703+ virtual void doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/);
704 virtual Gtk::Widget * newWidget();
705+ void toItem(Geom::Affine transform, size_t i, bool reset);
706+ void cloneD(SPObject *origin, SPObject *dest, bool root, bool reset);
707+ void resetStyles();
708 protected:
709 virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec);
710
711@@ -42,9 +51,11 @@
712 ScalarParam starting_angle;
713 ScalarParam rotation_angle;
714 ScalarParam num_copies;
715+ ScalarParam split_gap;
716 BoolParam copies_to_360;
717 BoolParam fuse_paths;
718 BoolParam join_paths;
719+ BoolParam split_items;
720 Geom::Point A;
721 Geom::Point B;
722 Geom::Point dir;
723@@ -52,6 +63,9 @@
724 Geom::Point rot_pos;
725 Geom::Point previous_start_point;
726 double dist_angle_handle;
727+ double previous_num_copies;
728+ bool reset;
729+ SPObject * container;
730 LPECopyRotate(const LPECopyRotate&);
731 LPECopyRotate& operator=(const LPECopyRotate&);
732 };
733
734=== modified file 'src/live_effects/lpe-measure-line.cpp'
735--- src/live_effects/lpe-measure-line.cpp 2016-12-30 16:11:45 +0000
736+++ src/live_effects/lpe-measure-line.cpp 2017-01-24 07:18:19 +0000
737@@ -21,7 +21,9 @@
738 #include "svg/svg-color.h"
739 #include "svg/svg.h"
740 #include "display/curve.h"
741+#include "helper/geom.h"
742 #include "2geom/affine.h"
743+#include "path-chemistry.h"
744 #include "style.h"
745 #include "sp-root.h"
746 #include "sp-defs.h"
747@@ -165,12 +167,6 @@
748
749 LPEMeasureLine::~LPEMeasureLine() {}
750
751-void swap(Geom::Point &A, Geom::Point &B){
752- Geom::Point tmp = A;
753- A = B;
754- B = tmp;
755-}
756-
757 void
758 LPEMeasureLine::createArrowMarker(const char * mode)
759 {
760@@ -229,7 +225,7 @@
761 elemref = SP_OBJECT(document->getDefs()->appendChildRepr(arrow));
762 Inkscape::GC::release(arrow);
763 }
764- elements.push_back(mode);
765+ items.push_back(mode);
766 }
767
768 void
769@@ -369,7 +365,7 @@
770 copy->setAttribute("id", id);
771 elemref = elemref_copy;
772 }
773- elements.push_back(id);
774+ items.push_back(id);
775 Geom::OptRect bounds = SP_ITEM(elemref)->bounds(SPItem::GEOMETRIC_BBOX);
776 if (bounds) {
777 anotation_width = bounds->width() * 1.4;
778@@ -483,7 +479,7 @@
779 elemref->deleteObject();
780 copy->setAttribute("id", id);
781 }
782- elements.push_back(id);
783+ items.push_back(id);
784 }
785
786 void
787@@ -535,7 +531,7 @@
788 sp_lpe_item->getCurrentLPE() != this){
789 return;
790 }
791- elements.clear();
792+ items.clear();
793 start_stored = start;
794 end_stored = end;
795 Geom::Point hstart = start;
796@@ -680,60 +676,12 @@
797 //unset "erase_extra_objects" hook on sp-lpe-item.cpp
798 if (!erase_extra_objects) {
799 processObjects(LPE_TO_OBJECTS);
800- elements.clear();
801+ items.clear();
802 return;
803 }
804 processObjects(LPE_ERASE);
805 }
806
807-void
808-LPEMeasureLine::processObjects(LpeAction lpe_action)
809-{
810- SPDocument * document = SP_ACTIVE_DOCUMENT;
811- for (std::vector<const char *>::iterator el_it = elements.begin();
812- el_it != elements.end(); ++el_it) {
813- const char * id = *el_it;
814- if (!id || strlen(id) == 0) {
815- return;
816- }
817- SPObject *elemref = NULL;
818- if (elemref = document->getObjectById(id)) {
819- SPCSSAttr *css;
820- Glib::ustring css_str;
821- switch (lpe_action){
822- case LPE_TO_OBJECTS:
823- elemref->getRepr()->setAttribute("inkscape:path-effect", NULL);
824- elemref->getRepr()->setAttribute("sodipodi:insensitive", NULL);
825- break;
826-
827- case LPE_ERASE:
828- if (std::strcmp(elemref->getId(),id_origin.param_getSVGValue()) != 0) {
829- elemref->deleteObject();
830- }
831- break;
832-
833- case LPE_VISIBILITY:
834- css = sp_repr_css_attr_new();
835- sp_repr_css_attr_add_from_string(css, elemref->getRepr()->attribute("style"));
836- if (!this->isVisible() && std::strcmp(elemref->getId(),id_origin.param_getSVGValue()) != 0) {
837- css->setAttribute("display", "none");
838- } else {
839- css->setAttribute("display", NULL);
840- }
841- sp_repr_css_write_string(css,css_str);
842- elemref->getRepr()->setAttribute("style", css_str.c_str());
843- break;
844-
845- default:
846- break;
847- }
848- }
849- }
850- if (lpe_action == LPE_ERASE) {
851- elements.clear();
852- }
853-}
854-
855 Gtk::Widget *LPEMeasureLine::newWidget()
856 {
857 // use manage here, because after deletion of Effect object, others might
858
859=== modified file 'src/live_effects/lpe-measure-line.h'
860--- src/live_effects/lpe-measure-line.h 2016-12-26 23:00:38 +0000
861+++ src/live_effects/lpe-measure-line.h 2017-01-24 07:18:19 +0000
862@@ -35,12 +35,6 @@
863 OM_END
864 };
865
866-enum LpeAction {
867- LPE_ERASE = 0,
868- LPE_TO_OBJECTS,
869- LPE_VISIBILITY
870-};
871-
872 class LPEMeasureLine : public Effect {
873 public:
874 LPEMeasureLine(LivePathEffectObject *lpeobject);
875@@ -51,7 +45,6 @@
876 virtual void doEffect (SPCurve * curve){}; //stop the chain
877 virtual void doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/);
878 virtual Geom::PathVector doEffect_path(Geom::PathVector const &path_in);
879- void processObjects(LpeAction lpe_action);
880 void createLine(Geom::Point start,Geom::Point end, const char * id, bool main, bool overflow, bool remove, bool arrows = false);
881 void createTextLabel(Geom::Point pos, double length, Geom::Coord angle, bool remove, bool valid);
882 void onExpanderChanged();
883@@ -92,7 +85,6 @@
884 double arrow_gap;
885 Geom::Point start_stored;
886 Geom::Point end_stored;
887- std::vector<const char *> elements;
888 /* Geom::Affine affine_over;*/
889 LPEMeasureLine(const LPEMeasureLine &);
890 LPEMeasureLine &operator=(const LPEMeasureLine &);
891
892=== modified file 'src/live_effects/parameter/bool.cpp'
893--- src/live_effects/parameter/bool.cpp 2016-07-24 23:43:33 +0000
894+++ src/live_effects/parameter/bool.cpp 2017-01-24 07:18:19 +0000
895@@ -72,7 +72,7 @@
896 checkwdg->setActive(value);
897 checkwdg->setProgrammatically = false;
898 checkwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change bool parameter"));
899-
900+ param_effect->upd_params = false;
901 return dynamic_cast<Gtk::Widget *> (checkwdg);
902 } else {
903 return NULL;
904@@ -82,6 +82,7 @@
905 void
906 BoolParam::param_setValue(bool newvalue)
907 {
908+ param_effect->upd_params = true;
909 value = newvalue;
910 }
911
912
913=== modified file 'src/live_effects/parameter/text.cpp'
914--- src/live_effects/parameter/text.cpp 2016-12-06 20:41:55 +0000
915+++ src/live_effects/parameter/text.cpp 2017-01-24 07:18:19 +0000
916@@ -125,13 +125,14 @@
917 rsu->setProgrammatically = false;
918
919 rsu->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change text parameter"));
920-
921+ param_effect->upd_params = false;
922 return dynamic_cast<Gtk::Widget *> (rsu);
923 }
924
925 void
926 TextParam::param_setValue(const Glib::ustring newvalue)
927 {
928+ param_effect->upd_params = true;
929 value = newvalue;
930 if (!_hide_canvas_text) {
931 sp_canvastext_set_text (canvas_text, newvalue.c_str());
932
933=== modified file 'src/sp-item-group.cpp'
934--- src/sp-item-group.cpp 2016-12-15 11:26:38 +0000
935+++ src/sp-item-group.cpp 2017-01-24 07:18:19 +0000
936@@ -925,6 +925,15 @@
937 }
938
939 sp_group_perform_patheffect(this, this, write);
940+
941+ for (PathEffectList::iterator it = this->path_effect_list->begin(); it != this->path_effect_list->end(); ++it)
942+ {
943+ LivePathEffectObject *lpeobj = (*it)->lpeobject;
944+
945+ if (lpeobj && lpeobj->get_lpe()) {
946+ lpeobj->get_lpe()->doAfterEffect(this);
947+ }
948+ }
949 }
950 }
951
952@@ -950,25 +959,35 @@
953 } else {
954 c = subShape->getCurve();
955 }
956-
957+ bool success = false;
958 // only run LPEs when the shape has a curve defined
959 if (c) {
960 c->transform(i2anc_affine(subitem, topgroup));
961- topgroup->performPathEffect(c);
962+ success = topgroup->performPathEffect(c, subShape);
963 c->transform(i2anc_affine(subitem, topgroup).inverse());
964- subShape->setCurve(c, TRUE);
965-
966- if (write) {
967- Inkscape::XML::Node *repr = subitem->getRepr();
968- gchar *str = sp_svg_write_path(c->get_pathvector());
969- repr->setAttribute("d", str);
970+ Inkscape::XML::Node *repr = subitem->getRepr();
971+ if (c && success) {
972+ subShape->setCurve(c, TRUE);
973+ if (write) {
974+ gchar *str = sp_svg_write_path(c->get_pathvector());
975+ repr->setAttribute("d", str);
976 #ifdef GROUP_VERBOSE
977- g_message("sp_group_perform_patheffect writes 'd' attribute");
978+ g_message("sp_group_perform_patheffect writes 'd' attribute");
979 #endif
980- g_free(str);
981+ g_free(str);
982+ }
983+ c->unref();
984+ } else {
985+ // LPE was unsuccesfull or doeffect stack return null. Read the old 'd'-attribute.
986+ if (gchar const * value = repr->attribute("d")) {
987+ Geom::PathVector pv = sp_svg_read_pathv(value);
988+ SPCurve *oldcurve = new (std::nothrow) SPCurve(pv);
989+ if (oldcurve) {
990+ subShape->setCurve(oldcurve, TRUE);
991+ oldcurve->unref();
992+ }
993+ }
994 }
995-
996- c->unref();
997 }
998 }
999 }
1000
1001=== modified file 'src/sp-lpe-item.cpp'
1002--- src/sp-lpe-item.cpp 2016-12-07 22:17:49 +0000
1003+++ src/sp-lpe-item.cpp 2017-01-24 07:18:19 +0000
1004@@ -24,6 +24,8 @@
1005 #include "live_effects/lpeobject.h"
1006 #include "live_effects/lpeobject-reference.h"
1007 #include "live_effects/lpe-measure-line.h"
1008+#include "live_effects/lpe-mirror_symmetry.h"
1009+#include "live_effects/lpe-copy_rotate.h"
1010
1011 #include "sp-path.h"
1012 #include "sp-item-group.h"
1013@@ -126,7 +128,10 @@
1014 if (!value) {
1015 LivePathEffectObject *lpeobj = (*it)->lpeobject;
1016 Inkscape::LivePathEffect::Effect * lpe = lpeobj->get_lpe();
1017- if (dynamic_cast<Inkscape::LivePathEffect::LPEMeasureLine *>(lpe)){
1018+ if (dynamic_cast<Inkscape::LivePathEffect::LPEMirrorSymmetry *>(lpe) ||
1019+ dynamic_cast<Inkscape::LivePathEffect::LPEMeasureLine *>(lpe) ||
1020+ dynamic_cast<Inkscape::LivePathEffect::LPECopyRotate *>(lpe) )
1021+ {
1022 lpe->doOnRemove(this);
1023 }
1024 }
1025@@ -209,7 +214,7 @@
1026 /**
1027 * returns true when LPE was successful.
1028 */
1029-bool SPLPEItem::performPathEffect(SPCurve *curve, bool is_clip_or_mask) {
1030+bool SPLPEItem::performPathEffect(SPCurve *curve, SPShape *current, bool is_clip_or_mask) {
1031
1032 if (!curve) {
1033 return false;
1034@@ -241,12 +246,15 @@
1035 }
1036 if (!is_clip_or_mask || (is_clip_or_mask && lpe->apply_to_clippath_and_mask)) {
1037 // Groups have their doBeforeEffect called elsewhere
1038+ if (current) {
1039+ lpe->setCurrentShape(current);
1040+ }
1041 if (!SP_IS_GROUP(this)) {
1042 lpe->doBeforeEffect_impl(this);
1043 }
1044
1045 try {
1046- lpe->doEffect(curve);
1047+ lpe->doEffect(curve);
1048 }
1049 catch (std::exception & e) {
1050 g_warning("Exception during LPE %s execution. \n %s", lpe->getName().c_str(), e.what());
1051@@ -257,6 +265,8 @@
1052 return false;
1053 }
1054 if (!SP_IS_GROUP(this)) {
1055+ lpe->pathvector_before_effect = curve->get_pathvector();
1056+ lpe->sp_curve->set_pathvector(lpe->pathvector_before_effect);
1057 lpe->doAfterEffect(this);
1058 }
1059 }
1060@@ -604,7 +614,7 @@
1061 return true;
1062 }
1063
1064-bool SPLPEItem::hasPathEffectOfType(int const type) const
1065+bool SPLPEItem::hasPathEffectOfType(int const type, bool is_ready) const
1066 {
1067 if (path_effect_list->empty()) {
1068 return false;
1069@@ -616,7 +626,9 @@
1070 if (lpeobj) {
1071 Inkscape::LivePathEffect::Effect const* lpe = lpeobj->get_lpe();
1072 if (lpe && (lpe->effectType() == type)) {
1073- return true;
1074+ if (is_ready || lpe->isReady()) {
1075+ return true;
1076+ }
1077 }
1078 }
1079 }
1080@@ -695,10 +707,10 @@
1081 try {
1082 if(SP_IS_GROUP(this)){
1083 c->transform(i2anc_affine(SP_GROUP(item), SP_GROUP(this)));
1084- success = this->performPathEffect(c, true);
1085+ success = this->performPathEffect(c, SP_SHAPE(clip_mask), true);
1086 c->transform(i2anc_affine(SP_GROUP(item), SP_GROUP(this)).inverse());
1087 } else {
1088- success = this->performPathEffect(c, true);
1089+ success = this->performPathEffect(c, SP_SHAPE(clip_mask), true);
1090 }
1091 } catch (std::exception & e) {
1092 g_warning("Exception during LPE execution. \n %s", e.what());
1093@@ -709,12 +721,13 @@
1094 success = false;
1095 }
1096 Inkscape::XML::Node *repr = clip_mask->getRepr();
1097- if (success) {
1098+ // This c check allow to not apply LPE if curve is NULL after performPathEffect used in clone.obgets LPE
1099+ if (success && c) {
1100 gchar *str = sp_svg_write_path(c->get_pathvector());
1101 repr->setAttribute("d", str);
1102 g_free(str);
1103 } else {
1104- // LPE was unsuccesfull. Read the old 'd'-attribute.
1105+ // LPE was unsuccesfull or doeffect stack return null.. Read the old 'd'-attribute.
1106 if (gchar const * value = repr->attribute("d")) {
1107 Geom::PathVector pv = sp_svg_read_pathv(value);
1108 SPCurve *oldcurve = new (std::nothrow) SPCurve(pv);
1109@@ -724,7 +737,9 @@
1110 }
1111 }
1112 }
1113- c->unref();
1114+ if (c) {
1115+ c->unref();
1116+ }
1117 }
1118 }
1119 }
1120
1121=== modified file 'src/sp-lpe-item.h'
1122--- src/sp-lpe-item.h 2016-03-19 11:17:59 +0000
1123+++ src/sp-lpe-item.h 2017-01-24 07:18:19 +0000
1124@@ -23,6 +23,7 @@
1125
1126 class LivePathEffectObject;
1127 class SPCurve;
1128+class SPShape;
1129 class SPDesktop;
1130
1131 namespace Inkscape{
1132@@ -69,11 +70,11 @@
1133
1134 virtual void update_patheffect(bool write);
1135
1136- bool performPathEffect(SPCurve *curve, bool is_clip_or_mask = false);
1137+ bool performPathEffect(SPCurve *curve, SPShape *current = NULL, bool is_clip_or_mask = false);
1138
1139 bool pathEffectsEnabled() const;
1140 bool hasPathEffect() const;
1141- bool hasPathEffectOfType(int const type) const;
1142+ bool hasPathEffectOfType(int const type, bool is_ready = true) const;
1143 bool hasPathEffectRecursive() const;
1144 Inkscape::LivePathEffect::Effect* getPathEffectOfType(int type);
1145 Inkscape::LivePathEffect::Effect const* getPathEffectOfType(int type) const;
1146
1147=== modified file 'src/sp-object.cpp'
1148--- src/sp-object.cpp 2017-01-07 15:32:48 +0000
1149+++ src/sp-object.cpp 2017-01-24 07:18:19 +0000
1150@@ -775,6 +775,21 @@
1151 repr->appendChild(child);
1152 }
1153
1154+SPObject* SPObject::nthChild(unsigned index) {
1155+ g_assert(this->repr);
1156+ if (hasChildren()) {
1157+ std::vector<SPObject*> l;
1158+ unsigned counter = 0;
1159+ for (auto& child: children) {
1160+ if (counter == index) {
1161+ return &child;
1162+ }
1163+ counter++;
1164+ }
1165+ }
1166+ return NULL;
1167+}
1168+
1169 void SPObject::addChild(Inkscape::XML::Node *child, Inkscape::XML::Node * prev)
1170 {
1171 g_assert(this->repr);
1172
1173=== modified file 'src/sp-object.h'
1174--- src/sp-object.h 2016-11-05 16:51:16 +0000
1175+++ src/sp-object.h 2017-01-24 07:18:19 +0000
1176@@ -318,6 +318,9 @@
1177 SPObject *lastChild() { return children.empty() ? nullptr : &children.back(); }
1178 SPObject const *lastChild() const { return children.empty() ? nullptr : &children.back(); }
1179
1180+ SPObject *nthChild(unsigned index);
1181+ SPObject const *nthChild(unsigned index) const;
1182+
1183 enum Action { ActionGeneral, ActionBBox, ActionUpdate, ActionShow };
1184
1185 /**