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

Proposed by Jabiertxof
Status: Merged
Merged at revision: 15438
Proposed branch: lp:~inkscape.dev/inkscape/mirror_improvements
Merge into: lp:~inkscape.dev/inkscape/trunk
Diff against target: 508 lines (+291/-67)
2 files modified
src/live_effects/lpe-mirror_symmetry.cpp (+280/-65)
src/live_effects/lpe-mirror_symmetry.h (+11/-2)
To merge this branch: bzr merge lp:~inkscape.dev/inkscape/mirror_improvements
Reviewer Review Type Date Requested Status
Martin Owens Approve
Review via email: mp+315307@code.launchpad.net

Description of the change

Add multiple object feature to LPE. Allow add custom style to the mirror

NEEDS ROTATE COPYES MERGED

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

Some of this code is the same as the previous code. Which means they won't both merge cleanly.

This should be merged after the previous one or one of them should be the merge request and the other closed.

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

Remove some code and make dependant of rotate copies

15359. By Jabiertxof <jtx@jtx>

Fixes some compiling bug

Revision history for this message
Martin Owens (doctormo) wrote :

Look good from here.

review: Approve
15360. By Jabiertxof <jtx@jtx>

Bug fixes

15361. By Jabiertxof

Update to trunk

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/live_effects/lpe-mirror_symmetry.cpp'
2--- src/live_effects/lpe-mirror_symmetry.cpp 2016-12-26 12:54:09 +0000
3+++ src/live_effects/lpe-mirror_symmetry.cpp 2017-01-24 08:45:44 +0000
4@@ -15,11 +15,22 @@
5 */
6
7 #include <gtkmm.h>
8+#include "live_effects/lpeobject.h"
9+#include "live_effects/lpeobject-reference.h"
10 #include "live_effects/lpe-mirror_symmetry.h"
11-#include <display/curve.h>
12-#include <svg/path-string.h>
13-#include "helper/geom.h"
14-#include <2geom/path-intersection.h>
15+#include "display/curve.h"
16+#include "svg/path-string.h"
17+#include "svg/svg.h"
18+#include "sp-defs.h"
19+#include "helper/geom.h"
20+#include "2geom/intersection-graph.h"
21+#include "2geom/path-intersection.h"
22+#include "2geom/affine.h"
23+#include "helper/geom.h"
24+#include "sp-lpe-item.h"
25+#include "path-chemistry.h"
26+#include "style.h"
27+#include "xml/sp-css-attr.h"
28
29 // TODO due to internal breakage in glibmm headers, this must be last:
30 #include <glibmm/i18n.h>
31@@ -40,73 +51,69 @@
32 LPEMirrorSymmetry::LPEMirrorSymmetry(LivePathEffectObject *lpeobject) :
33 Effect(lpeobject),
34 mode(_("Mode"), _("Symmetry move mode"), "mode", MTConverter, &wr, this, MT_FREE),
35+ split_gap(_("Gap on split"), _("Gap on split"), "split_gap", &wr, this, -0.001),
36 discard_orig_path(_("Discard original path"), _("Check this to only keep the mirrored part of the path"), "discard_orig_path", &wr, this, false),
37 fuse_paths(_("Fuse paths"), _("Fuse original and the reflection into a single path"), "fuse_paths", &wr, this, false),
38 oposite_fuse(_("Opposite fuse"), _("Picks the other side of the mirror as the original"), "oposite_fuse", &wr, this, false),
39+ split_items(_("Split elements"), _("Split elements, this allow gradients and other paints."), "split_items", &wr, this, false),
40 start_point(_("Start mirror line"), _("Start mirror line"), "start_point", &wr, this, _("Adjust start of mirroring")),
41 end_point(_("End mirror line"), _("End mirror line"), "end_point", &wr, this, _("Adjust end of mirroring")),
42- center_point(_("Center mirror line"), _("Center mirror line"), "center_point", &wr, this, _("Adjust center of mirroring"))
43+ center_point(_("Center mirror line"), _("Center mirror line"), "center_point", &wr, this, _("Adjust center of mirroring")),
44+ id_origin("id origin", "store the id of the first LPEItem", "id_origin", &wr, this,"")
45 {
46 show_orig_path = true;
47 registerParameter(&mode);
48- registerParameter( &discard_orig_path);
49- registerParameter( &fuse_paths);
50- registerParameter( &oposite_fuse);
51- registerParameter( &start_point);
52- registerParameter( &end_point);
53- registerParameter( &center_point);
54+ registerParameter(&split_gap);
55+ registerParameter(&discard_orig_path);
56+ registerParameter(&fuse_paths);
57+ registerParameter(&oposite_fuse);
58+ registerParameter(&split_items);
59+ registerParameter(&start_point);
60+ registerParameter(&end_point);
61+ registerParameter(&center_point);
62+ registerParameter(&id_origin);
63+ id_origin.param_hide_canvas_text();
64+ split_gap.param_set_range(-999999.0, 999999.0);
65+ split_gap.param_set_increments(0.1, 0.1);
66+ split_gap.param_set_digits(5);
67+ apply_to_clippath_and_mask = true;
68 previous_center = Geom::Point(0,0);
69- apply_to_clippath_and_mask = true;
70 }
71
72 LPEMirrorSymmetry::~LPEMirrorSymmetry()
73 {
74 }
75
76-Gtk::Widget * LPEMirrorSymmetry::newWidget()
77+void
78+LPEMirrorSymmetry::doAfterEffect (SPLPEItem const* lpeitem)
79 {
80- // use manage here, because after deletion of Effect object, others might
81- // still be pointing to this widget.
82- Gtk::VBox *vbox = Gtk::manage(new Gtk::VBox(Effect::newWidget()));
83-
84- vbox->set_border_width(5);
85- vbox->set_homogeneous(false);
86- vbox->set_spacing(2);
87-
88- std::vector<Parameter *>::iterator it = param_vector.begin();
89- while (it != param_vector.end()) {
90- if ((*it)->widget_is_visible) {
91- Parameter *param = *it;
92- Gtk::Widget *widg = dynamic_cast<Gtk::Widget *>(param->param_newWidget());
93- Glib::ustring *tip = param->param_getTooltip();
94- if (widg) {
95- if (param->param_key != "center_point") {
96- vbox->pack_start(*widg, true, true, 2);
97- if (tip) {
98- widg->set_tooltip_text(*tip);
99- } else {
100- widg->set_tooltip_text("");
101- widg->set_has_tooltip(false);
102- }
103- }
104- }
105+ if (split_items && !discard_orig_path) {
106+ container = dynamic_cast<SPObject *>(sp_lpe_item->parent);
107+ SPDocument * doc = SP_ACTIVE_DOCUMENT;
108+ Inkscape::XML::Node *root = sp_lpe_item->document->getReprRoot();
109+ Inkscape::XML::Node *root_origin = doc->getReprRoot();
110+ if (root_origin != root) {
111+ return;
112 }
113-
114- ++it;
115+ Geom::Line ls((Geom::Point)start_point, (Geom::Point)end_point);
116+ Geom::Affine m = Geom::reflection (ls.vector(), (Geom::Point)start_point);
117+ m = m * sp_lpe_item->transform;
118+ toMirror(m);
119+ } else {
120+ processObjects(LPE_ERASE);
121+ items.clear();
122 }
123- return dynamic_cast<Gtk::Widget *>(vbox);
124 }
125
126-
127 void
128 LPEMirrorSymmetry::doBeforeEffect (SPLPEItem const* lpeitem)
129 {
130+
131 using namespace Geom;
132 original_bbox(lpeitem);
133 //center_point->param_set_liveupdate(false);
134 Point point_a(boundingbox_X.max(), boundingbox_Y.min());
135 Point point_b(boundingbox_X.max(), boundingbox_Y.max());
136- Point point_c(boundingbox_X.max(), boundingbox_Y.middle());
137 if (mode == MT_Y) {
138 point_a = Geom::Point(boundingbox_X.min(),center_point[Y]);
139 point_b = Geom::Point(boundingbox_X.max(),center_point[Y]);
140@@ -173,6 +180,172 @@
141 }
142
143 void
144+LPEMirrorSymmetry::cloneD(SPObject *origin, SPObject *dest, bool live, bool root)
145+{
146+ SPDocument * document = SP_ACTIVE_DOCUMENT;
147+ Inkscape::XML::Document *xml_doc = document->getReprDoc();
148+ if ( SP_IS_GROUP(origin) && SP_IS_GROUP(dest) && SP_GROUP(origin)->getItemCount() == SP_GROUP(dest)->getItemCount() ) {
149+ std::vector< SPObject * > childs = origin->childList(true);
150+ size_t index = 0;
151+ for (std::vector<SPObject * >::iterator obj_it = childs.begin();
152+ obj_it != childs.end(); ++obj_it) {
153+ SPObject *dest_child = dest->nthChild(index);
154+ cloneD(*obj_it, dest_child, live, false);
155+ index++;
156+ }
157+ }
158+ SPShape * shape = SP_SHAPE(origin);
159+ SPPath * path = SP_PATH(dest);
160+ if (!path && !SP_IS_GROUP(dest)) {
161+ Inkscape::XML::Node *dest_node = sp_selected_item_to_curved_repr(SP_ITEM(dest), 0);
162+ dest->updateRepr(xml_doc, dest_node, SP_OBJECT_WRITE_ALL);
163+ path = SP_PATH(dest);
164+ }
165+ if (path && shape) {
166+ if ( live) {
167+ SPCurve *c = NULL;
168+ if (root) {
169+ c = new SPCurve();
170+ c->set_pathvector(pathvector_before_effect);
171+ } else {
172+ c = shape->getCurve();
173+ }
174+ if (c) {
175+ path->setCurve(c, TRUE);
176+ c->unref();
177+ } else {
178+ dest->getRepr()->setAttribute("d", NULL);
179+ }
180+ } else {
181+ dest->getRepr()->setAttribute("d", origin->getRepr()->attribute("d"));
182+ }
183+ }
184+}
185+
186+void
187+LPEMirrorSymmetry::toMirror(Geom::Affine transform)
188+{
189+ SPDocument * document = SP_ACTIVE_DOCUMENT;
190+ Inkscape::XML::Document *xml_doc = document->getReprDoc();
191+ const char * id_origin_char = id_origin.param_getSVGValue();
192+ const char * elemref_id = g_strdup(Glib::ustring("mirror-").append(id_origin_char).c_str());
193+ items.clear();
194+ items.push_back(elemref_id);
195+ SPObject *elemref= NULL;
196+ Inkscape::XML::Node *phantom = NULL;
197+ if (elemref = document->getObjectById(elemref_id)) {
198+ phantom = elemref->getRepr();
199+ } else {
200+ phantom = sp_lpe_item->getRepr()->duplicate(xml_doc);
201+ std::vector<const char *> attrs;
202+ attrs.push_back("inkscape:path-effect");
203+ attrs.push_back("inkscape:original-d");
204+ attrs.push_back("sodipodi:type");
205+ attrs.push_back("sodipodi:rx");
206+ attrs.push_back("sodipodi:ry");
207+ attrs.push_back("sodipodi:cx");
208+ attrs.push_back("sodipodi:cy");
209+ attrs.push_back("sodipodi:end");
210+ attrs.push_back("sodipodi:start");
211+ attrs.push_back("inkscape:flatsided");
212+ attrs.push_back("inkscape:randomized");
213+ attrs.push_back("inkscape:rounded");
214+ attrs.push_back("sodipodi:arg1");
215+ attrs.push_back("sodipodi:arg2");
216+ attrs.push_back("sodipodi:r1");
217+ attrs.push_back("sodipodi:r2");
218+ attrs.push_back("sodipodi:sides");
219+ attrs.push_back("inkscape:randomized");
220+ attrs.push_back("sodipodi:argument");
221+ attrs.push_back("sodipodi:expansion");
222+ attrs.push_back("sodipodi:radius");
223+ attrs.push_back("sodipodi:revolution");
224+ attrs.push_back("sodipodi:t0");
225+ attrs.push_back("inkscape:randomized");
226+ attrs.push_back("inkscape:randomized");
227+ attrs.push_back("inkscape:randomized");
228+ attrs.push_back("x");
229+ attrs.push_back("y");
230+ attrs.push_back("rx");
231+ attrs.push_back("ry");
232+ attrs.push_back("width");
233+ attrs.push_back("height");
234+ for(const char * attr : attrs) {
235+ phantom->setAttribute(attr, NULL);
236+ }
237+ }
238+ phantom->setAttribute("id", elemref_id);
239+ if (!elemref) {
240+ elemref = container->appendChildRepr(phantom);
241+ Inkscape::GC::release(phantom);
242+ }
243+ cloneD(SP_OBJECT(sp_lpe_item), elemref, true, true);
244+ elemref->getRepr()->setAttribute("transform" , sp_svg_transform_write(transform));
245+ if (elemref->parent != container) {
246+ Inkscape::XML::Node *copy = phantom->duplicate(xml_doc);
247+ copy->setAttribute("id", elemref_id);
248+ container->appendChildRepr(copy);
249+ Inkscape::GC::release(copy);
250+ elemref->deleteObject();
251+ }
252+}
253+
254+Gtk::Widget *
255+LPEMirrorSymmetry::newWidget()
256+{
257+ // use manage here, because after deletion of Effect object, others might
258+ // still be pointing to this widget.
259+ Gtk::VBox *vbox = Gtk::manage(new Gtk::VBox(Effect::newWidget()));
260+ vbox->set_border_width(5);
261+ vbox->set_homogeneous(false);
262+ vbox->set_spacing(2);
263+
264+ std::vector<Parameter *>::iterator it = param_vector.begin();
265+ while (it != param_vector.end()) {
266+ if ((*it)->widget_is_visible) {
267+ Parameter * param = *it;
268+ if (param->param_key == "id_origin" || param->param_key == "center_point") {
269+ ++it;
270+ continue;
271+ }
272+ Gtk::Widget * widg = param->param_newWidget();
273+ Glib::ustring * tip = param->param_getTooltip();
274+ if (widg) {
275+ vbox->pack_start(*widg, true, true, 2);
276+ if (tip) {
277+ widg->set_tooltip_text(*tip);
278+ } else {
279+ widg->set_tooltip_text("");
280+ widg->set_has_tooltip(false);
281+ }
282+ }
283+ }
284+
285+ ++it;
286+ }
287+ this->upd_params = false;
288+ return dynamic_cast<Gtk::Widget *>(vbox);
289+}
290+
291+//TODO: Migrate the tree next function to effect.cpp/h to avoid duplication
292+void
293+LPEMirrorSymmetry::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/)
294+{
295+ processObjects(LPE_VISIBILITY);
296+}
297+
298+void
299+LPEMirrorSymmetry::doOnRemove (SPLPEItem const* /*lpeitem*/)
300+{
301+ //unset "erase_extra_objects" hook on sp-lpe-item.cpp
302+ if (!erase_extra_objects) {
303+ processObjects(LPE_TO_OBJECTS);
304+ return;
305+ }
306+ processObjects(LPE_ERASE);
307+}
308+
309+void
310 LPEMirrorSymmetry::transform_multiply(Geom::Affine const& postmul, bool set)
311 {
312 // cycle through all parameters. Most parameters will not need transformation, but path and point params do.
313@@ -193,18 +366,26 @@
314 Point point_a(boundingbox_X.max(), boundingbox_Y.min());
315 Point point_b(boundingbox_X.max(), boundingbox_Y.max());
316 Point point_c(boundingbox_X.max(), boundingbox_Y.middle());
317- start_point.param_setValue(point_a, true);
318+ start_point.param_setValue(point_a);
319 start_point.param_update_default(point_a);
320- end_point.param_setValue(point_b, true);
321+ end_point.param_setValue(point_b);
322 end_point.param_update_default(point_b);
323 center_point.param_setValue(point_c, true);
324 previous_center = center_point;
325+ SPLPEItem * splpeitem = const_cast<SPLPEItem *>(lpeitem);
326+ if (!lpeitem->hasPathEffectOfType(this->effectType(), false) ){ //first applied not ready yet
327+ id_origin.param_setValue(lpeitem->getRepr()->attribute("id"));
328+ id_origin.write_to_SVG();
329+ }
330 }
331
332
333 Geom::PathVector
334 LPEMirrorSymmetry::doEffect_path (Geom::PathVector const & path_in)
335 {
336+ if (split_items && !fuse_paths) {
337+ return path_in;
338+ }
339 Geom::PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(path_in);
340 Geom::PathVector path_out;
341
342@@ -214,15 +395,52 @@
343
344 Geom::Line line_separation((Geom::Point)start_point, (Geom::Point)end_point);
345 Geom::Affine m = Geom::reflection (line_separation.vector(), (Geom::Point)start_point);
346-
347- if (fuse_paths && !discard_orig_path) {
348+ if (split_items && fuse_paths) {
349+ Geom::OptRect bbox = sp_lpe_item->geometricBounds();
350+ Geom::Path p(Geom::Point(bbox->left(), bbox->top()));
351+ p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->top()));
352+ p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->bottom()));
353+ p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->bottom()));
354+ p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->top()));
355+ p.close();
356+ p *= Geom::Translate(bbox->midpoint()).inverse();
357+ p *= Geom::Scale(1.2);
358+ p *= Geom::Rotate(line_separation.angle());
359+ p *= Geom::Translate(bbox->midpoint());
360+ bbox = p.boundsFast();
361+ p.clear();
362+ p.start(Geom::Point(bbox->left(), bbox->top()));
363+ p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->top()));
364+ p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->bottom()));
365+ p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->bottom()));
366+ p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->top()));
367+ p.close();
368+ p *= Geom::Translate(bbox->midpoint()).inverse();
369+ p *= Geom::Rotate(line_separation.angle());
370+ p *= Geom::Translate(bbox->midpoint());
371+ Geom::Point base(p.pointAt(3));
372+ if (oposite_fuse) {
373+ base = p.pointAt(0);
374+ }
375+ p *= Geom::Translate(line_separation.pointAt(line_separation.nearestTime(base)) - base);
376+ Geom::PathVector pv_bbox;
377+ pv_bbox.push_back(p);
378+ Geom::PathIntersectionGraph *pig = new Geom::PathIntersectionGraph(pv_bbox, original_pathv);
379+ if (pig && !original_pathv.empty() && !pv_bbox.empty()) {
380+ path_out = pig->getBminusA();
381+ }
382+ Geom::Point dir = rot90(unit_vector((Geom::Point)start_point - (Geom::Point)end_point));
383+ Geom::Point gap = dir * split_gap;
384+ m *= Geom::Translate(gap);
385+ path_out *= m;
386+ } else if (fuse_paths && !discard_orig_path) {
387 for (Geom::PathVector::const_iterator path_it = original_pathv.begin();
388 path_it != original_pathv.end(); ++path_it)
389 {
390 if (path_it->empty()) {
391 continue;
392 }
393- Geom::PathVector tmp_path;
394+ Geom::PathVector tmp_pathvector;
395 double time_start = 0.0;
396 int position = 0;
397 bool end_open = false;
398@@ -268,11 +486,11 @@
399 Geom::Path mirror = portion.reversed() * m;
400 mirror.setInitial(portion.finalPoint());
401 portion.append(mirror);
402- if(i!=0) {
403+ if(i != 0) {
404 portion.setFinal(portion.initialPoint());
405 portion.close();
406 }
407- tmp_path.push_back(portion);
408+ tmp_pathvector.push_back(portion);
409 }
410 portion.clear();
411 }
412@@ -293,36 +511,33 @@
413 portion.append(mirror);
414 portion = portion.reversed();
415 if (!original.closed()) {
416- tmp_path.push_back(portion);
417+ tmp_pathvector.push_back(portion);
418 } else {
419- if (cs.size() > 1 && tmp_path.size() > 0 && tmp_path[0].size() > 0 ) {
420- portion.setFinal(tmp_path[0].initialPoint());
421- portion.setInitial(tmp_path[0].finalPoint());
422- tmp_path[0].append(portion);
423+ if (cs.size() > 1 && tmp_pathvector.size() > 0 && tmp_pathvector[0].size() > 0 ) {
424+ portion.setFinal(tmp_pathvector[0].initialPoint());
425+ portion.setInitial(tmp_pathvector[0].finalPoint());
426+ tmp_pathvector[0].append(portion);
427 } else {
428- tmp_path.push_back(portion);
429+ tmp_pathvector.push_back(portion);
430 }
431- tmp_path[0].close();
432+ tmp_pathvector[0].close();
433 }
434 portion.clear();
435 }
436 }
437 }
438 if (cs.size() == 0 && position == 1) {
439- tmp_path.push_back(original);
440- tmp_path.push_back(original * m);
441+ tmp_pathvector.push_back(original);
442+ tmp_pathvector.push_back(original * m);
443 }
444- path_out.insert(path_out.end(), tmp_path.begin(), tmp_path.end());
445- tmp_path.clear();
446+ path_out.insert(path_out.end(), tmp_pathvector.begin(), tmp_pathvector.end());
447+ tmp_pathvector.clear();
448 }
449- }
450-
451- if (!fuse_paths || discard_orig_path) {
452+ } else if (!fuse_paths || discard_orig_path) {
453 for (size_t i = 0; i < original_pathv.size(); ++i) {
454 path_out.push_back(original_pathv[i] * m);
455 }
456 }
457-
458 return path_out;
459 }
460
461
462=== modified file 'src/live_effects/lpe-mirror_symmetry.h'
463--- src/live_effects/lpe-mirror_symmetry.h 2016-12-18 23:19:40 +0000
464+++ src/live_effects/lpe-mirror_symmetry.h 2017-01-24 08:45:44 +0000
465@@ -18,8 +18,8 @@
466
467 #include "live_effects/effect.h"
468 #include "live_effects/parameter/parameter.h"
469+#include "live_effects/parameter/text.h"
470 #include "live_effects/parameter/point.h"
471-#include "live_effects/parameter/path.h"
472 #include "live_effects/parameter/enum.h"
473 #include "live_effects/lpegroupbbox.h"
474
475@@ -41,23 +41,32 @@
476 virtual ~LPEMirrorSymmetry();
477 virtual void doOnApply (SPLPEItem const* lpeitem);
478 virtual void doBeforeEffect (SPLPEItem const* lpeitem);
479+ virtual void doAfterEffect (SPLPEItem const* lpeitem);
480 virtual void transform_multiply(Geom::Affine const& postmul, bool set);
481 virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in);
482+ virtual void doOnRemove (SPLPEItem const* /*lpeitem*/);
483+ virtual void doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/);
484 virtual Gtk::Widget * newWidget();
485+ void toMirror(Geom::Affine transform);
486+ // void cloneAttrbutes(Inkscape::XML::Node * origin, Inkscape::XML::Node * dest, const char * first_attribute, ...);
487+ void cloneD(SPObject *origin, SPObject *dest, bool live, bool root);
488
489 protected:
490 virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec);
491
492 private:
493 EnumParam<ModeType> mode;
494+ ScalarParam split_gap;
495 BoolParam discard_orig_path;
496 BoolParam fuse_paths;
497 BoolParam oposite_fuse;
498+ BoolParam split_items;
499 PointParam start_point;
500 PointParam end_point;
501 PointParam center_point;
502+ TextParam id_origin;
503 Geom::Point previous_center;
504-
505+ SPObject * container;
506 LPEMirrorSymmetry(const LPEMirrorSymmetry&);
507 LPEMirrorSymmetry& operator=(const LPEMirrorSymmetry&);
508 };