diff options
| author | Jabier Arraiza Cenoz <jabier.arraiza@marker.es> | 2017-01-24 09:32:24 +0000 |
|---|---|---|
| committer | jabiertxof <info@marker.es> | 2017-01-24 09:32:24 +0000 |
| commit | 7fecbddd1fb8b57f9c6efd3204645e33d9b38d09 (patch) | |
| tree | ce9f038347327a219b55fae5be1f6b30d91d1522 /src | |
| parent | Remove some code and make dependant of rotate copies (diff) | |
| download | inkscape-7fecbddd1fb8b57f9c6efd3204645e33d9b38d09.tar.gz inkscape-7fecbddd1fb8b57f9c6efd3204645e33d9b38d09.zip | |
update to trunk
(bzr r15356.1.18)
Diffstat (limited to 'src')
| -rw-r--r-- | src/2geom/intersection-graph.cpp | 6 | ||||
| -rw-r--r-- | src/helper/geom.cpp | 7 | ||||
| -rw-r--r-- | src/helper/geom.h | 2 | ||||
| -rw-r--r-- | src/live_effects/effect.cpp | 88 | ||||
| -rw-r--r-- | src/live_effects/effect.h | 20 | ||||
| -rw-r--r-- | src/live_effects/lpe-copy_rotate.cpp | 306 | ||||
| -rw-r--r-- | src/live_effects/lpe-copy_rotate.h | 14 | ||||
| -rw-r--r-- | src/live_effects/lpe-measure-line.cpp | 66 | ||||
| -rw-r--r-- | src/live_effects/lpe-measure-line.h | 8 | ||||
| -rw-r--r-- | src/live_effects/lpe-mirror_symmetry.cpp | 342 | ||||
| -rw-r--r-- | src/live_effects/lpe-mirror_symmetry.h | 13 | ||||
| -rw-r--r-- | src/live_effects/parameter/bool.cpp | 3 | ||||
| -rw-r--r-- | src/live_effects/parameter/text.cpp | 3 | ||||
| -rw-r--r-- | src/sp-item-group.cpp | 43 | ||||
| -rw-r--r-- | src/sp-lpe-item.cpp | 35 | ||||
| -rw-r--r-- | src/sp-lpe-item.h | 5 | ||||
| -rw-r--r-- | src/sp-object.cpp | 15 | ||||
| -rw-r--r-- | src/sp-object.h | 3 | ||||
| -rw-r--r-- | src/sp-path.cpp | 31 | ||||
| -rw-r--r-- | src/ui/dialog/polar-arrange-tab.cpp | 4 |
20 files changed, 812 insertions, 202 deletions
diff --git a/src/2geom/intersection-graph.cpp b/src/2geom/intersection-graph.cpp index d469d3ffc..cac010942 100644 --- a/src/2geom/intersection-graph.cpp +++ b/src/2geom/intersection-graph.cpp @@ -410,10 +410,10 @@ PathVector PathIntersectionGraph::_getResult(bool enter_a, bool enter_b) assert(!result.back().empty()); } - /*if (n_processed != size() * 2) { + if (n_processed != size() * 2) { std::cerr << "Processed " << n_processed << " intersections, expected " << (size() * 2) << std::endl; - }*/ - assert(n_processed == size() * 2); + } + //assert(n_processed == size() * 2); return result; } diff --git a/src/helper/geom.cpp b/src/helper/geom.cpp index 42c494c00..e1f05c3ce 100644 --- a/src/helper/geom.cpp +++ b/src/helper/geom.cpp @@ -843,6 +843,13 @@ recursive_bezier4(const double x1, const double y1, recursive_bezier4(x1234, y1234, x234, y234, x34, y34, x4, y4, m_points, level + 1); } +void +swap(Geom::Point &A, Geom::Point &B){ + Geom::Point tmp = A; + A = B; + B = tmp; +} + /* Local Variables: mode:c++ diff --git a/src/helper/geom.h b/src/helper/geom.h index d49e2070c..b3d907e51 100644 --- a/src/helper/geom.h +++ b/src/helper/geom.h @@ -32,7 +32,7 @@ void recursive_bezier4(const double x1, const double y1, const double x2, const const double x3, const double y3, const double x4, const double y4, std::vector<Geom::Point> &pointlist, int level); - +void swap(Geom::Point &A, Geom::Point &B); #endif // INKSCAPE_HELPER_GEOM_H /* diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index 9d9381294..50e2aa353 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -70,6 +70,8 @@ #include "ui/tools/node-tool.h" #include "ui/tools-switch.h" #include "knotholder.h" +#include "path-chemistry.h" +#include "xml/sp-css-attr.h" #include "live_effects/lpeobject.h" #include "display/curve.h" @@ -356,6 +358,7 @@ Effect::Effect(LivePathEffectObject *lpeobject) sp_lpe_item(NULL), current_zoom(1), upd_params(true), + sp_shape(NULL), sp_curve(NULL), provides_own_flash_paths(true), // is automatically set to false if providesOwnFlashPaths() is not overridden is_ready(false) // is automatically set to false if providesOwnFlashPaths() is not overridden @@ -392,15 +395,15 @@ Effect::doOnApply (SPLPEItem const*/*lpeitem*/) } void -Effect::setSelectedNodePoints(std::vector<Geom::Point> sNP) +Effect::setCurrentZoom(double cZ) { - selectedNodesPoints = sNP; + current_zoom = cZ; } void -Effect::setCurrentZoom(double cZ) +Effect::setSelectedNodePoints(std::vector<Geom::Point> sNP) { - current_zoom = cZ; + selectedNodesPoints = sNP; } bool @@ -423,6 +426,74 @@ Effect::isNodePointSelected(Geom::Point const &nodePoint) const return false; } +void +Effect::processObjects(LpeAction lpe_action) +{ + SPDocument * document = SP_ACTIVE_DOCUMENT; + for (std::vector<const char *>::iterator el_it = items.begin(); + el_it != items.end(); ++el_it) { + const char * id = *el_it; + if (!id || strlen(id) == 0) { + return; + } + SPObject *elemref = NULL; + if (elemref = document->getObjectById(id)) { + Inkscape::XML::Node * elemnode = elemref->getRepr(); + std::vector<SPItem*> item_list; + item_list.push_back(SP_ITEM(elemref)); + std::vector<Inkscape::XML::Node*> item_to_select; + std::vector<SPItem*> item_selected; + SPCSSAttr *css; + Glib::ustring css_str; + switch (lpe_action){ + case LPE_TO_OBJECTS: + if (SP_ITEM(elemref)->isHidden()) { + elemref->deleteObject(); + } else { + if (elemnode->attribute("inkscape:path-effect")) { + sp_item_list_to_curves(item_list, item_selected, item_to_select); + } + elemnode->setAttribute("sodipodi:insensitive", NULL); + } + break; + + case LPE_ERASE: + elemref->deleteObject(); + break; + + case LPE_VISIBILITY: + css = sp_repr_css_attr_new(); + sp_repr_css_attr_add_from_string(css, elemref->getRepr()->attribute("style")); + if (!this->isVisible()/* && std::strcmp(elemref->getId(),sp_lpe_item->getId()) != 0*/) { + css->setAttribute("display", "none"); + } else { + css->setAttribute("display", NULL); + } + sp_repr_css_write_string(css,css_str); + elemnode->setAttribute("style", css_str.c_str()); + break; + + default: + break; + } + } + } + if (lpe_action == LPE_ERASE || lpe_action == LPE_TO_OBJECTS) { + items.clear(); + } +} + +void Effect::setCurrentShape(SPShape * shape){ + if(shape){ + sp_shape = shape; + if (!(sp_curve = sp_shape->getCurve())) { + // oops + return; + } + pathvector_before_effect = sp_curve->get_pathvector(); + } +} + /** * Is performed each time before the effect is updated. */ @@ -446,8 +517,12 @@ void Effect::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/) void Effect::doOnApply_impl(SPLPEItem const* lpeitem) { sp_lpe_item = const_cast<SPLPEItem *>(lpeitem); - /*sp_curve = SP_SHAPE(sp_lpe_item)->getCurve(); - pathvector_before_effect = sp_curve->get_pathvector();*/ + sp_curve = SP_SHAPE(sp_lpe_item)->getCurve(); + pathvector_before_effect = sp_curve->get_pathvector(); + SPShape * shape = dynamic_cast<SPShape *>(sp_lpe_item); + if(shape){ + setCurrentShape(shape); + } doOnApply(lpeitem); } @@ -457,6 +532,7 @@ void Effect::doBeforeEffect_impl(SPLPEItem const* lpeitem) //printf("(SPLPEITEM*) %p\n", sp_lpe_item); SPShape * shape = dynamic_cast<SPShape *>(sp_lpe_item); if(shape){ + setCurrentShape(shape); sp_curve = shape->getCurve(); pathvector_before_effect = sp_curve->get_pathvector(); } diff --git a/src/live_effects/effect.h b/src/live_effects/effect.h index 1997ff0ca..f5e41d50e 100644 --- a/src/live_effects/effect.h +++ b/src/live_effects/effect.h @@ -19,7 +19,7 @@ class SPDocument; class SPDesktop; class SPItem; -class LivePathEffectObject; +class LivePathEffectObject; class SPLPEItem; class KnotHolder; class KnotHolderEntity; @@ -44,6 +44,12 @@ enum LPEPathFlashType { DEFAULT }; +enum LpeAction { + LPE_ERASE = 0, + LPE_TO_OBJECTS, + LPE_VISIBILITY +}; + class Effect { public: static Effect* New(EffectType lpenr, LivePathEffectObject *lpeobj); @@ -73,6 +79,9 @@ public: static int acceptsNumClicks(EffectType type); int acceptsNumClicks() const { return acceptsNumClicks(effectType()); } void doAcceptPathPreparations(SPLPEItem *lpeitem); + SPShape * getCurrentShape(){ return sp_shape; }; + void setCurrentShape(SPShape * shape); + void processObjects(LpeAction lpe_action); /* * isReady() indicates whether all preparations which are necessary to apply the LPE are done, @@ -125,7 +134,9 @@ public: bool apply_to_clippath_and_mask; bool erase_extra_objects; // set this to false allow retain extra generated objects, see measure line LPE bool upd_params; - + BoolParam is_visible; + SPCurve * sp_curve; + Geom::PathVector pathvector_before_effect; protected: Effect(LivePathEffectObject *lpeobject); @@ -150,7 +161,6 @@ protected: bool _provides_knotholder_entities; int oncanvasedit_it; - BoolParam is_visible; bool show_orig_path; // set this to true in derived effects to automatically have the original // path displayed as helperpath @@ -164,10 +174,10 @@ protected: bool concatenate_before_pwd2; SPLPEItem * sp_lpe_item; // these get stored in doBeforeEffect_impl, and derived classes may do as they please with them. + 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. + std::vector<const char *> items; double current_zoom; std::vector<Geom::Point> selectedNodesPoints; - SPCurve * sp_curve; - Geom::PathVector pathvector_before_effect; private: bool provides_own_flash_paths; // if true, the standard flash path is suppressed diff --git a/src/live_effects/lpe-copy_rotate.cpp b/src/live_effects/lpe-copy_rotate.cpp index 813f25d3d..fbc7933e7 100644 --- a/src/live_effects/lpe-copy_rotate.cpp +++ b/src/live_effects/lpe-copy_rotate.cpp @@ -17,6 +17,13 @@ #include <2geom/sbasis-to-bezier.h> #include <2geom/intersection-graph.h> #include "live_effects/lpe-copy_rotate.h" +#include "display/curve.h" +#include "svg/path-string.h" +#include "svg/svg.h" +#include "style.h" +#include "helper/geom.h" +#include "xml/sp-css-attr.h" +#include "path-chemistry.h" // TODO due to internal breakage in glibmm headers, this must be last: #include <glibmm/i18n.h> @@ -45,9 +52,11 @@ LPECopyRotate::LPECopyRotate(LivePathEffectObject *lpeobject) : starting_angle(_("Starting angle"), _("Angle of the first copy"), "starting_angle", &wr, this, 0.0), rotation_angle(_("Rotation angle"), _("Angle between two successive copies"), "rotation_angle", &wr, this, 60.0), num_copies(_("Number of copies"), _("Number of copies of the original path"), "num_copies", &wr, this, 6), + split_gap(_("Gap on split"), _("Gap on split"), "split_gap", &wr, this, -0.001), copies_to_360(_("360º Copies"), _("No rotation angle, fixed to 360º"), "copies_to_360", &wr, this, true), fuse_paths(_("Kaleidoskope"), _("Kaleidoskope by helper line, use fill-rule: evenodd for best result"), "fuse_paths", &wr, this, false), join_paths(_("Join paths"), _("Join paths, use fill-rule: evenodd for best result"), "join_paths", &wr, this, false), + split_items(_("Split elements"), _("Split elements, this allow gradients and other paints."), "split_items", &wr, this, false), dist_angle_handle(100.0) { show_orig_path = true; @@ -56,15 +65,21 @@ LPECopyRotate::LPECopyRotate(LivePathEffectObject *lpeobject) : registerParameter(&copies_to_360); registerParameter(&fuse_paths); registerParameter(&join_paths); + registerParameter(&split_items); registerParameter(&starting_angle); registerParameter(&starting_point); registerParameter(&rotation_angle); registerParameter(&num_copies); + registerParameter(&split_gap); registerParameter(&origin); - + split_gap.param_set_range(-999999.0, 999999.0); + split_gap.param_set_increments(0.1, 0.1); + split_gap.param_set_digits(5); + num_copies.param_set_range(0, 999999); num_copies.param_make_integer(true); - num_copies.param_set_range(0, 1000); apply_to_clippath_and_mask = true; + previous_num_copies = num_copies; + reset = false; } LPECopyRotate::~LPECopyRotate() @@ -72,6 +87,207 @@ LPECopyRotate::~LPECopyRotate() } +void +LPECopyRotate::doAfterEffect (SPLPEItem const* lpeitem) +{ + if (split_items) { + SPDocument * document = SP_ACTIVE_DOCUMENT; + items.clear(); + container = dynamic_cast<SPObject *>(sp_lpe_item->parent); + SPDocument * doc = SP_ACTIVE_DOCUMENT; + Inkscape::XML::Node *root = sp_lpe_item->document->getReprRoot(); + Inkscape::XML::Node *root_origin = doc->getReprRoot(); + if (root_origin != root) { + return; + } + if (previous_num_copies != num_copies) { + gint numcopies_gap = previous_num_copies - num_copies; + if (numcopies_gap > 0 && num_copies != 0) { + guint counter = num_copies - 1; + while (numcopies_gap > 0) { + const char * id = g_strdup(Glib::ustring("rotated-").append(std::to_string(counter)).append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str()); + if (!id || strlen(id) == 0) { + return; + } + SPObject *elemref = NULL; + if (elemref = document->getObjectById(id)) { + SP_ITEM(elemref)->setHidden(true); + } + counter++; + numcopies_gap--; + } + } + previous_num_copies = num_copies; + } + SPObject *elemref = NULL; + char * id = g_strdup(Glib::ustring("rotated-").append("1").append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str()); + guint counter = 0; + while(elemref = document->getObjectById(id)) { + if (SP_ITEM(elemref)->isHidden()) { + items.push_back(id); + } + id = g_strdup(Glib::ustring("rotated-").append(std::to_string(counter)).append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str()); + counter++; + } + g_free(id); + double diagonal = Geom::distance(Geom::Point(boundingbox_X.min(),boundingbox_Y.min()),Geom::Point(boundingbox_X.max(),boundingbox_Y.max())); + Geom::Rect bbox(Geom::Point(boundingbox_X.min(),boundingbox_Y.min()),Geom::Point(boundingbox_X.max(),boundingbox_Y.max())); + double size_divider = Geom::distance(origin,bbox) + (diagonal * 2); + Geom::Point line_start = origin + dir * Geom::Rotate(-(Geom::rad_from_deg(starting_angle))) * size_divider; + Geom::Point line_end = origin + dir * Geom::Rotate(-(Geom::rad_from_deg(rotation_angle + starting_angle))) * size_divider; + Geom::Affine m = Geom::Translate(-origin) * Geom::Rotate(-(Geom::rad_from_deg(starting_angle))); + if (fuse_paths) { + size_t rest = 0; + for (size_t i = 1; i < num_copies; ++i) { + Geom::Affine r = Geom::identity(); + Geom::Point dir = unit_vector((Geom::Point)origin - Geom::middle_point(line_start,line_end)); + if( rest%2 == 0) { + r *= Geom::Rotate(Geom::Angle(dir)).inverse(); + r *= Geom::Scale(1, -1); + r *= Geom::Rotate(Geom::Angle(dir)); + } + Geom::Rotate rot(-(Geom::rad_from_deg(rotation_angle * i))); + Geom::Affine t = m * r * rot * Geom::Rotate(Geom::rad_from_deg(starting_angle)) * Geom::Translate(origin); + if( rest%2 == 0) { + t = m * r * rot * Geom::Rotate(Geom::rad_from_deg(starting_angle)).inverse() * Geom::Translate(origin); + } + t *= sp_lpe_item->transform; + toItem(t, i-1, reset); + rest ++; + } + } else { + for (size_t i = 1; i < num_copies; ++i) { + Geom::Rotate rot(-(Geom::rad_from_deg(rotation_angle * i))); + Geom::Affine t = m * rot * Geom::Rotate(Geom::rad_from_deg(starting_angle)) * Geom::Translate(origin); + t *= sp_lpe_item->transform; + toItem(t, i - 1, reset); + } + } + reset = false; + } else { + processObjects(LPE_ERASE); + items.clear(); + } + + std::cout << previous_num_copies << "previous_num_copies\n"; + std::cout << num_copies << "num_copies\n"; +} + +void +LPECopyRotate::cloneD(SPObject *origin, SPObject *dest, bool root, bool reset) +{ + SPDocument * document = SP_ACTIVE_DOCUMENT; + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + if ( SP_IS_GROUP(origin) && SP_IS_GROUP(dest) && SP_GROUP(origin)->getItemCount() == SP_GROUP(dest)->getItemCount() ) { + std::vector< SPObject * > childs = origin->childList(true); + size_t index = 0; + for (std::vector<SPObject * >::iterator obj_it = childs.begin(); + obj_it != childs.end(); ++obj_it) { + SPObject *dest_child = dest->nthChild(index); + cloneD(*obj_it, dest_child, false, reset); + index++; + } + } + SPShape * shape = SP_SHAPE(origin); + SPPath * path = SP_PATH(dest); + if (!path && !SP_IS_GROUP(dest)) { + Inkscape::XML::Node *dest_node = sp_selected_item_to_curved_repr(SP_ITEM(dest), 0); + dest->updateRepr(xml_doc, dest_node, SP_OBJECT_WRITE_ALL); + path = SP_PATH(dest); + } + if (path && shape) { + SPCurve *c = NULL; + if (root) { + c = new SPCurve(); + c->set_pathvector(pathvector_before_effect); + } else { + c = shape->getCurve(); + } + if (c) { + path->setCurve(c, TRUE); + c->unref(); + } else { + dest->getRepr()->setAttribute("d", NULL); + } + if (reset) { + dest->getRepr()->setAttribute("style", shape->getRepr()->attribute("style")); + } + } +} + +void +LPECopyRotate::toItem(Geom::Affine transform, size_t i, bool reset) +{ + SPDocument * document = SP_ACTIVE_DOCUMENT; + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + const char * elemref_id = g_strdup(Glib::ustring("rotated-").append(std::to_string(i)).append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str()); + items.push_back(elemref_id); + SPObject *elemref= NULL; + Inkscape::XML::Node *phantom = NULL; + if (elemref = document->getObjectById(elemref_id)) { + phantom = elemref->getRepr(); + } else { + phantom = sp_lpe_item->getRepr()->duplicate(xml_doc); + std::vector<const char *> attrs; + attrs.push_back("inkscape:path-effect"); + attrs.push_back("inkscape:original-d"); + attrs.push_back("sodipodi:type"); + attrs.push_back("sodipodi:rx"); + attrs.push_back("sodipodi:ry"); + attrs.push_back("sodipodi:cx"); + attrs.push_back("sodipodi:cy"); + attrs.push_back("sodipodi:end"); + attrs.push_back("sodipodi:start"); + attrs.push_back("inkscape:flatsided"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("inkscape:rounded"); + attrs.push_back("sodipodi:arg1"); + attrs.push_back("sodipodi:arg2"); + attrs.push_back("sodipodi:r1"); + attrs.push_back("sodipodi:r2"); + attrs.push_back("sodipodi:sides"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("sodipodi:argument"); + attrs.push_back("sodipodi:expansion"); + attrs.push_back("sodipodi:radius"); + attrs.push_back("sodipodi:revolution"); + attrs.push_back("sodipodi:t0"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("x"); + attrs.push_back("y"); + attrs.push_back("rx"); + attrs.push_back("ry"); + attrs.push_back("width"); + attrs.push_back("height"); + phantom->setAttribute("id", elemref_id); + for(const char * attr : attrs) { + phantom->setAttribute(attr, NULL); + } + } + if (!elemref) { + elemref = container->appendChildRepr(phantom); + Inkscape::GC::release(phantom); + } + cloneD(SP_OBJECT(sp_lpe_item), elemref, true, reset); + elemref->getRepr()->setAttribute("transform" , sp_svg_transform_write(transform)); + SP_ITEM(elemref)->setHidden(false); + if (elemref->parent != container) { + Inkscape::XML::Node *copy = phantom->duplicate(xml_doc); + copy->setAttribute("id", elemref_id); + container->appendChildRepr(copy); + Inkscape::GC::release(copy); + elemref->deleteObject(); + } +} + +void +LPECopyRotate::resetStyles(){ + reset = true; + doAfterEffect(sp_lpe_item); +} + Gtk::Widget * LPECopyRotate::newWidget() { // use manage here, because after deletion of Effect object, others might @@ -81,7 +297,15 @@ Gtk::Widget * LPECopyRotate::newWidget() vbox->set_border_width(5); vbox->set_homogeneous(false); vbox->set_spacing(2); - + Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false,0)); + Gtk::VBox * vbox_expander = Gtk::manage( new Gtk::VBox(Effect::newWidget()) ); + vbox_expander->set_border_width(0); + vbox_expander->set_spacing(2); + Gtk::Button * reset_button = Gtk::manage(new Gtk::Button(Glib::ustring(_("Reset styles")))); + reset_button->signal_clicked().connect(sigc::mem_fun (*this,&LPECopyRotate::resetStyles)); + reset_button->set_size_request(140,30); + vbox->pack_start(*hbox, true,true,2); + hbox->pack_start(*reset_button, false, false,2); std::vector<Parameter *>::iterator it = param_vector.begin(); while (it != param_vector.end()) { if ((*it)->widget_is_visible) { @@ -125,11 +349,11 @@ void LPECopyRotate::transform_multiply(Geom::Affine const& postmul, bool set) { // cycle through all parameters. Most parameters will not need transformation, but path and point params do. - for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); ++it) { Parameter * param = *it; param->param_transform_multiply(postmul, set); } + sp_lpe_item_update_patheffect(sp_lpe_item, false, false); } void @@ -140,17 +364,17 @@ LPECopyRotate::doBeforeEffect (SPLPEItem const* lpeitem) if (copies_to_360) { rotation_angle.param_set_value(360.0/(double)num_copies); } - if (fuse_paths && rotation_angle * num_copies > 360 && rotation_angle > 0) { + if (fuse_paths && rotation_angle * num_copies > 360.1 && rotation_angle > 0) { num_copies.param_set_value(floor(360/rotation_angle)); } if (fuse_paths && copies_to_360) { - num_copies.param_set_increments(2,2); + num_copies.param_set_increments(2.0,10.0); if ((int)num_copies%2 !=0) { num_copies.param_set_value(num_copies+1); rotation_angle.param_set_value(360.0/(double)num_copies); } } else { - num_copies.param_set_increments(1,1); + num_copies.param_set_increments(1.0, 10.0); } if (dist_angle_handle < 1.0) { @@ -352,12 +576,57 @@ LPECopyRotate::setFusion(Geom::PathVector &path_on, Geom::Path divider, double s tmp_path.clear(); } +Geom::PathVector +LPECopyRotate::doEffect_path (Geom::PathVector const & path_in) +{ + Geom::PathVector path_out; + if (split_items && (fuse_paths || join_paths)) { + if (num_copies == 0) { + return path_out; + } + path_out = pathv_to_linear_and_cubic_beziers(path_in); + double diagonal = Geom::distance(Geom::Point(boundingbox_X.min(),boundingbox_Y.min()),Geom::Point(boundingbox_X.max(),boundingbox_Y.max())); + Geom::OptRect bbox = sp_lpe_item->geometricBounds(); + double size_divider = Geom::distance(origin,bbox) + (diagonal * 2); + Geom::Point line_start = origin + dir * Geom::Rotate(-(Geom::rad_from_deg(starting_angle))) * size_divider; + Geom::Point line_end = origin + dir * Geom::Rotate(-(Geom::rad_from_deg(rotation_angle + starting_angle))) * size_divider; + Geom::Path divider = Geom::Path(line_start); + divider.appendNew<Geom::LineSegment>((Geom::Point)origin); + divider.appendNew<Geom::LineSegment>(line_end); + divider.close(); + Geom::PathVector triangle; + triangle.push_back(divider); + Geom::PathIntersectionGraph *pig = new Geom::PathIntersectionGraph(triangle, path_out); + if (pig && ! path_out.empty() && !triangle.empty()) { + //TODO: Here can produce a crash because some knows problems in new boolops code + path_out = pig->getIntersection(); + } + Geom::Affine r = Geom::identity(); + Geom::Point dir = unit_vector(Geom::middle_point(line_start,line_end) - (Geom::Point)origin); + Geom::Point gap = dir * split_gap; + r *= Geom::Translate(gap); + path_out *= r; + } else { + // default behavior + for (unsigned int i=0; i < path_in.size(); i++) { + Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in = path_in[i].toPwSb(); + Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in); + Geom::PathVector path = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE); + // add the output path vector to the already accumulated vector: + for (unsigned int j=0; j < path.size(); j++) { + path_out.push_back(path[j]); + } + } + } + return pathv_to_linear_and_cubic_beziers(path_out); +} + Geom::Piecewise<Geom::D2<Geom::SBasis> > LPECopyRotate::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in) { using namespace Geom; - if (num_copies == 1 && !fuse_paths) { + if ((num_copies == 1 && !fuse_paths) || split_items) { return pwd2_in; } @@ -373,10 +642,10 @@ LPECopyRotate::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & p divider.appendNew<Geom::LineSegment>(line_end); Piecewise<D2<SBasis> > output; Affine pre = Translate(-origin) * Rotate(-rad_from_deg(starting_angle)); + PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(path_from_piecewise(remove_short_cuts(pwd2_in, 0.1), 0.001)); if (fuse_paths) { Geom::PathVector path_out; Geom::PathVector tmp_path; - PathVector const original_pathv = path_from_piecewise(remove_short_cuts(pwd2_in, 0.1), 0.001); for (Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) { if (path_it->empty()) { continue; @@ -403,7 +672,7 @@ LPECopyRotate::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & p output = paths_to_pw(path_out); } } else { - Geom::PathVector output_pv = path_from_piecewise(output , 0.01); + Geom::PathVector output_pv; for (int i = 0; i < num_copies; ++i) { Rotate rot(-rad_from_deg(rotation_angle * i)); Affine t = pre * rot * Translate(origin); @@ -449,6 +718,23 @@ LPECopyRotate::resetDefaults(SPItem const* item) original_bbox(SP_LPE_ITEM(item)); } +void +LPECopyRotate::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/) +{ + processObjects(LPE_VISIBILITY); +} + +void +LPECopyRotate::doOnRemove (SPLPEItem const* /*lpeitem*/) +{ + //unset "erase_extra_objects" hook on sp-lpe-item.cpp + if (!erase_extra_objects) { + processObjects(LPE_TO_OBJECTS); + return; + } + processObjects(LPE_ERASE); +} + } //namespace LivePathEffect } /* namespace Inkscape */ diff --git a/src/live_effects/lpe-copy_rotate.h b/src/live_effects/lpe-copy_rotate.h index c2ae2daf1..dbec2e1c3 100644 --- a/src/live_effects/lpe-copy_rotate.h +++ b/src/live_effects/lpe-copy_rotate.h @@ -15,6 +15,8 @@ */ #include "live_effects/effect.h" +#include "live_effects/parameter/parameter.h" +#include "live_effects/parameter/text.h" #include "live_effects/parameter/point.h" #include "live_effects/lpegroupbbox.h" @@ -26,13 +28,20 @@ public: LPECopyRotate(LivePathEffectObject *lpeobject); virtual ~LPECopyRotate(); virtual void doOnApply (SPLPEItem const* lpeitem); + virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in); virtual Geom::Piecewise<Geom::D2<Geom::SBasis> > doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in); virtual void doBeforeEffect (SPLPEItem const* lpeitem); + virtual void doAfterEffect (SPLPEItem const* lpeitem); virtual void setFusion(Geom::PathVector &path_in, Geom::Path divider, double sizeDivider); virtual void split(Geom::PathVector &path_in, Geom::Path const ÷r); virtual void resetDefaults(SPItem const* item); virtual void transform_multiply(Geom::Affine const& postmul, bool set); + virtual void doOnRemove (SPLPEItem const* /*lpeitem*/); + virtual void doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/); virtual Gtk::Widget * newWidget(); + void toItem(Geom::Affine transform, size_t i, bool reset); + void cloneD(SPObject *origin, SPObject *dest, bool root, bool reset); + void resetStyles(); protected: virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec); @@ -42,9 +51,11 @@ private: ScalarParam starting_angle; ScalarParam rotation_angle; ScalarParam num_copies; + ScalarParam split_gap; BoolParam copies_to_360; BoolParam fuse_paths; BoolParam join_paths; + BoolParam split_items; Geom::Point A; Geom::Point B; Geom::Point dir; @@ -52,6 +63,9 @@ private: Geom::Point rot_pos; Geom::Point previous_start_point; double dist_angle_handle; + double previous_num_copies; + bool reset; + SPObject * container; LPECopyRotate(const LPECopyRotate&); LPECopyRotate& operator=(const LPECopyRotate&); }; diff --git a/src/live_effects/lpe-measure-line.cpp b/src/live_effects/lpe-measure-line.cpp index af2a8e919..ef87be81c 100644 --- a/src/live_effects/lpe-measure-line.cpp +++ b/src/live_effects/lpe-measure-line.cpp @@ -21,7 +21,9 @@ #include "svg/svg-color.h" #include "svg/svg.h" #include "display/curve.h" +#include "helper/geom.h" #include "2geom/affine.h" +#include "path-chemistry.h" #include "style.h" #include "sp-root.h" #include "sp-defs.h" @@ -165,12 +167,6 @@ LPEMeasureLine::LPEMeasureLine(LivePathEffectObject *lpeobject) : LPEMeasureLine::~LPEMeasureLine() {} -void swap(Geom::Point &A, Geom::Point &B){ - Geom::Point tmp = A; - A = B; - B = tmp; -} - void LPEMeasureLine::createArrowMarker(const char * mode) { @@ -229,7 +225,7 @@ LPEMeasureLine::createArrowMarker(const char * mode) elemref = SP_OBJECT(document->getDefs()->appendChildRepr(arrow)); Inkscape::GC::release(arrow); } - elements.push_back(mode); + items.push_back(mode); } void @@ -369,7 +365,7 @@ LPEMeasureLine::createTextLabel(Geom::Point pos, double length, Geom::Coord angl copy->setAttribute("id", id); elemref = elemref_copy; } - elements.push_back(id); + items.push_back(id); Geom::OptRect bounds = SP_ITEM(elemref)->bounds(SPItem::GEOMETRIC_BBOX); if (bounds) { anotation_width = bounds->width() * 1.4; @@ -483,7 +479,7 @@ LPEMeasureLine::createLine(Geom::Point start,Geom::Point end, const char * id, b elemref->deleteObject(); copy->setAttribute("id", id); } - elements.push_back(id); + items.push_back(id); } void @@ -535,7 +531,7 @@ LPEMeasureLine::doBeforeEffect (SPLPEItem const* lpeitem) sp_lpe_item->getCurrentLPE() != this){ return; } - elements.clear(); + items.clear(); start_stored = start; end_stored = end; Geom::Point hstart = start; @@ -680,60 +676,12 @@ LPEMeasureLine::doOnRemove (SPLPEItem const* /*lpeitem*/) //unset "erase_extra_objects" hook on sp-lpe-item.cpp if (!erase_extra_objects) { processObjects(LPE_TO_OBJECTS); - elements.clear(); + items.clear(); return; } processObjects(LPE_ERASE); } -void -LPEMeasureLine::processObjects(LpeAction lpe_action) -{ - SPDocument * document = SP_ACTIVE_DOCUMENT; - for (std::vector<const char *>::iterator el_it = elements.begin(); - el_it != elements.end(); ++el_it) { - const char * id = *el_it; - if (!id || strlen(id) == 0) { - return; - } - SPObject *elemref = NULL; - if (elemref = document->getObjectById(id)) { - SPCSSAttr *css; - Glib::ustring css_str; - switch (lpe_action){ - case LPE_TO_OBJECTS: - elemref->getRepr()->setAttribute("inkscape:path-effect", NULL); - elemref->getRepr()->setAttribute("sodipodi:insensitive", NULL); - break; - - case LPE_ERASE: - if (std::strcmp(elemref->getId(),id_origin.param_getSVGValue()) != 0) { - elemref->deleteObject(); - } - break; - - case LPE_VISIBILITY: - css = sp_repr_css_attr_new(); - sp_repr_css_attr_add_from_string(css, elemref->getRepr()->attribute("style")); - if (!this->isVisible() && std::strcmp(elemref->getId(),id_origin.param_getSVGValue()) != 0) { - css->setAttribute("display", "none"); - } else { - css->setAttribute("display", NULL); - } - sp_repr_css_write_string(css,css_str); - elemref->getRepr()->setAttribute("style", css_str.c_str()); - break; - - default: - break; - } - } - } - if (lpe_action == LPE_ERASE) { - elements.clear(); - } -} - Gtk::Widget *LPEMeasureLine::newWidget() { // use manage here, because after deletion of Effect object, others might diff --git a/src/live_effects/lpe-measure-line.h b/src/live_effects/lpe-measure-line.h index c69921a4d..724c0d924 100644 --- a/src/live_effects/lpe-measure-line.h +++ b/src/live_effects/lpe-measure-line.h @@ -35,12 +35,6 @@ enum OrientationMethod { OM_END }; -enum LpeAction { - LPE_ERASE = 0, - LPE_TO_OBJECTS, - LPE_VISIBILITY -}; - class LPEMeasureLine : public Effect { public: LPEMeasureLine(LivePathEffectObject *lpeobject); @@ -51,7 +45,6 @@ public: virtual void doEffect (SPCurve * curve){}; //stop the chain virtual void doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/); virtual Geom::PathVector doEffect_path(Geom::PathVector const &path_in); - void processObjects(LpeAction lpe_action); void createLine(Geom::Point start,Geom::Point end, const char * id, bool main, bool overflow, bool remove, bool arrows = false); void createTextLabel(Geom::Point pos, double length, Geom::Coord angle, bool remove, bool valid); void onExpanderChanged(); @@ -92,7 +85,6 @@ private: double arrow_gap; Geom::Point start_stored; Geom::Point end_stored; - std::vector<const char *> elements; /* Geom::Affine affine_over;*/ LPEMeasureLine(const LPEMeasureLine &); LPEMeasureLine &operator=(const LPEMeasureLine &); diff --git a/src/live_effects/lpe-mirror_symmetry.cpp b/src/live_effects/lpe-mirror_symmetry.cpp index 61b2b8b5c..7f0a93c52 100644 --- a/src/live_effects/lpe-mirror_symmetry.cpp +++ b/src/live_effects/lpe-mirror_symmetry.cpp @@ -15,11 +15,22 @@ */ #include <gtkmm.h> +#include "live_effects/lpeobject.h" +#include "live_effects/lpeobject-reference.h" #include "live_effects/lpe-mirror_symmetry.h" -#include <display/curve.h> -#include <svg/path-string.h> +#include "display/curve.h" +#include "svg/path-string.h" +#include "svg/svg.h" +#include "sp-defs.h" #include "helper/geom.h" -#include <2geom/path-intersection.h> +#include "2geom/intersection-graph.h" +#include "2geom/path-intersection.h" +#include "2geom/affine.h" +#include "helper/geom.h" +#include "sp-lpe-item.h" +#include "path-chemistry.h" +#include "style.h" +#include "xml/sp-css-attr.h" // TODO due to internal breakage in glibmm headers, this must be last: #include <glibmm/i18n.h> @@ -40,73 +51,69 @@ MTConverter(ModeTypeData, MT_END); LPEMirrorSymmetry::LPEMirrorSymmetry(LivePathEffectObject *lpeobject) : Effect(lpeobject), mode(_("Mode"), _("Symmetry move mode"), "mode", MTConverter, &wr, this, MT_FREE), + split_gap(_("Gap on split"), _("Gap on split"), "split_gap", &wr, this, -0.001), discard_orig_path(_("Discard original path"), _("Check this to only keep the mirrored part of the path"), "discard_orig_path", &wr, this, false), fuse_paths(_("Fuse paths"), _("Fuse original and the reflection into a single path"), "fuse_paths", &wr, this, false), oposite_fuse(_("Opposite fuse"), _("Picks the other side of the mirror as the original"), "oposite_fuse", &wr, this, false), + split_items(_("Split elements"), _("Split elements, this allow gradients and other paints."), "split_items", &wr, this, false), start_point(_("Start mirror line"), _("Start mirror line"), "start_point", &wr, this, _("Adjust start of mirroring")), end_point(_("End mirror line"), _("End mirror line"), "end_point", &wr, this, _("Adjust end of mirroring")), - center_point(_("Center mirror line"), _("Center mirror line"), "center_point", &wr, this, _("Adjust center of mirroring")) + center_point(_("Center mirror line"), _("Center mirror line"), "center_point", &wr, this, _("Adjust center of mirroring")), + id_origin("id origin", "store the id of the first LPEItem", "id_origin", &wr, this,"") { show_orig_path = true; registerParameter(&mode); - registerParameter( &discard_orig_path); - registerParameter( &fuse_paths); - registerParameter( &oposite_fuse); - registerParameter( &start_point); - registerParameter( &end_point); - registerParameter( ¢er_point); - previous_center = Geom::Point(0,0); + registerParameter(&split_gap); + registerParameter(&discard_orig_path); + registerParameter(&fuse_paths); + registerParameter(&oposite_fuse); + registerParameter(&split_items); + registerParameter(&start_point); + registerParameter(&end_point); + registerParameter(¢er_point); + registerParameter(&id_origin); + id_origin.param_hide_canvas_text(); + split_gap.param_set_range(-999999.0, 999999.0); + split_gap.param_set_increments(0.1, 0.1); + split_gap.param_set_digits(5); apply_to_clippath_and_mask = true; + previous_center = Geom::Point(0,0); } LPEMirrorSymmetry::~LPEMirrorSymmetry() { } -Gtk::Widget * LPEMirrorSymmetry::newWidget() +void +LPEMirrorSymmetry::doAfterEffect (SPLPEItem const* lpeitem) { - // use manage here, because after deletion of Effect object, others might - // still be pointing to this widget. - Gtk::VBox *vbox = Gtk::manage(new Gtk::VBox(Effect::newWidget())); - - vbox->set_border_width(5); - vbox->set_homogeneous(false); - vbox->set_spacing(2); - - std::vector<Parameter *>::iterator it = param_vector.begin(); - while (it != param_vector.end()) { - if ((*it)->widget_is_visible) { - Parameter *param = *it; - Gtk::Widget *widg = dynamic_cast<Gtk::Widget *>(param->param_newWidget()); - Glib::ustring *tip = param->param_getTooltip(); - if (widg) { - if (param->param_key != "center_point") { - vbox->pack_start(*widg, true, true, 2); - if (tip) { - widg->set_tooltip_text(*tip); - } else { - widg->set_tooltip_text(""); - widg->set_has_tooltip(false); - } - } - } + if (split_items && !discard_orig_path) { + container = dynamic_cast<SPObject *>(sp_lpe_item->parent); + SPDocument * doc = SP_ACTIVE_DOCUMENT; + Inkscape::XML::Node *root = sp_lpe_item->document->getReprRoot(); + Inkscape::XML::Node *root_origin = doc->getReprRoot(); + if (root_origin != root) { + return; } - - ++it; + Geom::Line ls((Geom::Point)start_point, (Geom::Point)end_point); + Geom::Affine m = Geom::reflection (ls.vector(), (Geom::Point)start_point); + m = m * sp_lpe_item->transform; + toMirror(m); + } else { + processObjects(LPE_ERASE); + items.clear(); } - return dynamic_cast<Gtk::Widget *>(vbox); } - void LPEMirrorSymmetry::doBeforeEffect (SPLPEItem const* lpeitem) { + using namespace Geom; original_bbox(lpeitem); //center_point->param_set_liveupdate(false); Point point_a(boundingbox_X.max(), boundingbox_Y.min()); Point point_b(boundingbox_X.max(), boundingbox_Y.max()); - Point point_c(boundingbox_X.max(), boundingbox_Y.middle()); if (mode == MT_Y) { point_a = Geom::Point(boundingbox_X.min(),center_point[Y]); point_b = Geom::Point(boundingbox_X.max(),center_point[Y]); @@ -173,6 +180,172 @@ LPEMirrorSymmetry::doBeforeEffect (SPLPEItem const* lpeitem) } void +LPEMirrorSymmetry::cloneD(SPObject *origin, SPObject *dest, bool live, bool root) +{ + SPDocument * document = SP_ACTIVE_DOCUMENT; + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + if ( SP_IS_GROUP(origin) && SP_IS_GROUP(dest) && SP_GROUP(origin)->getItemCount() == SP_GROUP(dest)->getItemCount() ) { + std::vector< SPObject * > childs = origin->childList(true); + size_t index = 0; + for (std::vector<SPObject * >::iterator obj_it = childs.begin(); + obj_it != childs.end(); ++obj_it) { + SPObject *dest_child = dest->nthChild(index); + cloneD(*obj_it, dest_child, live, false); + index++; + } + } + SPShape * shape = SP_SHAPE(origin); + SPPath * path = SP_PATH(dest); + if (!path && !SP_IS_GROUP(dest)) { + Inkscape::XML::Node *dest_node = sp_selected_item_to_curved_repr(SP_ITEM(dest), 0); + dest->updateRepr(xml_doc, dest_node, SP_OBJECT_WRITE_ALL); + path = SP_PATH(dest); + } + if (path && shape) { + if ( live) { + SPCurve *c = NULL; + if (root) { + c = new SPCurve(); + c->set_pathvector(pathvector_before_effect); + } else { + c = shape->getCurve(); + } + if (c) { + path->setCurve(c, TRUE); + c->unref(); + } else { + dest->getRepr()->setAttribute("d", NULL); + } + } else { + dest->getRepr()->setAttribute("d", origin->getRepr()->attribute("d")); + } + } +} + +void +LPEMirrorSymmetry::toMirror(Geom::Affine transform) +{ + SPDocument * document = SP_ACTIVE_DOCUMENT; + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + const char * id_origin_char = id_origin.param_getSVGValue(); + const char * elemref_id = g_strdup(Glib::ustring("mirror-").append(id_origin_char).c_str()); + items.clear(); + items.push_back(elemref_id); + SPObject *elemref= NULL; + Inkscape::XML::Node *phantom = NULL; + if (elemref = document->getObjectById(elemref_id)) { + phantom = elemref->getRepr(); + } else { + phantom = sp_lpe_item->getRepr()->duplicate(xml_doc); + std::vector<const char *> attrs; + attrs.push_back("inkscape:path-effect"); + attrs.push_back("inkscape:original-d"); + attrs.push_back("sodipodi:type"); + attrs.push_back("sodipodi:rx"); + attrs.push_back("sodipodi:ry"); + attrs.push_back("sodipodi:cx"); + attrs.push_back("sodipodi:cy"); + attrs.push_back("sodipodi:end"); + attrs.push_back("sodipodi:start"); + attrs.push_back("inkscape:flatsided"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("inkscape:rounded"); + attrs.push_back("sodipodi:arg1"); + attrs.push_back("sodipodi:arg2"); + attrs.push_back("sodipodi:r1"); + attrs.push_back("sodipodi:r2"); + attrs.push_back("sodipodi:sides"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("sodipodi:argument"); + attrs.push_back("sodipodi:expansion"); + attrs.push_back("sodipodi:radius"); + attrs.push_back("sodipodi:revolution"); + attrs.push_back("sodipodi:t0"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("x"); + attrs.push_back("y"); + attrs.push_back("rx"); + attrs.push_back("ry"); + attrs.push_back("width"); + attrs.push_back("height"); + for(const char * attr : attrs) { + phantom->setAttribute(attr, NULL); + } + } + phantom->setAttribute("id", elemref_id); + if (!elemref) { + elemref = container->appendChildRepr(phantom); + Inkscape::GC::release(phantom); + } + cloneD(SP_OBJECT(sp_lpe_item), elemref, true, true); + elemref->getRepr()->setAttribute("transform" , sp_svg_transform_write(transform)); + if (elemref->parent != container) { + Inkscape::XML::Node *copy = phantom->duplicate(xml_doc); + copy->setAttribute("id", elemref_id); + container->appendChildRepr(copy); + Inkscape::GC::release(copy); + elemref->deleteObject(); + } +} + +Gtk::Widget * +LPEMirrorSymmetry::newWidget() +{ + // use manage here, because after deletion of Effect object, others might + // still be pointing to this widget. + Gtk::VBox *vbox = Gtk::manage(new Gtk::VBox(Effect::newWidget())); + vbox->set_border_width(5); + vbox->set_homogeneous(false); + vbox->set_spacing(2); + + std::vector<Parameter *>::iterator it = param_vector.begin(); + while (it != param_vector.end()) { + if ((*it)->widget_is_visible) { + Parameter * param = *it; + if (param->param_key == "id_origin" || param->param_key == "center_point") { + ++it; + continue; + } + Gtk::Widget * widg = param->param_newWidget(); + Glib::ustring * tip = param->param_getTooltip(); + if (widg) { + vbox->pack_start(*widg, true, true, 2); + if (tip) { + widg->set_tooltip_text(*tip); + } else { + widg->set_tooltip_text(""); + widg->set_has_tooltip(false); + } + } + } + + ++it; + } + this->upd_params = false; + return dynamic_cast<Gtk::Widget *>(vbox); +} + +//TODO: Migrate the tree next function to effect.cpp/h to avoid duplication +void +LPEMirrorSymmetry::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/) +{ + processObjects(LPE_VISIBILITY); +} + +void +LPEMirrorSymmetry::doOnRemove (SPLPEItem const* /*lpeitem*/) +{ + //unset "erase_extra_objects" hook on sp-lpe-item.cpp + if (!erase_extra_objects) { + processObjects(LPE_TO_OBJECTS); + return; + } + processObjects(LPE_ERASE); +} + +void LPEMirrorSymmetry::transform_multiply(Geom::Affine const& postmul, bool set) { // cycle through all parameters. Most parameters will not need transformation, but path and point params do. @@ -193,18 +366,26 @@ LPEMirrorSymmetry::doOnApply (SPLPEItem const* lpeitem) Point point_a(boundingbox_X.max(), boundingbox_Y.min()); Point point_b(boundingbox_X.max(), boundingbox_Y.max()); Point point_c(boundingbox_X.max(), boundingbox_Y.middle()); - start_point.param_setValue(point_a, true); + start_point.param_setValue(point_a); start_point.param_update_default(point_a); - end_point.param_setValue(point_b, true); + end_point.param_setValue(point_b); end_point.param_update_default(point_b); center_point.param_setValue(point_c, true); previous_center = center_point; + SPLPEItem * splpeitem = const_cast<SPLPEItem *>(lpeitem); + if (!lpeitem->hasPathEffectOfType(this->effectType(), false) ){ //first applied not ready yet + id_origin.param_setValue(lpeitem->getRepr()->attribute("id")); + id_origin.write_to_SVG(); + } } Geom::PathVector LPEMirrorSymmetry::doEffect_path (Geom::PathVector const & path_in) { + if (split_items && !fuse_paths) { + return path_in; + } Geom::PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(path_in); Geom::PathVector path_out; @@ -214,15 +395,51 @@ LPEMirrorSymmetry::doEffect_path (Geom::PathVector const & path_in) Geom::Line line_separation((Geom::Point)start_point, (Geom::Point)end_point); Geom::Affine m = Geom::reflection (line_separation.vector(), (Geom::Point)start_point); - - if (fuse_paths && !discard_orig_path) { + if (split_items && fuse_paths) { + Geom::OptRect bbox = sp_lpe_item->geometricBounds(); + Geom::Path p(Geom::Point(bbox->left(), bbox->top())); + p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->top())); + p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->bottom())); + p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->bottom())); + p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->top())); + p.close(); + p *= Geom::Translate(bbox->midpoint()).inverse(); + p *= Geom::Scale(1.2); + p *= Geom::Rotate(line_separation.angle()); + p *= Geom::Translate(bbox->midpoint()); + bbox = p.boundsFast(); + p.clear(); + p.start(Geom::Point(bbox->left(), bbox->top())); + p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->top())); + p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->bottom())); + p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->bottom())); + p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->top())); + p.close(); + p *= Geom::Translate(bbox->midpoint()).inverse(); + p *= Geom::Rotate(line_separation.angle()); + p *= Geom::Translate(bbox->midpoint()); + Geom::Point base(p.pointAt(3)); + if (oposite_fuse) { + base = p.pointAt(0); + } + p *= Geom::Translate(line_separation.pointAt(line_separation.nearestTime(base)) - base); + Geom::PathVector pv_bbox; + pv_bbox.push_back(p); + Geom::PathIntersectionGraph *pig = new Geom::PathIntersectionGraph(pv_bbox, original_pathv); + if (pig && !original_pathv.empty() && !pv_bbox.empty()) { + path_out = pig->getBminusA(); + } + Geom::Point dir = rot90(unit_vector((Geom::Point)end_point - (Geom::Point)start_point)); + Geom::Point gap = dir * split_gap; + path_out *= Geom::Translate(gap); + } else if (fuse_paths && !discard_orig_path) { for (Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) { if (path_it->empty()) { continue; } - Geom::PathVector tmp_path; + Geom::PathVector tmp_pathvector; double time_start = 0.0; int position = 0; bool end_open = false; @@ -268,11 +485,11 @@ LPEMirrorSymmetry::doEffect_path (Geom::PathVector const & path_in) Geom::Path mirror = portion.reversed() * m; mirror.setInitial(portion.finalPoint()); portion.append(mirror); - if(i!=0) { + if(i != 0) { portion.setFinal(portion.initialPoint()); portion.close(); } - tmp_path.push_back(portion); + tmp_pathvector.push_back(portion); } portion.clear(); } @@ -293,36 +510,33 @@ LPEMirrorSymmetry::doEffect_path (Geom::PathVector const & path_in) portion.append(mirror); portion = portion.reversed(); if (!original.closed()) { - tmp_path.push_back(portion); + tmp_pathvector.push_back(portion); } else { - if (cs.size() > 1 && tmp_path.size() > 0 && tmp_path[0].size() > 0 ) { - portion.setFinal(tmp_path[0].initialPoint()); - portion.setInitial(tmp_path[0].finalPoint()); - tmp_path[0].append(portion); + if (cs.size() > 1 && tmp_pathvector.size() > 0 && tmp_pathvector[0].size() > 0 ) { + portion.setFinal(tmp_pathvector[0].initialPoint()); + portion.setInitial(tmp_pathvector[0].finalPoint()); + tmp_pathvector[0].append(portion); } else { - tmp_path.push_back(portion); + tmp_pathvector.push_back(portion); } - tmp_path[0].close(); + tmp_pathvector[0].close(); } portion.clear(); } } } if (cs.size() == 0 && position == 1) { - tmp_path.push_back(original); - tmp_path.push_back(original * m); + tmp_pathvector.push_back(original); + tmp_pathvector.push_back(original * m); } - path_out.insert(path_out.end(), tmp_path.begin(), tmp_path.end()); - tmp_path.clear(); + path_out.insert(path_out.end(), tmp_pathvector.begin(), tmp_pathvector.end()); + tmp_pathvector.clear(); } - } - - if (!fuse_paths || discard_orig_path) { + } else if (!fuse_paths || discard_orig_path) { for (size_t i = 0; i < original_pathv.size(); ++i) { path_out.push_back(original_pathv[i] * m); } } - return path_out; } diff --git a/src/live_effects/lpe-mirror_symmetry.h b/src/live_effects/lpe-mirror_symmetry.h index b4967173a..592a11894 100644 --- a/src/live_effects/lpe-mirror_symmetry.h +++ b/src/live_effects/lpe-mirror_symmetry.h @@ -18,8 +18,8 @@ #include "live_effects/effect.h" #include "live_effects/parameter/parameter.h" +#include "live_effects/parameter/text.h" #include "live_effects/parameter/point.h" -#include "live_effects/parameter/path.h" #include "live_effects/parameter/enum.h" #include "live_effects/lpegroupbbox.h" @@ -41,23 +41,32 @@ public: virtual ~LPEMirrorSymmetry(); virtual void doOnApply (SPLPEItem const* lpeitem); virtual void doBeforeEffect (SPLPEItem const* lpeitem); + virtual void doAfterEffect (SPLPEItem const* lpeitem); virtual void transform_multiply(Geom::Affine const& postmul, bool set); virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in); + virtual void doOnRemove (SPLPEItem const* /*lpeitem*/); + virtual void doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/); virtual Gtk::Widget * newWidget(); + void toMirror(Geom::Affine transform); + // void cloneAttrbutes(Inkscape::XML::Node * origin, Inkscape::XML::Node * dest, const char * first_attribute, ...); + void cloneD(SPObject *origin, SPObject *dest, bool live, bool root); protected: virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec); private: EnumParam<ModeType> mode; + ScalarParam split_gap; BoolParam discard_orig_path; BoolParam fuse_paths; BoolParam oposite_fuse; + BoolParam split_items; PointParam start_point; PointParam end_point; PointParam center_point; + TextParam id_origin; Geom::Point previous_center; - + SPObject * container; LPEMirrorSymmetry(const LPEMirrorSymmetry&); LPEMirrorSymmetry& operator=(const LPEMirrorSymmetry&); }; diff --git a/src/live_effects/parameter/bool.cpp b/src/live_effects/parameter/bool.cpp index af99ef362..813c06b4e 100644 --- a/src/live_effects/parameter/bool.cpp +++ b/src/live_effects/parameter/bool.cpp @@ -72,7 +72,7 @@ BoolParam::param_newWidget() checkwdg->setActive(value); checkwdg->setProgrammatically = false; checkwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change bool parameter")); - + param_effect->upd_params = false; return dynamic_cast<Gtk::Widget *> (checkwdg); } else { return NULL; @@ -82,6 +82,7 @@ BoolParam::param_newWidget() void BoolParam::param_setValue(bool newvalue) { + param_effect->upd_params = true; value = newvalue; } diff --git a/src/live_effects/parameter/text.cpp b/src/live_effects/parameter/text.cpp index 8cab68ad0..5c4cdf4c6 100644 --- a/src/live_effects/parameter/text.cpp +++ b/src/live_effects/parameter/text.cpp @@ -125,13 +125,14 @@ TextParam::param_newWidget() rsu->setProgrammatically = false; rsu->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change text parameter")); - + param_effect->upd_params = false; return dynamic_cast<Gtk::Widget *> (rsu); } void TextParam::param_setValue(const Glib::ustring newvalue) { + param_effect->upd_params = true; value = newvalue; if (!_hide_canvas_text) { sp_canvastext_set_text (canvas_text, newvalue.c_str()); diff --git a/src/sp-item-group.cpp b/src/sp-item-group.cpp index 7b2507b5e..f2c0d2f2c 100644 --- a/src/sp-item-group.cpp +++ b/src/sp-item-group.cpp @@ -925,6 +925,15 @@ void SPGroup::update_patheffect(bool write) { } sp_group_perform_patheffect(this, this, write); + + for (PathEffectList::iterator it = this->path_effect_list->begin(); it != this->path_effect_list->end(); ++it) + { + LivePathEffectObject *lpeobj = (*it)->lpeobject; + + if (lpeobj && lpeobj->get_lpe()) { + lpeobj->get_lpe()->doAfterEffect(this); + } + } } } @@ -950,25 +959,35 @@ sp_group_perform_patheffect(SPGroup *group, SPGroup *topgroup, bool write) } else { c = subShape->getCurve(); } - + bool success = false; // only run LPEs when the shape has a curve defined if (c) { c->transform(i2anc_affine(subitem, topgroup)); - topgroup->performPathEffect(c); + success = topgroup->performPathEffect(c, subShape); c->transform(i2anc_affine(subitem, topgroup).inverse()); - subShape->setCurve(c, TRUE); - - if (write) { - Inkscape::XML::Node *repr = subitem->getRepr(); - gchar *str = sp_svg_write_path(c->get_pathvector()); - repr->setAttribute("d", str); + Inkscape::XML::Node *repr = subitem->getRepr(); + if (c && success) { + subShape->setCurve(c, TRUE); + if (write) { + gchar *str = sp_svg_write_path(c->get_pathvector()); + repr->setAttribute("d", str); #ifdef GROUP_VERBOSE - g_message("sp_group_perform_patheffect writes 'd' attribute"); + g_message("sp_group_perform_patheffect writes 'd' attribute"); #endif - g_free(str); + g_free(str); + } + c->unref(); + } else { + // LPE was unsuccesfull or doeffect stack return null. Read the old 'd'-attribute. + if (gchar const * value = repr->attribute("d")) { + Geom::PathVector pv = sp_svg_read_pathv(value); + SPCurve *oldcurve = new (std::nothrow) SPCurve(pv); + if (oldcurve) { + subShape->setCurve(oldcurve, TRUE); + oldcurve->unref(); + } + } } - - c->unref(); } } } diff --git a/src/sp-lpe-item.cpp b/src/sp-lpe-item.cpp index e2f61bfb5..00671b936 100644 --- a/src/sp-lpe-item.cpp +++ b/src/sp-lpe-item.cpp @@ -24,6 +24,8 @@ #include "live_effects/lpeobject.h" #include "live_effects/lpeobject-reference.h" #include "live_effects/lpe-measure-line.h" +#include "live_effects/lpe-mirror_symmetry.h" +#include "live_effects/lpe-copy_rotate.h" #include "sp-path.h" #include "sp-item-group.h" @@ -126,7 +128,10 @@ void SPLPEItem::set(unsigned int key, gchar const* value) { if (!value) { LivePathEffectObject *lpeobj = (*it)->lpeobject; Inkscape::LivePathEffect::Effect * lpe = lpeobj->get_lpe(); - if (dynamic_cast<Inkscape::LivePathEffect::LPEMeasureLine *>(lpe)){ + if (dynamic_cast<Inkscape::LivePathEffect::LPEMirrorSymmetry *>(lpe) || + dynamic_cast<Inkscape::LivePathEffect::LPEMeasureLine *>(lpe) || + dynamic_cast<Inkscape::LivePathEffect::LPECopyRotate *>(lpe) ) + { lpe->doOnRemove(this); } } @@ -209,7 +214,7 @@ Inkscape::XML::Node* SPLPEItem::write(Inkscape::XML::Document *xml_doc, Inkscape /** * returns true when LPE was successful. */ -bool SPLPEItem::performPathEffect(SPCurve *curve, bool is_clip_or_mask) { +bool SPLPEItem::performPathEffect(SPCurve *curve, SPShape *current, bool is_clip_or_mask) { if (!curve) { return false; @@ -241,12 +246,15 @@ bool SPLPEItem::performPathEffect(SPCurve *curve, bool is_clip_or_mask) { } if (!is_clip_or_mask || (is_clip_or_mask && lpe->apply_to_clippath_and_mask)) { // Groups have their doBeforeEffect called elsewhere + if (current) { + lpe->setCurrentShape(current); + } if (!SP_IS_GROUP(this)) { lpe->doBeforeEffect_impl(this); } try { - lpe->doEffect(curve); + lpe->doEffect(curve); } catch (std::exception & e) { g_warning("Exception during LPE %s execution. \n %s", lpe->getName().c_str(), e.what()); @@ -257,6 +265,8 @@ bool SPLPEItem::performPathEffect(SPCurve *curve, bool is_clip_or_mask) { return false; } if (!SP_IS_GROUP(this)) { + lpe->pathvector_before_effect = curve->get_pathvector(); + lpe->sp_curve->set_pathvector(lpe->pathvector_before_effect); lpe->doAfterEffect(this); } } @@ -604,7 +614,7 @@ bool SPLPEItem::hasPathEffect() const return true; } -bool SPLPEItem::hasPathEffectOfType(int const type) const +bool SPLPEItem::hasPathEffectOfType(int const type, bool is_ready) const { if (path_effect_list->empty()) { return false; @@ -616,7 +626,9 @@ bool SPLPEItem::hasPathEffectOfType(int const type) const if (lpeobj) { Inkscape::LivePathEffect::Effect const* lpe = lpeobj->get_lpe(); if (lpe && (lpe->effectType() == type)) { - return true; + if (is_ready || lpe->isReady()) { + return true; + } } } } @@ -695,10 +707,10 @@ SPLPEItem::apply_to_clip_or_mask(SPItem *clip_mask, SPItem *item) try { if(SP_IS_GROUP(this)){ c->transform(i2anc_affine(SP_GROUP(item), SP_GROUP(this))); - success = this->performPathEffect(c, true); + success = this->performPathEffect(c, SP_SHAPE(clip_mask), true); c->transform(i2anc_affine(SP_GROUP(item), SP_GROUP(this)).inverse()); } else { - success = this->performPathEffect(c, true); + success = this->performPathEffect(c, SP_SHAPE(clip_mask), true); } } catch (std::exception & e) { g_warning("Exception during LPE execution. \n %s", e.what()); @@ -709,12 +721,13 @@ SPLPEItem::apply_to_clip_or_mask(SPItem *clip_mask, SPItem *item) success = false; } Inkscape::XML::Node *repr = clip_mask->getRepr(); - if (success) { + // This c check allow to not apply LPE if curve is NULL after performPathEffect used in clone.obgets LPE + if (success && c) { gchar *str = sp_svg_write_path(c->get_pathvector()); repr->setAttribute("d", str); g_free(str); } else { - // LPE was unsuccesfull. Read the old 'd'-attribute. + // LPE was unsuccesfull or doeffect stack return null.. Read the old 'd'-attribute. if (gchar const * value = repr->attribute("d")) { Geom::PathVector pv = sp_svg_read_pathv(value); SPCurve *oldcurve = new (std::nothrow) SPCurve(pv); @@ -724,7 +737,9 @@ SPLPEItem::apply_to_clip_or_mask(SPItem *clip_mask, SPItem *item) } } } - c->unref(); + if (c) { + c->unref(); + } } } } diff --git a/src/sp-lpe-item.h b/src/sp-lpe-item.h index 9e5cb3329..0f198c49c 100644 --- a/src/sp-lpe-item.h +++ b/src/sp-lpe-item.h @@ -23,6 +23,7 @@ class LivePathEffectObject; class SPCurve; +class SPShape; class SPDesktop; namespace Inkscape{ @@ -69,11 +70,11 @@ public: virtual void update_patheffect(bool write); - bool performPathEffect(SPCurve *curve, bool is_clip_or_mask = false); + bool performPathEffect(SPCurve *curve, SPShape *current = NULL, bool is_clip_or_mask = false); bool pathEffectsEnabled() const; bool hasPathEffect() const; - bool hasPathEffectOfType(int const type) const; + bool hasPathEffectOfType(int const type, bool is_ready = true) const; bool hasPathEffectRecursive() const; Inkscape::LivePathEffect::Effect* getPathEffectOfType(int type); Inkscape::LivePathEffect::Effect const* getPathEffectOfType(int type) const; diff --git a/src/sp-object.cpp b/src/sp-object.cpp index 75f4657ef..0dc301c49 100644 --- a/src/sp-object.cpp +++ b/src/sp-object.cpp @@ -775,6 +775,21 @@ void SPObject::appendChild(Inkscape::XML::Node *child) { repr->appendChild(child); } +SPObject* SPObject::nthChild(unsigned index) { + g_assert(this->repr); + if (hasChildren()) { + std::vector<SPObject*> l; + unsigned counter = 0; + for (auto& child: children) { + if (counter == index) { + return &child; + } + counter++; + } + } + return NULL; +} + void SPObject::addChild(Inkscape::XML::Node *child, Inkscape::XML::Node * prev) { g_assert(this->repr); diff --git a/src/sp-object.h b/src/sp-object.h index 9abbd324b..d145e966b 100644 --- a/src/sp-object.h +++ b/src/sp-object.h @@ -318,6 +318,9 @@ public: SPObject *lastChild() { return children.empty() ? nullptr : &children.back(); } SPObject const *lastChild() const { return children.empty() ? nullptr : &children.back(); } + SPObject *nthChild(unsigned index); + SPObject const *nthChild(unsigned index) const; + enum Action { ActionGeneral, ActionBBox, ActionUpdate, ActionShow }; /** diff --git a/src/sp-path.cpp b/src/sp-path.cpp index 6c69f3463..b593b7937 100644 --- a/src/sp-path.cpp +++ b/src/sp-path.cpp @@ -289,23 +289,10 @@ Geom::Affine SPPath::set_transform(Geom::Affine const &transform) { if (!_curve) { // 0 nodes, nothing to transform return Geom::identity(); } - // Adjust stroke - this->adjust_stroke(transform.descrim()); - - // Adjust pattern fill - this->adjust_pattern(transform); - - // Adjust gradient fill - this->adjust_gradient(transform); - - // Adjust LPE - this->adjust_livepatheffect(transform); // Transform the original-d path if this is a valid LPE this, other else the (ordinary) path if (_curve_before_lpe && hasPathEffectRecursive()) { - if (this->hasPathEffectOfType(Inkscape::LivePathEffect::CLONE_ORIGINAL) || - this->hasPathEffectOfType(Inkscape::LivePathEffect::BEND_PATH)) - { + if (this->hasPathEffectOfType(Inkscape::LivePathEffect::CLONE_ORIGINAL) || this->hasPathEffectOfType(Inkscape::LivePathEffect::BEND_PATH)) { // if path has the CLONE_ORIGINAL LPE applied, don't write the transform to the pathdata, but write it 'unoptimized' // also if the effect is type BEND PATH to fix bug #179842 return transform; @@ -316,6 +303,18 @@ Geom::Affine SPPath::set_transform(Geom::Affine const &transform) { _curve->transform(transform); } + // Adjust stroke + this->adjust_stroke(transform.descrim()); + + // Adjust pattern fill + this->adjust_pattern(transform); + + // Adjust gradient fill + this->adjust_gradient(transform); + + // Adjust LPE + this->adjust_livepatheffect(transform); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); // nothing remains - we've written all of the transform, so return identity @@ -355,9 +354,9 @@ g_message("sp_path_update_patheffect writes 'd' attribute"); if (gchar const * value = repr->attribute("d")) { Geom::PathVector pv = sp_svg_read_pathv(value); SPCurve *oldcurve = new SPCurve(pv); + if (oldcurve) { - this->setCurveInsync(oldcurve, TRUE); - repr->setAttribute("d", value); + this->setCurve(oldcurve, TRUE); oldcurve->unref(); } } diff --git a/src/ui/dialog/polar-arrange-tab.cpp b/src/ui/dialog/polar-arrange-tab.cpp index c51881a96..9485b6ba3 100644 --- a/src/ui/dialog/polar-arrange-tab.cpp +++ b/src/ui/dialog/polar-arrange-tab.cpp @@ -43,14 +43,14 @@ PolarArrangeTab::PolarArrangeTab(ArrangeDialog *parent_) anchorPointLabel.set_alignment(Gtk::ALIGN_START); pack_start(anchorPointLabel, false, false); - anchorBoundingBoxRadio.set_label(C_("Polar arrange tab", "Object's bounding box:")); + anchorBoundingBoxRadio.set_label(C_("Polar arrange tab", "Objects' bounding boxes:")); anchorRadioGroup = anchorBoundingBoxRadio.get_group(); anchorBoundingBoxRadio.signal_toggled().connect(sigc::mem_fun(*this, &PolarArrangeTab::on_anchor_radio_changed)); pack_start(anchorBoundingBoxRadio, false, false); pack_start(anchorSelector, false, false); - anchorObjectPivotRadio.set_label(C_("Polar arrange tab", "Object's rotational center")); + anchorObjectPivotRadio.set_label(C_("Polar arrange tab", "Objects' rotational centers")); anchorObjectPivotRadio.set_group(anchorRadioGroup); anchorObjectPivotRadio.signal_toggled().connect(sigc::mem_fun(*this, &PolarArrangeTab::on_anchor_radio_changed)); pack_start(anchorObjectPivotRadio, false, false); |
