summaryrefslogtreecommitdiffstats
path: root/src/live_effects/lpe-copy_rotate.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/live_effects/lpe-copy_rotate.cpp')
-rw-r--r--src/live_effects/lpe-copy_rotate.cpp617
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 */