diff options
| author | Jabier Arraiza Cenoz <jabier.arraiza@marker.es> | 2016-05-08 09:32:39 +0000 |
|---|---|---|
| committer | jabiertxof <info@marker.es> | 2016-05-08 09:32:39 +0000 |
| commit | bc76326ca6312ab85f64bbb32d8635faea07387e (patch) | |
| tree | 9ecaa2b74d1fa392e9b52b6295c0a05319cbf442 | |
| parent | Start of GTK3 external style sheet support. (diff) | |
| parent | update to trunk (diff) | |
| download | inkscape-bc76326ca6312ab85f64bbb32d8635faea07387e.tar.gz inkscape-bc76326ca6312ab85f64bbb32d8635faea07387e.zip | |
Merge lp:~inkscape.dev/inkscape/mirror-symetry-lpe-improvements into lp:inkscape
(bzr r14877)
| -rw-r--r-- | src/live_effects/effect.cpp | 2 | ||||
| -rw-r--r-- | src/live_effects/lpe-mirror_symmetry.cpp | 330 | ||||
| -rw-r--r-- | src/live_effects/lpe-mirror_symmetry.h | 37 |
3 files changed, 331 insertions, 38 deletions
diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index c6ecba30a..437aed5bd 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -104,7 +104,6 @@ const Util::EnumData<EffectType> LPETypeData[] = { {EXTRUDE, N_("Extrude"), "extrude"}, {LATTICE, N_("Lattice Deformation"), "lattice"}, {LINE_SEGMENT, N_("Line Segment"), "line_segment"}, - {MIRROR_SYMMETRY, N_("Mirror symmetry"), "mirror_symmetry"}, {OFFSET, N_("Offset"), "offset"}, {PARALLEL, N_("Parallel"), "parallel"}, {PATH_LENGTH, N_("Path length"), "path_length"}, @@ -144,6 +143,7 @@ const Util::EnumData<EffectType> LPETypeData[] = { {BSPLINE, N_("BSpline"), "bspline"}, {JOIN_TYPE, N_("Join type"), "join_type"}, {TAPER_STROKE, N_("Taper stroke"), "taper_stroke"}, + {MIRROR_SYMMETRY, N_("Mirror symmetry"), "mirror_symmetry"}, {COPY_ROTATE, N_("Rotate copies"), "copy_rotate"}, /* Ponyscape -> Inkscape 0.92*/ {ATTACH_PATH, N_("Attach path"), "attach_path"}, diff --git a/src/live_effects/lpe-mirror_symmetry.cpp b/src/live_effects/lpe-mirror_symmetry.cpp index c13cffb6a..88fee4c47 100644 --- a/src/live_effects/lpe-mirror_symmetry.cpp +++ b/src/live_effects/lpe-mirror_symmetry.cpp @@ -6,36 +6,84 @@ * Maximilian Albert * Johan Engelen * Abhishek Sharma + * Jabiertxof * * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl> * Copyright (C) Maximilin Albert 2008 <maximilian.albert@gmail.com> * * Released under GNU GPL, read the file 'COPYING' for more information */ - +#include <gtkmm.h> #include <glibmm/i18n.h> #include "live_effects/lpe-mirror_symmetry.h" #include <sp-path.h> #include <display/curve.h> #include <svg/path-string.h> - +#include "helper/geom.h" #include <2geom/path.h> +#include <2geom/path-intersection.h> #include <2geom/transforms.h> #include <2geom/affine.h> +#include "knot-holder-entity.h" +#include "knotholder.h" +#include "inkscape.h" namespace Inkscape { namespace LivePathEffect { +static const Util::EnumData<ModeType> ModeTypeData[MT_END] = { + { MT_V, N_("Vertical Page Center"), "Vertical Page Center, use select tool to move item instead line" }, + { MT_H, N_("Horizontal Page Center"), "Horizontal Page Center, use select tool to move item instead line" }, + { MT_FREE, N_("Free from reflection line"), "Free from path" }, + { MT_X, N_("X from middle knot"), "X from middle knot" }, + { MT_Y, N_("Y from middle knot"), "Y from middle knot" } +}; +static const Util::EnumDataConverter<ModeType> +MTConverter(ModeTypeData, MT_END); + +namespace MS { + +class KnotHolderEntityCenterMirrorSymmetry : public LPEKnotHolderEntity { +public: + KnotHolderEntityCenterMirrorSymmetry(LPEMirrorSymmetry *effect) : LPEKnotHolderEntity(effect){}; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); + virtual Geom::Point knot_get() const; +}; + +class KnotHolderEntityStartMirrorSymmetry : public LPEKnotHolderEntity { +public: + KnotHolderEntityStartMirrorSymmetry(LPEMirrorSymmetry *effect) : LPEKnotHolderEntity(effect){}; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); + virtual Geom::Point knot_get() const; +}; + +class KnotHolderEntityEndMirrorSymmetry : public LPEKnotHolderEntity { +public: + KnotHolderEntityEndMirrorSymmetry(LPEMirrorSymmetry *effect) : LPEKnotHolderEntity(effect){}; + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); + virtual Geom::Point knot_get() const; +}; + +} // namespace MS + LPEMirrorSymmetry::LPEMirrorSymmetry(LivePathEffectObject *lpeobject) : Effect(lpeobject), + mode(_("Mode"), _("Symmetry move mode"), "mode", MTConverter, &wr, this, MT_FREE), discard_orig_path(_("Discard original path?"), _("Check this to only keep the mirrored part of the path"), "discard_orig_path", &wr, this, false), - reflection_line(_("Reflection line:"), _("Line which serves as 'mirror' for the reflection"), "reflection_line", &wr, this, "M0,0 L100,100") + fuse_paths(_("Fuse paths"), _("Fuse original and the reflection into a single path"), "fuse_paths", &wr, this, false), + oposite_fuse(_("Oposite fuse"), _("Picks the other side of the mirror as the original"), "oposite_fuse", &wr, this, false), + start_point(_("Start mirror line"), _("Start mirror line"), "start_point", &wr, this, "Adjust the start of mirroring"), + end_point(_("End mirror line"), _("End mirror line"), "end_point", &wr, this, "Adjust end of mirroring") { show_orig_path = true; + registerParameter(&mode); + registerParameter( &discard_orig_path); + registerParameter( &fuse_paths); + registerParameter( &oposite_fuse); + registerParameter( &start_point); + registerParameter( &end_point); apply_to_clippath_and_mask = true; - registerParameter( dynamic_cast<Parameter *>(&discard_orig_path) ); - registerParameter( dynamic_cast<Parameter *>(&reflection_line) ); } LPEMirrorSymmetry::~LPEMirrorSymmetry() @@ -43,60 +91,276 @@ LPEMirrorSymmetry::~LPEMirrorSymmetry() } void + +LPEMirrorSymmetry::doBeforeEffect (SPLPEItem const* lpeitem) +{ + using namespace Geom; + + 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]); + } + if (mode == MT_X) { + point_a = Geom::Point(center_point[X],boundingbox_Y.min()); + point_b = Geom::Point(center_point[X],boundingbox_Y.max()); + } + line_separation.setPoints(point_a, point_b); + if ( mode == MT_X || mode == MT_Y ) { + start_point.param_setValue(point_a); + end_point.param_setValue(point_b); + center_point = Geom::middle_point(point_a, point_b); + } else if ( mode == MT_FREE) { + if(!are_near(previous_center,center_point, 0.01)) { + Geom::Point trans = center_point - previous_center; + start_point.param_setValue(start_point * trans); + end_point.param_setValue(end_point * trans); + line_separation.setPoints(start_point, end_point); + } else { + center_point = Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point); + line_separation.setPoints(start_point, end_point); + } + } else if ( mode == MT_V){ + if(SP_ACTIVE_DESKTOP){ + SPDocument * doc = SP_ACTIVE_DESKTOP->getDocument(); + Geom::Rect view_box_rect = doc->getViewBox(); + Geom::Point sp = Geom::Point(view_box_rect.width()/2.0, 0); + sp *= i2anc_affine(SP_OBJECT(lpeitem), SP_OBJECT(SP_ACTIVE_DESKTOP->currentLayer()->parent)) .inverse(); + start_point.param_setValue(sp); + Geom::Point ep = Geom::Point(view_box_rect.width()/2.0, view_box_rect.height()); + ep *= i2anc_affine(SP_OBJECT(lpeitem), SP_OBJECT(SP_ACTIVE_DESKTOP->currentLayer()->parent)) .inverse(); + end_point.param_setValue(ep); + center_point = Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point); + line_separation.setPoints(start_point, end_point); + } + } else { //horizontal page + if(SP_ACTIVE_DESKTOP){ + SPDocument * doc = SP_ACTIVE_DESKTOP->getDocument(); + Geom::Rect view_box_rect = doc->getViewBox(); + Geom::Point sp = Geom::Point(0, view_box_rect.height()/2.0); + sp *= i2anc_affine(SP_OBJECT(lpeitem), SP_OBJECT(SP_ACTIVE_DESKTOP->currentLayer()->parent)) .inverse(); + start_point.param_setValue(sp); + Geom::Point ep = Geom::Point(view_box_rect.width(), view_box_rect.height()/2.0); + ep *= i2anc_affine(SP_OBJECT(lpeitem), SP_OBJECT(SP_ACTIVE_DESKTOP->currentLayer()->parent)) .inverse(); + end_point.param_setValue(ep); + center_point = Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point); + line_separation.setPoints(start_point, end_point); + } + } + previous_center = center_point; +} + +void LPEMirrorSymmetry::doOnApply (SPLPEItem const* lpeitem) { using namespace Geom; - // fixme: what happens if the bbox is empty? - // fixme: this is probably wrong - Geom::Affine t = lpeitem->i2dt_affine(); - Geom::Rect bbox = *lpeitem->desktopVisualBounds(); - - Point A(bbox.left(), bbox.bottom()); - Point B(bbox.left(), bbox.top()); - A *= t; - B *= t; - Piecewise<D2<SBasis> > rline = Piecewise<D2<SBasis> >(D2<SBasis>(SBasis(A[X], B[X]), SBasis(A[Y], B[Y]))); - reflection_line.set_new_value(rline, true); + original_bbox(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); + start_point.param_update_default(point_a); + end_point.param_setValue(point_b); + end_point.param_update_default(point_b); + center_point = point_c; + previous_center = center_point; } + Geom::PathVector LPEMirrorSymmetry::doEffect_path (Geom::PathVector const & path_in) { - // Don't allow empty path parameter: - if ( reflection_line.get_pathvector().empty() ) { - return path_in; - } - + Geom::PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(path_in); Geom::PathVector path_out; - if (!discard_orig_path) { - path_out = path_in; + + if (!discard_orig_path && !fuse_paths) { + path_out = pathv_to_linear_and_cubic_beziers(path_in); } - Geom::PathVector mline(reflection_line.get_pathvector()); - Geom::Point A(mline.front().initialPoint()); - Geom::Point B(mline.back().finalPoint()); + Geom::Point point_a(line_separation.initialPoint()); + Geom::Point point_b(line_separation.finalPoint()); - Geom::Affine m1(1.0, 0.0, 0.0, 1.0, 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::Translate m1(point_a[0], point_a[1]); + double hyp = Geom::distance(point_a, point_b); + double c = (point_b[0] - point_a[0]) / hyp; // cos(alpha) + double s = (point_b[1] - point_a[1]) / hyp; // sin(alpha) Geom::Affine m2(c, -s, s, c, 0.0, 0.0); - Geom::Affine sca(1.0, 0.0, 0.0, -1.0, 0.0, 0.0); + Geom::Scale sca(1.0, -1.0); Geom::Affine m = m1.inverse() * m2; m = m * sca; m = m * m2.inverse(); m = m * m1; - for (int i = 0; i < static_cast<int>(path_in.size()); ++i) { - path_out.push_back(path_in[i] * m); + 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; + double time_start = 0.0; + int position = 0; + 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 = *path_it; + if (end_open && path_it->closed()) { + original.close(false); + original.appendNew<Geom::LineSegment>( original.initialPoint() ); + original.close(true); + } + Geom::Point s = start_point; + Geom::Point e = end_point; + double dir = line_separation.angle(); + 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(center_point, bbox) + diagonal; + s = Geom::Point::polar(dir,size_divider) + center_point; + e = Geom::Point::polar(dir + Geom::rad_from_deg(180),size_divider) + center_point; + Geom::Path divider = Geom::Path(s); + divider.appendNew<Geom::LineSegment>(e); + Geom::Crossings cs = crossings(original, divider); + std::vector<double> crossed; + for(unsigned int i = 0; i < cs.size(); i++) { + crossed.push_back(cs[i].ta); + } + std::sort(crossed.begin(), crossed.end()); + for (unsigned int i = 0; i < crossed.size(); i++) { + double time_end = crossed[i]; + if (time_start != time_end && time_end - time_start > Geom::EPSILON) { + Geom::Path portion = original.portion(time_start, time_end); + if (!portion.empty()) { + Geom::Point middle = portion.pointAt((double)portion.size()/2.0); + position = Geom::sgn(Geom::cross(e - s, middle - s)); + if (!oposite_fuse) { + position *= -1; + } + if (position == 1) { + Geom::Path mirror = portion.reversed() * m; + mirror.setInitial(portion.finalPoint()); + portion.append(mirror); + if(i!=0) { + portion.setFinal(portion.initialPoint()); + portion.close(); + } + tmp_path.push_back(portion); + } + portion.clear(); + } + } + time_start = time_end; + } + position = Geom::sgn(Geom::cross(e - s, original.finalPoint() - s)); + if (!oposite_fuse) { + position *= -1; + } + if (cs.size()!=0 && position == 1) { + if (time_start != original.size() && original.size() - time_start > Geom::EPSILON) { + Geom::Path portion = original.portion(time_start, original.size()); + if (!portion.empty()) { + portion = portion.reversed(); + Geom::Path mirror = portion.reversed() * m; + mirror.setInitial(portion.finalPoint()); + portion.append(mirror); + portion = portion.reversed(); + if (!original.closed()) { + tmp_path.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); + } else { + tmp_path.push_back(portion); + } + tmp_path[0].close(); + } + portion.clear(); + } + } + } + if (cs.size() == 0 && position == 1) { + tmp_path.push_back(original); + tmp_path.push_back(original * m); + } + path_out.insert(path_out.end(), tmp_path.begin(), tmp_path.end()); + tmp_path.clear(); + } + } + + if (!fuse_paths || discard_orig_path) { + for (int i = 0; i < static_cast<int>(path_in.size()); ++i) { + path_out.push_back(path_in[i] * m); + } } return path_out; } +void +LPEMirrorSymmetry::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec) +{ + using namespace Geom; + hp_vec.clear(); + Geom::Path path; + Geom::Point s = start_point; + Geom::Point e = end_point; + path.start( s ); + path.appendNew<Geom::LineSegment>( e ); + Geom::PathVector helper; + helper.push_back(path); + hp_vec.push_back(helper); +} + +void +LPEMirrorSymmetry::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) +{ + SPKnotShapeType knot_shape = SP_KNOT_SHAPE_CIRCLE; + SPKnotModeType knot_mode = SP_KNOT_MODE_XOR; + guint32 knot_color = 0x0000ff00; + { + KnotHolderEntity *c = new MS::KnotHolderEntityCenterMirrorSymmetry(this); + c->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, + _("Adjust the center"), knot_shape, knot_mode, knot_color ); + knotholder->add(c); + } +}; + +namespace MS { + +using namespace Geom; + +void +KnotHolderEntityCenterMirrorSymmetry::knot_set(Geom::Point const &p, Geom::Point const &origin, guint state) +{ + LPEMirrorSymmetry* lpe = dynamic_cast<LPEMirrorSymmetry *>(_effect); + Geom::Point const s = snap_knot_position(p, state); + lpe->center_point = s; + + // 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); +} + +Geom::Point +KnotHolderEntityCenterMirrorSymmetry::knot_get() const +{ + LPEMirrorSymmetry const *lpe = dynamic_cast<LPEMirrorSymmetry const*>(_effect); + return lpe->center_point; +} + +} // namespace CR + } //namespace LivePathEffect } /* namespace Inkscape */ diff --git a/src/live_effects/lpe-mirror_symmetry.h b/src/live_effects/lpe-mirror_symmetry.h index 64a72d7b3..3a244cb7e 100644 --- a/src/live_effects/lpe-mirror_symmetry.h +++ b/src/live_effects/lpe-mirror_symmetry.h @@ -8,6 +8,7 @@ * Authors: * Maximilian Albert * Johan Engelen + * Jabiertxof * * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl> * Copyright (C) Maximilin Albert 2008 <maximilian.albert@gmail.com> @@ -19,22 +20,50 @@ #include "live_effects/parameter/parameter.h" #include "live_effects/parameter/point.h" #include "live_effects/parameter/path.h" +#include "live_effects/parameter/enum.h" +#include "live_effects/lpegroupbbox.h" namespace Inkscape { namespace LivePathEffect { -class LPEMirrorSymmetry : public Effect { +namespace MS { +// we need a separate namespace to avoid clashes with LPEPerpBisector +class KnotHolderEntityCenterMirrorSymmetry; +} + +enum ModeType { + MT_V, + MT_H, + MT_FREE, + MT_X, + MT_Y, + MT_END +}; + +class LPEMirrorSymmetry : public Effect, GroupBBoxEffect { public: LPEMirrorSymmetry(LivePathEffectObject *lpeobject); virtual ~LPEMirrorSymmetry(); - virtual void doOnApply (SPLPEItem const* lpeitem); - + virtual void doBeforeEffect (SPLPEItem const* lpeitem); virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in); + /* the knotholder entity classes must be declared friends */ + friend class MS::KnotHolderEntityCenterMirrorSymmetry; + void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item); + +protected: + virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec); private: + EnumParam<ModeType> mode; BoolParam discard_orig_path; - PathParam reflection_line; + BoolParam fuse_paths; + BoolParam oposite_fuse; + PointParam start_point; + PointParam end_point; + Geom::Line line_separation; + Geom::Point previous_center; + Geom::Point center_point; LPEMirrorSymmetry(const LPEMirrorSymmetry&); LPEMirrorSymmetry& operator=(const LPEMirrorSymmetry&); |
