diff options
Diffstat (limited to 'src/live_effects/lpe-copy_rotate.cpp')
| -rw-r--r-- | src/live_effects/lpe-copy_rotate.cpp | 617 |
1 files changed, 423 insertions, 194 deletions
diff --git a/src/live_effects/lpe-copy_rotate.cpp b/src/live_effects/lpe-copy_rotate.cpp index efea76039..3abcbf217 100644 --- a/src/live_effects/lpe-copy_rotate.cpp +++ b/src/live_effects/lpe-copy_rotate.cpp @@ -11,38 +11,32 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <glibmm/i18n.h> +#include <gtkmm.h> #include <gdk/gdk.h> #include <2geom/path-intersection.h> #include <2geom/sbasis-to-bezier.h> +#include <2geom/intersection-graph.h> #include "live_effects/lpe-copy_rotate.h" -#include <2geom/path.h> -#include <2geom/transforms.h> -#include <2geom/angle.h> - -#include "knot-holder-entity.h" -#include "knotholder.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> namespace Inkscape { namespace LivePathEffect { -namespace CR { - -class KnotHolderEntityStartingAngle : public LPEKnotHolderEntity { -public: - KnotHolderEntityStartingAngle(LPECopyRotate *effect) : LPEKnotHolderEntity(effect) {}; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); - virtual Geom::Point knot_get() const; +static const Util::EnumData<RotateMethod> RotateMethodData[RM_END] = { + { RM_NORMAL, N_("Normal"), "normal" }, + { RM_KALEIDOSCOPE, N_("Kaleidoscope"), "kaleidoskope" }, + { RM_FUSE, N_("Fuse paths"), "fuse_paths" } }; - -class KnotHolderEntityRotationAngle : public LPEKnotHolderEntity { -public: - KnotHolderEntityRotationAngle(LPECopyRotate *effect) : LPEKnotHolderEntity(effect) {}; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); - virtual Geom::Point knot_get() const; -}; - -} // namespace CR +static const Util::EnumDataConverter<RotateMethod> +RMConverter(RotateMethodData, RM_END); bool pointInTriangle(Geom::Point const &p, Geom::Point const &p1, Geom::Point const &p2, Geom::Point const &p3) @@ -61,28 +55,49 @@ pointInTriangle(Geom::Point const &p, Geom::Point const &p1, Geom::Point const & LPECopyRotate::LPECopyRotate(LivePathEffectObject *lpeobject) : Effect(lpeobject), - origin(_("Origin"), _("Origin of the rotation"), "origin", &wr, this, "Adjust the origin of the rotation"), - starting_angle(_("Starting:"), _("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), + method(_("Method:"), _("Rotate methods"), "method", RMConverter, &wr, this, RM_NORMAL), + origin(_("Origin"), _("Adjust origin of the rotation"), "origin", &wr, this, _("Adjust origin of the rotation")), + starting_point(_("Start point"), _("Starting point to define start angle"), "starting_point", &wr, this, _("Adjust starting point to define start angle")), + 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), + gap(_("Gap"), _("Gap"), "gap", &wr, this, -0.0001), copies_to_360(_("360º Copies"), _("No rotation angle, fixed to 360º"), "copies_to_360", &wr, this, true), - fuse_paths(_("Fuse paths"), _("Fuse paths by helper line, use fill-rule: evenodd for best result"), "fuse_paths", &wr, this, false), + mirror_copies(_("Mirror copies"), _("Mirror between copies"), "mirror_copies", &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; _provides_knotholder_entities = true; - apply_to_clippath_and_mask = true; - + //0.92 compatibility + if (this->getRepr()->attribute("fuse_paths") && strcmp(this->getRepr()->attribute("fuse_paths"), "true") == 0){ + this->getRepr()->setAttribute("fuse_paths", NULL); + this->getRepr()->setAttribute("method", "kaleidoskope"); + this->getRepr()->setAttribute("mirror_copies", "true"); + }; // register all your parameters here, so Inkscape knows which parameters this effect has: - registerParameter(&copies_to_360); - registerParameter(&fuse_paths); + registerParameter(&method); + registerParameter(&num_copies); registerParameter(&starting_angle); + registerParameter(&starting_point); registerParameter(&rotation_angle); - registerParameter(&num_copies); + registerParameter(&gap); registerParameter(&origin); + registerParameter(&copies_to_360); + registerParameter(&mirror_copies); + registerParameter(&split_items); + gap.param_set_range(-99999.0, 99999.0); + gap.param_set_increments(0.1, 0.1); + gap.param_set_digits(5); + num_copies.param_set_range(1, 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; + previous_origin = Geom::Point(0,0); + previous_start_point = Geom::Point(0,0); + starting_point.param_widget_is_visible(false); + reset = false; } LPECopyRotate::~LPECopyRotate() @@ -91,6 +106,242 @@ LPECopyRotate::~LPECopyRotate() } void +LPECopyRotate::doAfterEffect (SPLPEItem const* lpeitem) +{ + if (split_items) { + is_load = false; + SPDocument * document = SP_ACTIVE_DOCUMENT; + if (!document) { + return; + } + items.clear(); + container = dynamic_cast<SPObject *>(sp_lpe_item->parent); + Inkscape::XML::Node *root = sp_lpe_item->document->getReprRoot(); + Inkscape::XML::Node *root_origin = document->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; + const char * id = g_strdup(Glib::ustring("rotated-").append("0").append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str()); + guint counter = 0; + while((elemref = document->getObjectById(id))) { + id = g_strdup(Glib::ustring("rotated-").append(std::to_string(counter)).append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str()); + if (SP_ITEM(elemref)->isHidden()) { + items.push_back(id); + } + counter++; + } + Geom::Affine m = Geom::Translate(-origin) * Geom::Rotate(-(Geom::rad_from_deg(starting_angle))); + for (size_t i = 1; i < num_copies; ++i) { + Geom::Affine r = Geom::identity(); + if(mirror_copies && i%2 != 0) { + r *= Geom::Rotate(Geom::Angle(half_dir)).inverse(); + r *= Geom::Scale(1, -1); + r *= Geom::Rotate(Geom::Angle(half_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 (method != RM_NORMAL) { + if(mirror_copies && i%2 != 0) { + t = m * r * rot * Geom::Rotate(-Geom::rad_from_deg(starting_angle)) * Geom::Translate(origin); + } + } else { + if(mirror_copies && i%2 != 0) { + t = m * Geom::Rotate(Geom::rad_from_deg(-rotation_angle)) * r * 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(); + } +} + +void +LPECopyRotate::cloneD(SPObject *orig, SPObject *dest, Geom::Affine transform, bool root, bool reset) +{ + SPDocument * document = SP_ACTIVE_DOCUMENT; + if (!document) { + return; + } + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + if ( SP_IS_GROUP(orig) && SP_IS_GROUP(dest) && SP_GROUP(orig)->getItemCount() == SP_GROUP(dest)->getItemCount() ) { + std::vector< SPObject * > childs = orig->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, transform, false, reset); + index++; + } + } + SPShape * shape = SP_SHAPE(orig); + if (shape && !SP_IS_PATH(dest)) { + const char * id = dest->getId(); + 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); + dest->getRepr()->setAttribute("d", id); + } + if (SP_IS_PATH(dest) && shape) { + SPCurve *c = NULL; + if (root) { + c = new SPCurve(); + c->set_pathvector(pathvector_after_effect); + } else { + c = shape->getCurve(); + } + if (c) { + SP_PATH(dest)->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; + if (!document) { + return; + } + 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, transform, true, reset); + gchar *str = sp_svg_transform_write(transform); + elemref->getRepr()->setAttribute("transform" , str); + g_free(str); + 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 + // 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) { + 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; + } + Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false,0)); + 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(110,20); + vbox->pack_start(*hbox, true,true,2); + hbox->pack_start(*reset_button, false, false,2); + return dynamic_cast<Gtk::Widget *>(vbox); +} + + +void LPECopyRotate::doOnApply(SPLPEItem const* lpeitem) { using namespace Geom; @@ -107,17 +358,14 @@ LPECopyRotate::doOnApply(SPLPEItem const* lpeitem) void LPECopyRotate::transform_multiply(Geom::Affine const& postmul, bool set) { - if(fuse_paths) { - Geom::Coord angle = Geom::deg_from_rad(atan(-postmul[1]/postmul[0])); - angle += starting_angle; - starting_angle.param_set_value(angle); - } // 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); + if(param->param_key != "starting_point" || postmul.isRotation()) { + param->param_transform_multiply(postmul, set); + } } + sp_lpe_item_update_patheffect(sp_lpe_item, false, false); } void @@ -125,38 +373,54 @@ LPECopyRotate::doBeforeEffect (SPLPEItem const* lpeitem) { using namespace Geom; original_bbox(lpeitem); - if (copies_to_360) { + if (copies_to_360 && num_copies > 2) { rotation_angle.param_set_value(360.0/(double)num_copies); } - if (fuse_paths && rotation_angle * num_copies > 360 && rotation_angle > 0) { + if (method != RM_NORMAL && rotation_angle * num_copies > 360.1 && rotation_angle > 0 && copies_to_360) { num_copies.param_set_value(floor(360/rotation_angle)); } - if (fuse_paths && copies_to_360) { - num_copies.param_set_increments(2,2); + if (method != RM_NORMAL && mirror_copies && copies_to_360) { + 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) { - dist_angle_handle = 1.0; - } A = Point(boundingbox_X.min(), boundingbox_Y.middle()); B = Point(boundingbox_X.middle(), boundingbox_Y.middle()); + if (Geom::are_near(A, B, 0.01)) { + B += Geom::Point(1.0, 0.0); + } dir = unit_vector(B - A); // I first suspected the minus sign to be a bug in 2geom but it is // likely due to SVG's choice of coordinate system orientation (max) - start_pos = origin + dir * Rotate(-rad_from_deg(starting_angle)) * dist_angle_handle; - rot_pos = origin + dir * Rotate(-rad_from_deg(rotation_angle+starting_angle)) * dist_angle_handle; - if ( fuse_paths || copies_to_360 ) { - rot_pos = origin; + bool near_start_point = Geom::are_near(previous_start_point, (Geom::Point)starting_point, 0.01); + bool near_origin = Geom::are_near(previous_origin, (Geom::Point)origin, 0.01); + if (!near_start_point) { + starting_angle.param_set_value(deg_from_rad(-angle_between(dir, starting_point - origin))); + if (GDK_SHIFT_MASK) { + dist_angle_handle = L2(B - A); + } else { + dist_angle_handle = L2(starting_point - origin); + } } - SPLPEItem * item = const_cast<SPLPEItem*>(lpeitem); - item->apply_to_clippath(item); - item->apply_to_mask(item); + if (dist_angle_handle < 1.0) { + dist_angle_handle = 1.0; + } + double distance = dist_angle_handle; + if (previous_start_point != Geom::Point(0,0) || previous_origin != Geom::Point(0,0)) { + distance = Geom::distance(previous_origin, starting_point); + } + start_pos = origin + dir * Rotate(-rad_from_deg(starting_angle)) * distance; + if (!near_start_point || !near_origin || split_items) { + starting_point.param_setValue(start_pos); + } + + previous_origin = (Geom::Point)origin; + previous_start_point = (Geom::Point)starting_point; } void @@ -231,30 +495,21 @@ LPECopyRotate::setFusion(Geom::PathVector &path_on, Geom::Path divider, double s } Geom::PathVector tmp_path_helper; Geom::Path append_path = original; - + Geom::Point previous = original.finalPoint(); for (int i = 0; i < num_copies; ++i) { Geom::Rotate rot(-Geom::rad_from_deg(rotation_angle * (i))); Geom::Affine m = pre * rot * Geom::Translate(origin); - if (i%2 != 0) { - Geom::Point A = (Geom::Point)origin; - Geom::Point B = origin + dir * Geom::Rotate(-Geom::rad_from_deg((rotation_angle*i)+starting_angle)) * size_divider; - Geom::Translate m1(A[0], A[1]); - double hyp = Geom::distance(A, B); - double c = (B[0] - A[0]) / hyp; // cos(alpha) - double s = (B[1] - A[1]) / hyp; // sin(alpha) - - Geom::Affine m2(c, -s, s, c, 0.0, 0.0); - Geom::Scale sca(1.0, -1.0); - - Geom::Affine tmp_m = m1.inverse() * m2; - m = tmp_m; - m = m * sca; - m = m * m2.inverse(); - m = m * m1; + if (i%2 != 0 && mirror_copies) { + Geom::Point point_a = (Geom::Point)origin; + Geom::Point point_b = origin + dir * Geom::Rotate(-Geom::rad_from_deg((rotation_angle*i)+starting_angle)) * size_divider; + Geom::Line ls(point_a, point_b); + m = Geom::reflection (ls.vector(), point_a); + append_path *= m; } else { append_path = original; + append_path *= m; } - append_path *= m; + previous = append_path.finalPoint(); if (tmp_path_helper.size() > 0) { if (Geom::are_near(tmp_path_helper[tmp_path_helper.size()-1].finalPoint(), append_path.finalPoint())) { Geom::Path tmp_append = append_path.reversed(); @@ -340,63 +595,98 @@ LPECopyRotate::setFusion(Geom::PathVector &path_on, Geom::Path divider, double s tmp_path.clear(); } -Geom::Piecewise<Geom::D2<Geom::SBasis> > -LPECopyRotate::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in) +Geom::PathVector +LPECopyRotate::doEffect_path (Geom::PathVector const & path_in) { - using namespace Geom; - - if (num_copies == 1 && !fuse_paths) { - return pwd2_in; - } - + Geom::PathVector path_out; 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 * Rotate(-rad_from_deg(starting_angle)) * size_divider; - Geom::Point line_end = origin + dir * Rotate(-rad_from_deg(rotation_angle + starting_angle)) * size_divider; - //Note:: beter way to do this - //Whith AppendNew have problems whith the crossing order - Geom::Path divider = Geom::Path(line_start); + Geom::OptRect bbox = sp_lpe_item->geometricBounds(); + size_divider = Geom::distance(origin,bbox) + (diagonal * 6); + 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; + divider = Geom::Path(line_start); divider.appendNew<Geom::LineSegment>((Geom::Point)origin); divider.appendNew<Geom::LineSegment>(line_end); - Piecewise<D2<SBasis> > output; - Affine pre = Translate(-origin) * Rotate(-rad_from_deg(starting_angle)); - 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; - } - bool end_open = false; - if (path_it->closed()) { - const Geom::Curve &closingline = path_it->back_closed(); - if (!are_near(closingline.initialPoint(), closingline.finalPoint())) { - end_open = true; - } - } - Geom::Path original = (Geom::Path)(*path_it); - if (end_open && path_it->closed()) { - original.close(false); - original.appendNew<Geom::LineSegment>( original.initialPoint() ); - original.close(true); - } - tmp_path.push_back(original); - setFusion(tmp_path, divider, size_divider); - path_out.insert(path_out.end(), tmp_path.begin(), tmp_path.end()); - tmp_path.clear(); + divider.close(); + half_dir = unit_vector(Geom::middle_point(line_start,line_end) - (Geom::Point)origin); + if (method != RM_NORMAL) { + if (method != RM_KALEIDOSCOPE) { + path_out = doEffect_path_post(path_in); + } else { + path_out = pathv_to_linear_and_cubic_beziers(path_in); + } + if (num_copies == 0) { + return path_out; } - if (path_out.size()>0) { - output = paths_to_pw(path_out); + Geom::PathVector triangle; + triangle.push_back(divider); + Geom::PathIntersectionGraph *pig = new Geom::PathIntersectionGraph(triangle, path_out); + if (pig && !path_out.empty() && !triangle.empty()) { + path_out = pig->getIntersection(); + } + path_out *= Geom::Translate(half_dir * gap); + if ( !split_items ) { + path_out *= Geom::Translate(half_dir * gap).inverse(); + path_out = doEffect_path_post(path_out); } } else { - for (int i = 0; i < num_copies; ++i) { - Rotate rot(-rad_from_deg(rotation_angle * i)); - Affine t = pre * rot * Translate(origin); - output.concat(pwd2_in * t); + path_out = doEffect_path_post(path_in); + } + return pathv_to_linear_and_cubic_beziers(path_out); +} + +Geom::PathVector +LPECopyRotate::doEffect_path_post (Geom::PathVector const & path_in) +{ + if ((split_items || num_copies == 1) && method == RM_NORMAL) { + if (split_items) { + Geom::PathVector path_out = pathv_to_linear_and_cubic_beziers(path_in); + Geom::Affine m = Geom::Translate(-origin) * Geom::Rotate(-(Geom::rad_from_deg(starting_angle))); + Geom::Affine t = m * Geom::Rotate(-Geom::rad_from_deg(starting_angle)) * Geom::Rotate(Geom::rad_from_deg(starting_angle)) * Geom::Translate(origin); + return path_out * t; + } + return path_in; + } + + Geom::Affine pre = Geom::Translate(-origin) * Geom::Rotate(-Geom::rad_from_deg(starting_angle)); + Geom::PathVector original_pathv = pathv_to_linear_and_cubic_beziers(path_in); + Geom::PathVector output_pv; + Geom::PathVector output; + for (int i = 0; i < num_copies; ++i) { + Geom::Rotate rot(-Geom::rad_from_deg(rotation_angle * i)); + Geom::Affine r = Geom::identity(); + if( i%2 != 0 && mirror_copies) { + r *= Geom::Rotate(Geom::Angle(half_dir)).inverse(); + r *= Geom::Scale(1, -1); + r *= Geom::Rotate(Geom::Angle(half_dir)); + } + Geom::Affine t = pre * r * rot * Geom::Rotate(Geom::rad_from_deg(starting_angle)) * Geom::Translate(origin); + if(mirror_copies && i%2 != 0) { + t = pre * r * rot * Geom::Rotate(Geom::rad_from_deg(starting_angle)).inverse() * Geom::Translate(origin); + } + if (method != RM_NORMAL) { + Geom::PathVector join_pv = original_pathv * t; + join_pv *= Geom::Translate(half_dir * rot * gap); + Geom::PathIntersectionGraph *pig = new Geom::PathIntersectionGraph(output_pv, join_pv); + if (pig) { + if (!output_pv.empty()) { + output_pv = pig->getUnion(); + } else { + output_pv = join_pv; + } + } + } else { + t = pre * Geom::Rotate(-Geom::rad_from_deg(starting_angle)) * r * rot * Geom::Rotate(Geom::rad_from_deg(starting_angle)) * Geom::Translate(origin); + if(mirror_copies && i%2 != 0) { + t = pre * Geom::Rotate(Geom::rad_from_deg(-starting_angle-rotation_angle)) * r * rot * Geom::Rotate(-Geom::rad_from_deg(starting_angle)) * Geom::Translate(origin); + } + output_pv = path_in * t; + output.insert(output.end(), output_pv.begin(), output_pv.end()); } } + if (method != RM_NORMAL) { + output = output_pv; + } return output; } @@ -408,7 +698,7 @@ LPECopyRotate::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geo Geom::Path hp; hp.start(start_pos); hp.appendNew<Geom::LineSegment>((Geom::Point)origin); - hp.appendNew<Geom::LineSegment>(origin + dir * Rotate(-rad_from_deg(rotation_angle+starting_angle)) * dist_angle_handle); + hp.appendNew<Geom::LineSegment>(origin + dir * Rotate(-rad_from_deg(rotation_angle+starting_angle)) * Geom::distance(origin,starting_point)); Geom::PathVector pathv; pathv.push_back(hp); hp_vec.push_back(pathv); @@ -422,84 +712,23 @@ LPECopyRotate::resetDefaults(SPItem const* item) } void -LPECopyRotate::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) -{ - { - KnotHolderEntity *e = new CR::KnotHolderEntityStartingAngle(this); - e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, - _("Adjust the starting angle")); - knotholder->add(e); - } - { - KnotHolderEntity *e = new CR::KnotHolderEntityRotationAngle(this); - e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, - _("Adjust the rotation angle")); - knotholder->add(e); - } -}; - -namespace CR { - -using namespace Geom; - -void -KnotHolderEntityStartingAngle::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state) +LPECopyRotate::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/) { - LPECopyRotate* lpe = dynamic_cast<LPECopyRotate *>(_effect); - - Geom::Point const s = snap_knot_position(p, state); - - // I first suspected the minus sign to be a bug in 2geom but it is - // likely due to SVG's choice of coordinate system orientation (max) - lpe->starting_angle.param_set_value(deg_from_rad(-angle_between(lpe->dir, s - lpe->origin))); - if (state & GDK_SHIFT_MASK) { - lpe->dist_angle_handle = L2(lpe->B - lpe->A); - } else { - lpe->dist_angle_handle = L2(p - lpe->origin); - } - - // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating. - sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true); + processObjects(LPE_VISIBILITY); } -void -KnotHolderEntityRotationAngle::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state) +void +LPECopyRotate::doOnRemove (SPLPEItem const* /*lpeitem*/) { - LPECopyRotate* lpe = dynamic_cast<LPECopyRotate *>(_effect); - - Geom::Point const s = snap_knot_position(p, state); - - // I first suspected the minus sign to be a bug in 2geom but it is - // likely due to SVG's choice of coordinate system orientation (max) - lpe->rotation_angle.param_set_value(deg_from_rad(-angle_between(lpe->dir, s - lpe->origin)) - lpe->starting_angle); - if (state & GDK_SHIFT_MASK) { - lpe->dist_angle_handle = L2(lpe->B - lpe->A); - } else { - lpe->dist_angle_handle = L2(p - lpe->origin); + //set "keep paths" hook on sp-lpe-item.cpp + if (keep_paths) { + processObjects(LPE_TO_OBJECTS); + items.clear(); + return; } - - // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating. - sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true); + processObjects(LPE_ERASE); } -Geom::Point -KnotHolderEntityStartingAngle::knot_get() const -{ - LPECopyRotate const *lpe = dynamic_cast<LPECopyRotate const*>(_effect); - return lpe->start_pos; -} - -Geom::Point -KnotHolderEntityRotationAngle::knot_get() const -{ - LPECopyRotate const *lpe = dynamic_cast<LPECopyRotate const*>(_effect); - return lpe->rot_pos; -} - -} // namespace CR - -/* ######################## */ - } //namespace LivePathEffect } /* namespace Inkscape */ |
