From 0cc5b8d2f7b87c4222ee3662071bef1cb1f22b06 Mon Sep 17 00:00:00 2001 From: bgk Date: Sat, 17 May 2008 10:48:43 +0000 Subject: [PATCH] New LPE : Envelope deformation --- src/live_effects/CMakeLists.txt | 2 + src/live_effects/Makefile_insert | 4 +- src/live_effects/effect.cpp | 7 +- src/live_effects/effect.h | 1 + src/live_effects/lpe-envelope.cpp | 260 ++++++++++++++++++++++++++++++ src/live_effects/lpe-envelope.h | 59 +++++++ 6 files changed, 331 insertions(+), 2 deletions(-) create mode 100755 src/live_effects/lpe-envelope.cpp create mode 100755 src/live_effects/lpe-envelope.h diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt index 1906f8a9b..bb37a3c23 100644 --- a/src/live_effects/CMakeLists.txt +++ b/src/live_effects/CMakeLists.txt @@ -10,6 +10,8 @@ lpe-circle_with_radius.cpp lpe-circle_with_radius.h lpe-curvestitch.cpp lpe-curvestitch.h +lpe-envelope.cpp +lpe-envelope.h lpe-gears.cpp lpe-gears.h lpegroupbbox.cpp diff --git a/src/live_effects/Makefile_insert b/src/live_effects/Makefile_insert index 7f0adf3a6..c476948b6 100644 --- a/src/live_effects/Makefile_insert +++ b/src/live_effects/Makefile_insert @@ -42,5 +42,7 @@ live_effects_liblive_effects_a_SOURCES = \ live_effects/lpe-circle_with_radius.cpp \ live_effects/lpe-circle_with_radius.h \ live_effects/lpe-perspective_path.cpp \ - live_effects/lpe-perspective_path.h + live_effects/lpe-perspective_path.h \ + live_effects/lpe-envelope.cpp \ + live_effects/lpe-envelope.h diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index 2d2a8cb22..4f0e97a3b 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -44,6 +44,7 @@ #include "live_effects/lpe-perspective_path.h" #include "live_effects/lpe-spiro.h" #include "live_effects/lpe-constructgrid.h" +#include "live_effects/lpe-envelope.h" #include "nodepath.h" @@ -66,7 +67,8 @@ const Util::EnumData LPETypeData[INVALID_LPE] = { {CIRCLE_WITH_RADIUS, N_("Circle (center+radius)"), "circle_with_radius"}, {PERSPECTIVE_PATH, N_("Perspective path"), "perspective_path"}, {SPIRO, N_("Spiro spline"), "spiro"}, - {CONSTRUCT_GRID, N_("Construct grid"), "construct_grid"}, + {CONSTRUCT_GRID, N_("Construct grid"), "construct_grid"}, + {ENVELOPE, N_("Envelope Deformation"), "envelope"}, }; const Util::EnumDataConverter LPETypeConverter(LPETypeData, INVALID_LPE); @@ -113,6 +115,9 @@ Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj) case CONSTRUCT_GRID: neweffect = static_cast ( new LPEConstructGrid(lpeobj) ); break; + case ENVELOPE: + neweffect = static_cast ( new LPEEnvelope(lpeobj) ); + break; default: g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr); neweffect = NULL; diff --git a/src/live_effects/effect.h b/src/live_effects/effect.h index f4287bb52..328181adc 100644 --- a/src/live_effects/effect.h +++ b/src/live_effects/effect.h @@ -65,6 +65,7 @@ enum EffectType { PERSPECTIVE_PATH, SPIRO, CONSTRUCT_GRID, + ENVELOPE, INVALID_LPE // This must be last }; diff --git a/src/live_effects/lpe-envelope.cpp b/src/live_effects/lpe-envelope.cpp new file mode 100755 index 000000000..db16ae2eb --- /dev/null +++ b/src/live_effects/lpe-envelope.cpp @@ -0,0 +1,260 @@ +#define INKSCAPE_LPE_ENVELOPE_CPP + +/* + * Copyright (C) Steren Giannini 2008 + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "live_effects/lpe-envelope.h" +#include "sp-shape.h" +#include "sp-item.h" +#include "sp-path.h" +#include "sp-item-group.h" +#include "display/curve.h" +#include +#include +#include "libnr/n-art-bpath-2geom.h" +#include "svg/svg.h" +#include "ui/widget/scalar.h" + +#include <2geom/sbasis.h> +#include <2geom/sbasis-geometric.h> +#include <2geom/bezier-to-sbasis.h> +#include <2geom/sbasis-to-bezier.h> +#include <2geom/d2.h> +#include <2geom/piecewise.h> + +#include +using std::vector; + +namespace Inkscape { +namespace LivePathEffect { + +LPEEnvelope::LPEEnvelope(LivePathEffectObject *lpeobject) : + Effect(lpeobject), + bend_path1(_("Top bend path"), _("Top path along which to bend the original path"), "bendpath1", &wr, this, "M0,0 L1,0"), + bend_path2(_("Right bend path"), _("Right path along which to bend the original path"), "bendpath2", &wr, this, "M0,0 L1,0"), + bend_path3(_("Bottom bend path"), _("Bottom path along which to bend the original path"), "bendpath3", &wr, this, "M0,0 L1,0"), + bend_path4(_("Left bend path"), _("Left path along which to bend the original path"), "bendpath4", &wr, this, "M0,0 L1,0"), + xx(_("Enable left & right paths"), _("Enable the left and right deformation paths"), "xx", &wr, this, true), + yy(_("Enable top & bottom paths"), _("Enable the top and bottom deformation paths"), "yy", &wr, this, true) +{ + registerParameter( dynamic_cast(&yy) ); + registerParameter( dynamic_cast(&xx) ); + registerParameter( dynamic_cast(&bend_path1) ); + registerParameter( dynamic_cast(&bend_path2) ); + registerParameter( dynamic_cast(&bend_path3) ); + registerParameter( dynamic_cast(&bend_path4) ); + concatenate_before_pwd2 = true; +} + +LPEEnvelope::~LPEEnvelope() +{ + +} + +void +LPEEnvelope::doBeforeEffect (SPLPEItem *lpeitem) +{ + // get the item bounding box + original_bbox(lpeitem); +} + +Geom::Piecewise > +LPEEnvelope::doEffect_pwd2 (Geom::Piecewise > const & pwd2_in) +{ + + if(xx.get_value() == false && yy.get_value() == false) + { + return pwd2_in; + } + + using namespace Geom; + + /* + The code below is inspired from the Bend Path code developed by jfb and mgsloan + Please, read it before tring to understand this one + */ + + Piecewise > uskeleton1 = arc_length_parametrization(bend_path1.get_pwd2(),2,.1); + uskeleton1 = remove_short_cuts(uskeleton1,.01); + Piecewise > n1 = rot90(derivative(uskeleton1)); + n1 = force_continuity(remove_short_cuts(n1,.1)); + + Piecewise > uskeleton2 = arc_length_parametrization(bend_path2.get_pwd2(),2,.1); + uskeleton2 = remove_short_cuts(uskeleton2,.01); + Piecewise > n2 = rot90(derivative(uskeleton2)); + n2 = force_continuity(remove_short_cuts(n2,.1)); + + Piecewise > uskeleton3 = arc_length_parametrization(bend_path3.get_pwd2(),2,.1); + uskeleton3 = remove_short_cuts(uskeleton3,.01); + Piecewise > n3 = rot90(derivative(uskeleton3)); + n3 = force_continuity(remove_short_cuts(n3,.1)); + + Piecewise > uskeleton4 = arc_length_parametrization(bend_path4.get_pwd2(),2,.1); + uskeleton4 = remove_short_cuts(uskeleton4,.01); + Piecewise > n4 = rot90(derivative(uskeleton4)); + n4 = force_continuity(remove_short_cuts(n4,.1)); + + + D2 > patternd2 = make_cuts_independant(pwd2_in); + Piecewise x = Piecewise(patternd2[0]); + Piecewise y = Piecewise(patternd2[1]); + + /*The *1.001 is a hack to avoid a small bug : path at x=0 and y=0 don't work well. */ + x-= boundingbox_X.min()*1.001; + y-= boundingbox_Y.min()*1.001; + + Piecewise x1 = x ; + Piecewise y1 = y ; + + Piecewise x2 = x ; + Piecewise y2 = y ; + x2 -= boundingbox_X.extent(); + + Piecewise x3 = x ; + Piecewise y3 = y ; + y3 -= boundingbox_Y.extent(); + + Piecewise x4 = x ; + Piecewise y4 = y ; + + + /*Scaling to the Bend Path length*/ + double scaling1 = uskeleton1.cuts.back()/boundingbox_X.extent(); + if (scaling1 != 1.0) { + x1*=scaling1; + } + + double scaling2 = uskeleton2.cuts.back()/boundingbox_Y.extent(); + if (scaling2 != 1.0) { + y2*=scaling2; + } + + double scaling3 = uskeleton3.cuts.back()/boundingbox_X.extent(); + if (scaling3 != 1.0) { + x3*=scaling3; + } + + double scaling4 = uskeleton4.cuts.back()/boundingbox_Y.extent(); + if (scaling4 != 1.0) { + y4*=scaling4; + } + + + + Piecewise xbis = x; + Piecewise ybis = y; + xbis *= -1.0; + xbis += boundingbox_X.extent(); + ybis *= -1.0; + ybis += boundingbox_Y.extent(); + /* This is important : y + ybis = constant and x +xbis = constant */ + + Piecewise > output; + Piecewise > output1; + Piecewise > output2; + Piecewise > output_x; + Piecewise > output_y; + + /* + output_y : Deformation by Up and Down Bend Paths + We use weighting : The closer a point is to a Band Path, the more it will be affected by this Bend Path. + This is done by the line "ybis*Derformation1 + y*Deformation2" + The result is a mix between the 2 deformed paths + */ + output_y = ybis*(compose((uskeleton1),x1) + y1*compose(n1,x1) ) + + y*(compose((uskeleton3),x3) + y3*compose(n3,x3) ); + output_y /= (boundingbox_Y.extent()); + if(xx.get_value() == false && yy.get_value() == true) + { + return output_y; + } + + /*output_x : Deformation by Left and Right Bend Paths*/ + output_x = x*(compose((uskeleton2),y2) + -x2*compose(n2,y2) ) + + xbis*(compose((uskeleton4),y4) + -x4*compose(n4,y4) ); + output_x /= (boundingbox_X.extent()); + if(xx.get_value() == true && yy.get_value() == false) + { + return output_x; + } + + /*output : Deformation by Up, Left, Right and Down Bend Paths*/ + if(xx.get_value() == true && yy.get_value() == true) + { + Piecewise xsqr = x*xbis; /* xsqr = x * (BBox_X - x) */ + Piecewise ysqr = y*ybis; /* xsqr = y * (BBox_Y - y) */ + Piecewise xsqrbis = xsqr; + Piecewise ysqrbis = ysqr; + xsqrbis *= -1; + xsqrbis += boundingbox_X.extent()*boundingbox_X.extent()/4.; + ysqrbis *= -1; + ysqrbis += boundingbox_Y.extent()*boundingbox_Y.extent()/4.; + /*This is important : xsqr + xsqrbis = constant*/ + + + /* + Here we mix the last two results : output_x and output_y + output1 : The more a point is close to Up and Down, the less it will be affected by output_x. + (This is done with the polynomial function) + output2 : The more a point is close to Left and Right, the less it will be affected by output_y. + output : we do the mean between output1 and output2 for all points. + */ + output1 = (ysqrbis*output_y) + (ysqr*output_x); + output1 /= (boundingbox_Y.extent()*boundingbox_Y.extent()/4.); + + output2 = (xsqrbis*output_x) + (xsqr*output_y); + output2 /= (boundingbox_X.extent()*boundingbox_X.extent()/4.); + + output = output1 + output2; + output /= 2.; + + return output; + /*Of course, the result is not perfect, but on a graphical point of view, this is sufficent.*/ + + } +} + +void +LPEEnvelope::resetDefaults(SPItem * item) +{ + original_bbox(SP_LPE_ITEM(item)); + + Geom::Point Up_Left(boundingbox_X.min(), boundingbox_Y.min()); + Geom::Point Up_Right(boundingbox_X.max(), boundingbox_Y.min()); + Geom::Point Down_Left(boundingbox_X.min(), boundingbox_Y.max()); + Geom::Point Down_Right(boundingbox_X.max(), boundingbox_Y.max()); + + Geom::Path path1; + path1.start( Up_Left ); + path1.appendNew( Up_Right ); + bend_path1.param_set_and_write_new_value( path1.toPwSb() ); + + Geom::Path path2; + path2.start( Up_Right ); + path2.appendNew( Down_Right ); + bend_path2.param_set_and_write_new_value( path2.toPwSb() ); + + Geom::Path path3; + path3.start( Down_Left ); + path3.appendNew( Down_Right ); + bend_path3.param_set_and_write_new_value( path3.toPwSb() ); + + Geom::Path path4; + path4.start( Up_Left ); + path4.appendNew( Down_Left ); + bend_path4.param_set_and_write_new_value( path4.toPwSb() ); +} + +void +LPEEnvelope::transform_multiply(Geom::Matrix const& postmul, bool set) +{ + // TODO: implement correct transformation instead of this default behavior + Effect::transform_multiply(postmul, set); +} + + +} // namespace LivePathEffect +} /* namespace Inkscape */ diff --git a/src/live_effects/lpe-envelope.h b/src/live_effects/lpe-envelope.h new file mode 100755 index 000000000..282169674 --- /dev/null +++ b/src/live_effects/lpe-envelope.h @@ -0,0 +1,59 @@ +#ifndef INKSCAPE_LPE_ENVELOPE_H +#define INKSCAPE_LPE_ENVELOPE_H + +/* + * Inkscape::LPEEnvelope + * + * Copyright (C) Steren Giannini 2008 + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "live_effects/effect.h" +#include "live_effects/parameter/path.h" +#include "live_effects/parameter/enum.h" +#include "live_effects/parameter/bool.h" + +#include <2geom/sbasis.h> +#include <2geom/sbasis-geometric.h> +#include <2geom/bezier-to-sbasis.h> +#include <2geom/sbasis-to-bezier.h> +#include <2geom/d2.h> +#include <2geom/piecewise.h> + +#include "live_effects/lpegroupbbox.h" + +namespace Inkscape { +namespace LivePathEffect { + +class LPEEnvelope : public Effect, GroupBBoxEffect { +public: + LPEEnvelope(LivePathEffectObject *lpeobject); + virtual ~LPEEnvelope(); + + virtual void doBeforeEffect (SPLPEItem *lpeitem); + + virtual Geom::Piecewise > doEffect_pwd2 (Geom::Piecewise > const & pwd2_in); + + virtual void resetDefaults(SPItem * item); + + virtual void transform_multiply(Geom::Matrix const& postmul, bool set); + +private: + PathParam bend_path1; + PathParam bend_path2; + PathParam bend_path3; + PathParam bend_path4; + BoolParam xx; + BoolParam yy; + + void on_pattern_pasted(); + + LPEEnvelope(const LPEEnvelope&); + LPEEnvelope& operator=(const LPEEnvelope&); +}; + +}; //namespace LivePathEffect +}; //namespace Inkscape + +#endif -- 2.30.2