diff options
| author | Jabier Arraiza Cenoz <jabier.arraiza@marker.es> | 2014-11-15 19:07:31 +0000 |
|---|---|---|
| committer | Jabiertxof <jtx@jtx.marker.es> | 2014-11-15 19:07:31 +0000 |
| commit | 124e1974a8eff0d45063c618266db15cd8b113cb (patch) | |
| tree | 2e2f05d6d902e9ff3bff930e34d2b60e34131275 /src | |
| parent | ignore this commit (diff) | |
| parent | Macro cleanup of commented out code. (diff) | |
| download | inkscape-124e1974a8eff0d45063c618266db15cd8b113cb.tar.gz inkscape-124e1974a8eff0d45063c618266db15cd8b113cb.zip | |
Add vertical and horizontal modes
(bzr r13682.1.11)
Diffstat (limited to 'src')
| -rw-r--r-- | src/knotholder.cpp | 6 | ||||
| -rw-r--r-- | src/live_effects/effect.cpp | 11 | ||||
| -rw-r--r-- | src/live_effects/effect.h | 2 | ||||
| -rw-r--r-- | src/live_effects/lpe-mirror_symmetry.cpp | 238 | ||||
| -rw-r--r-- | src/live_effects/lpe-mirror_symmetry.h | 30 |
5 files changed, 263 insertions, 24 deletions
diff --git a/src/knotholder.cpp b/src/knotholder.cpp index f46daa09e..28f6f5748 100644 --- a/src/knotholder.cpp +++ b/src/knotholder.cpp @@ -69,6 +69,12 @@ KnotHolder::KnotHolder(SPDesktop *desktop, SPItem *item, SPKnotHolderReleasedFun } KnotHolder::~KnotHolder() { + if(SP_IS_LPE_ITEM(item)){ + Inkscape::LivePathEffect::Effect *effect = SP_LPE_ITEM(item)->getCurrentLPE(); + if(effect){ + effect->removeHandles(); + } + } sp_object_unref(item); for (std::list<KnotHolderEntity *>::iterator i = entity.begin(); i != entity.end(); ++i) diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index e49a15dd0..391eccdd3 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -106,7 +106,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"}, @@ -153,6 +152,7 @@ const Util::EnumData<EffectType> LPETypeData[] = { {PERSPECTIVE_ENVELOPE, N_("Perspective/Envelope"), "perspective-envelope"}, {FILLET_CHAMFER, N_("Fillet/Chamfer"), "fillet-chamfer"}, {INTERPOLATE_POINTS, N_("Interpolate points"), "interpolate_points"}, + {MIRROR_SYMMETRY, N_("Mirror symmetry"), "mirror_symmetry"}, }; const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, sizeof(LPETypeData)/sizeof(*LPETypeData)); @@ -369,6 +369,7 @@ Effect::Effect(LivePathEffectObject *lpeobject) registerParameter( dynamic_cast<Parameter *>(&is_visible) ); is_visible.widget_is_visible = false; current_zoom = 0.0; + knot_holder = NULL; } Effect::~Effect() @@ -613,7 +614,7 @@ Effect::registerParameter(Parameter * param) void Effect::addHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) { using namespace Inkscape::LivePathEffect; - + knot_holder = knotholder; // add handles provided by the effect itself addKnotHolderEntities(knotholder, desktop, item); @@ -623,6 +624,12 @@ Effect::addHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) { } } +void +Effect::removeHandles(){ + if(knot_holder){ + knot_holder = NULL; + } +} /** * Return a vector of PathVectors which contain all canvas indicators for this effect. * This is the function called by external code to get all canvas indicators (effect and its parameters) diff --git a/src/live_effects/effect.h b/src/live_effects/effect.h index 7da76b267..5d715c7f2 100644 --- a/src/live_effects/effect.h +++ b/src/live_effects/effect.h @@ -101,6 +101,7 @@ public: // (but spiro lpe still needs it!) virtual LPEPathFlashType pathFlashType() const { return DEFAULT; } void addHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item); + void removeHandles(); std::vector<Geom::PathVector> getCanvasIndicators(SPLPEItem const* lpeitem); inline bool providesOwnFlashPaths() const { @@ -160,6 +161,7 @@ protected: SPLPEItem * sp_lpe_item; // these get stored in doBeforeEffect_impl, and derived classes may do as they please with them. Glib::ustring const * defaultUnit; // these get stored in doBeforeEffect_impl, and derived classes may do as they please with them. + KnotHolder *knot_holder; double current_zoom; std::vector<Geom::Point> selectedNodesPoints; SPCurve * sp_curve; diff --git a/src/live_effects/lpe-mirror_symmetry.cpp b/src/live_effects/lpe-mirror_symmetry.cpp index 0bb67a4a2..4024ff83e 100644 --- a/src/live_effects/lpe-mirror_symmetry.cpp +++ b/src/live_effects/lpe-mirror_symmetry.cpp @@ -12,30 +12,61 @@ * * 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" namespace Inkscape { namespace LivePathEffect { +static const Util::EnumData<ModeType> ModeTypeData[MT_END] = { + { 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; +}; + +} // namespace MS + LPEMirrorSymmetry::LPEMirrorSymmetry(LivePathEffectObject *lpeobject) : Effect(lpeobject), + mode(_("Mode"), _("Symetry 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") + fusionPaths(_("Fusioned symetry"), _("Fusion right side whith symm"), "fusionPaths", &wr, this, true), + reverseFusion(_("Reverse fusion"), _("Reverse fusion"), "reverseFusion", &wr, this, false), + reflection_line(_("Reflection line:"), _("Line which serves as 'mirror' for the reflection"), "reflection_line", &wr, this, "M0,0 L1,0"), + center(_("Center of mirroring (X or Y)"), _("Center of the mirror"), "center", &wr, this, "Adjust the center of mirroring") { show_orig_path = true; - registerParameter( dynamic_cast<Parameter *>(&discard_orig_path) ); - registerParameter( dynamic_cast<Parameter *>(&reflection_line) ); + registerParameter(&mode); + registerParameter( &discard_orig_path); + registerParameter( &fusionPaths); + registerParameter( &reverseFusion); + registerParameter( &reflection_line); + registerParameter( ¢er); + } LPEMirrorSymmetry::~LPEMirrorSymmetry() @@ -45,7 +76,32 @@ LPEMirrorSymmetry::~LPEMirrorSymmetry() void LPEMirrorSymmetry::doBeforeEffect (SPLPEItem const* lpeitem) { + using namespace Geom; + SPLPEItem * item = const_cast<SPLPEItem*>(lpeitem); + std::vector<Geom::Path> mline(reflection_line.get_pathvector()); + Point A = mline[0].initialPoint(); + Point B = mline[0].finalPoint(); + Point C = mline[0].pointAt(0.5); + double dist = distance(A,B); + if(mode == MT_X){ + A = Geom::Point(center[X]+(dist/2.0),center[Y]); + B = Geom::Point(center[X]-(dist/2.0),center[Y]); + } + if(mode == MT_Y){ + A = Geom::Point(center[X],center[Y]+(dist/2.0)); + B = Geom::Point(center[X],center[Y]-(dist/2.0)); + } + if( mode == MT_X || mode == MT_Y ){ + Piecewise<D2<SBasis> > rline = Piecewise<D2<SBasis> >(D2<SBasis>(Linear(A[X], B[X]), Linear(A[Y], B[Y]))); + reflection_line.set_new_value(rline, true); + } else { + center.param_setValue(C); + } + lineSeparation.setPoints(A,B); + if(knot_holder){ + knot_holder->update_knots(); + } item->apply_to_clippath(item); item->apply_to_mask(item); } @@ -55,19 +111,22 @@ 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(); + original_bbox(lpeitem); - Point A(bbox.left(), bbox.bottom()); - Point B(bbox.left(), bbox.top()); - A *= t; - B *= t; + Point A(boundingbox_X.max(), boundingbox_Y.max()); + Point B(boundingbox_X.max(), boundingbox_Y.min()); Piecewise<D2<SBasis> > rline = Piecewise<D2<SBasis> >(D2<SBasis>(Linear(A[X], B[X]), Linear(A[Y], B[Y]))); reflection_line.set_new_value(rline, true); } +int +LPEMirrorSymmetry::pointSideOfLine(Geom::Point A, Geom::Point B, Geom::Point X) +{ + //http://stackoverflow.com/questions/1560492/how-to-tell-whether-a-point-is-to-the-right-or-left-side-of-a-line + double pos = (B[Geom::X]-A[Geom::X])*(X[Geom::Y]-A[Geom::Y]) - (B[Geom::Y]-A[Geom::Y])*(X[Geom::X]-A[Geom::X]); + return (pos < 0) ? -1 : (pos > 0); +} + std::vector<Geom::Path> LPEMirrorSymmetry::doEffect_path (std::vector<Geom::Path> const & path_in) { @@ -75,15 +134,20 @@ LPEMirrorSymmetry::doEffect_path (std::vector<Geom::Path> const & path_in) if ( reflection_line.get_pathvector().empty() ) { return path_in; } - + Geom::PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(path_in); std::vector<Geom::Path> path_out; - if (!discard_orig_path) { + Geom::Path mlineExpanded; + Geom::Point lineStart = lineSeparation.pointAt(-100000.0); + Geom::Point lineEnd = lineSeparation.pointAt(100000.0); + mlineExpanded.start( lineStart); + mlineExpanded.appendNew<Geom::LineSegment>( lineEnd); + + if (!discard_orig_path && !fusionPaths) { path_out = path_in; } - std::vector<Geom::Path> mline(reflection_line.get_pathvector()); - Geom::Point A(mline.front().initialPoint()); - Geom::Point B(mline.back().finalPoint()); + Geom::Point A(lineStart); + Geom::Point B(lineEnd); Geom::Affine m1(1.0, 0.0, 0.0, 1.0, A[0], A[1]); double hyp = Geom::distance(A, B); @@ -97,14 +161,146 @@ LPEMirrorSymmetry::doEffect_path (std::vector<Geom::Path> const & path_in) 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(fusionPaths && !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; + } + std::vector<Geom::Path> temp_path; + double timeStart = 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 = (Geom::Path)(*path_it); + if(end_open && path_it->closed()){ + original.close(false); + original.appendNew<Geom::LineSegment>( original.initialPoint() ); + original.close(true); + } + Geom::Crossings cs = crossings(original, mlineExpanded); + for(unsigned int i = 0; i < cs.size(); i++) { + double timeEnd = cs[i].ta; + Geom::Path portion = original.portion(timeStart, timeEnd); + Geom::Point middle = portion.pointAt((double)portion.size()/2.0); + position = pointSideOfLine(lineStart, lineEnd, middle); + if(reverseFusion){ + position *= -1; + } + if(position == -1){ + Geom::Path mirror = portion.reverse() * m; + mirror.setInitial(portion.finalPoint()); + portion.append(mirror); + if(i!=0){ + portion.setFinal(portion.initialPoint()); + portion.close(); + } + temp_path.push_back(portion); + } + portion.clear(); + timeStart = timeEnd; + } + position = pointSideOfLine(lineStart, lineEnd, original.finalPoint()); + if(reverseFusion){ + position *= -1; + } + if(cs.size()!=0 && position == -1){ + Geom::Path portion = original.portion(timeStart, original.size()); + portion = portion.reverse(); + Geom::Path mirror = portion.reverse() * m; + mirror.setInitial(portion.finalPoint()); + portion.append(mirror); + portion = portion.reverse(); + if (!original.closed()){ + temp_path.push_back(portion); + } else { + if(cs.size() >1 ){ + portion.setFinal(temp_path[0].initialPoint()); + portion.setInitial(temp_path[0].finalPoint()); + temp_path[0].append(portion); + } else { + temp_path.push_back(portion); + } + temp_path[0].close(); + } + portion.clear(); + } + if(cs.size() == 0 && position == -1){ + temp_path.push_back(original); + temp_path.push_back(original * m); + } + path_out.insert(path_out.end(), temp_path.begin(), temp_path.end()); + temp_path.clear(); + } + } + + if (!fusionPaths || 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; + + PathVector pathv; + Geom::Path mlineExpanded; + Geom::Point lineStart = lineSeparation.pointAt(-100000.0); + Geom::Point lineEnd = lineSeparation.pointAt(100000.0); + mlineExpanded.start( lineStart); + mlineExpanded.appendNew<Geom::LineSegment>( lineEnd); + pathv.push_back(mlineExpanded); + hp_vec.push_back(pathv); +} + +void +LPEMirrorSymmetry::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) { + { + KnotHolderEntity *e = new MS::KnotHolderEntityCenterMirrorSymmetry(this); + e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, + _("Adjust the center") ); + knotholder->add(e); + } + +}; + +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.param_setValue(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; +} + +} // 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 a4a2b86c0..4a5ea4755 100644 --- a/src/live_effects/lpe-mirror_symmetry.h +++ b/src/live_effects/lpe-mirror_symmetry.h @@ -19,11 +19,25 @@ #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_FREE, + MT_X, + MT_Y, + MT_END +}; + +class LPEMirrorSymmetry : public Effect, GroupBBoxEffect{ public: LPEMirrorSymmetry(LivePathEffectObject *lpeobject); virtual ~LPEMirrorSymmetry(); @@ -32,11 +46,25 @@ public: virtual void doBeforeEffect (SPLPEItem const* lpeitem); + virtual int pointSideOfLine(Geom::Point A, Geom::Point B, Geom::Point X); + virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> 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; + BoolParam fusionPaths; + BoolParam reverseFusion; PathParam reflection_line; + Geom::Line lineSeparation; + PointParam center; LPEMirrorSymmetry(const LPEMirrorSymmetry&); LPEMirrorSymmetry& operator=(const LPEMirrorSymmetry&); |
