summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJabier Arraiza Cenoz <jabier.arraiza@marker.es>2014-11-15 19:07:31 +0000
committerJabiertxof <jtx@jtx.marker.es>2014-11-15 19:07:31 +0000
commit124e1974a8eff0d45063c618266db15cd8b113cb (patch)
tree2e2f05d6d902e9ff3bff930e34d2b60e34131275 /src
parentignore this commit (diff)
parentMacro cleanup of commented out code. (diff)
downloadinkscape-124e1974a8eff0d45063c618266db15cd8b113cb.tar.gz
inkscape-124e1974a8eff0d45063c618266db15cd8b113cb.zip
Add vertical and horizontal modes
(bzr r13682.1.11)
Diffstat (limited to 'src')
-rw-r--r--src/knotholder.cpp6
-rw-r--r--src/live_effects/effect.cpp11
-rw-r--r--src/live_effects/effect.h2
-rw-r--r--src/live_effects/lpe-mirror_symmetry.cpp238
-rw-r--r--src/live_effects/lpe-mirror_symmetry.h30
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( &center);
+
}
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&);