abd975b4ebe61023f2cfcb13d6fe192996b61b8a
1 #define INKSCAPE_LPE_ENVELOPE_CPP
3 /*
4 * Copyright (C) Steren Giannini 2008 <steren.giannini@gmail.com>
5 *
6 * Released under GNU GPL, read the file 'COPYING' for more information
7 */
9 #include "live_effects/lpe-envelope.h"
10 #include "sp-shape.h"
11 #include "sp-item.h"
12 #include "sp-path.h"
13 #include "sp-item-group.h"
14 #include "display/curve.h"
15 #include "svg/svg.h"
16 #include "ui/widget/scalar.h"
18 #include <2geom/sbasis.h>
19 #include <2geom/sbasis-geometric.h>
20 #include <2geom/bezier-to-sbasis.h>
21 #include <2geom/sbasis-to-bezier.h>
22 #include <2geom/d2.h>
23 #include <2geom/piecewise.h>
25 #include <algorithm>
26 using std::vector;
28 namespace Inkscape {
29 namespace LivePathEffect {
31 LPEEnvelope::LPEEnvelope(LivePathEffectObject *lpeobject) :
32 Effect(lpeobject),
33 bend_path1(_("Top bend path"), _("Top path along which to bend the original path"), "bendpath1", &wr, this, "M0,0 L1,0"),
34 bend_path2(_("Right bend path"), _("Right path along which to bend the original path"), "bendpath2", &wr, this, "M0,0 L1,0"),
35 bend_path3(_("Bottom bend path"), _("Bottom path along which to bend the original path"), "bendpath3", &wr, this, "M0,0 L1,0"),
36 bend_path4(_("Left bend path"), _("Left path along which to bend the original path"), "bendpath4", &wr, this, "M0,0 L1,0"),
37 xx(_("Enable left & right paths"), _("Enable the left and right deformation paths"), "xx", &wr, this, true),
38 yy(_("Enable top & bottom paths"), _("Enable the top and bottom deformation paths"), "yy", &wr, this, true)
39 {
40 registerParameter( dynamic_cast<Parameter *>(&yy) );
41 registerParameter( dynamic_cast<Parameter *>(&xx) );
42 registerParameter( dynamic_cast<Parameter *>(&bend_path1) );
43 registerParameter( dynamic_cast<Parameter *>(&bend_path2) );
44 registerParameter( dynamic_cast<Parameter *>(&bend_path3) );
45 registerParameter( dynamic_cast<Parameter *>(&bend_path4) );
46 concatenate_before_pwd2 = true;
47 }
49 LPEEnvelope::~LPEEnvelope()
50 {
52 }
54 void
55 LPEEnvelope::doBeforeEffect (SPLPEItem *lpeitem)
56 {
57 // get the item bounding box
58 original_bbox(lpeitem);
59 }
61 Geom::Piecewise<Geom::D2<Geom::SBasis> >
62 LPEEnvelope::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
63 {
65 if(xx.get_value() == false && yy.get_value() == false)
66 {
67 return pwd2_in;
68 }
70 using namespace Geom;
72 // Don't allow empty path parameters:
73 if ( bend_path1.get_pathvector().empty()
74 || bend_path2.get_pathvector().empty()
75 || bend_path3.get_pathvector().empty()
76 || bend_path4.get_pathvector().empty() )
77 {
78 return pwd2_in;
79 }
81 /*
82 The code below is inspired from the Bend Path code developed by jfb and mgsloan
83 Please, read it before tring to understand this one
84 */
86 Piecewise<D2<SBasis> > uskeleton1 = arc_length_parametrization(bend_path1.get_pwd2(),2,.1);
87 uskeleton1 = remove_short_cuts(uskeleton1,.01);
88 Piecewise<D2<SBasis> > n1 = rot90(derivative(uskeleton1));
89 n1 = force_continuity(remove_short_cuts(n1,.1));
91 Piecewise<D2<SBasis> > uskeleton2 = arc_length_parametrization(bend_path2.get_pwd2(),2,.1);
92 uskeleton2 = remove_short_cuts(uskeleton2,.01);
93 Piecewise<D2<SBasis> > n2 = rot90(derivative(uskeleton2));
94 n2 = force_continuity(remove_short_cuts(n2,.1));
96 Piecewise<D2<SBasis> > uskeleton3 = arc_length_parametrization(bend_path3.get_pwd2(),2,.1);
97 uskeleton3 = remove_short_cuts(uskeleton3,.01);
98 Piecewise<D2<SBasis> > n3 = rot90(derivative(uskeleton3));
99 n3 = force_continuity(remove_short_cuts(n3,.1));
101 Piecewise<D2<SBasis> > uskeleton4 = arc_length_parametrization(bend_path4.get_pwd2(),2,.1);
102 uskeleton4 = remove_short_cuts(uskeleton4,.01);
103 Piecewise<D2<SBasis> > n4 = rot90(derivative(uskeleton4));
104 n4 = force_continuity(remove_short_cuts(n4,.1));
107 D2<Piecewise<SBasis> > patternd2 = make_cuts_independent(pwd2_in);
108 Piecewise<SBasis> x = Piecewise<SBasis>(patternd2[0]);
109 Piecewise<SBasis> y = Piecewise<SBasis>(patternd2[1]);
111 /*The *1.001 is a hack to avoid a small bug : path at x=0 and y=0 don't work well. */
112 x-= boundingbox_X.min()*1.001;
113 y-= boundingbox_Y.min()*1.001;
115 Piecewise<SBasis> x1 = x ;
116 Piecewise<SBasis> y1 = y ;
118 Piecewise<SBasis> x2 = x ;
119 Piecewise<SBasis> y2 = y ;
120 x2 -= boundingbox_X.extent();
122 Piecewise<SBasis> x3 = x ;
123 Piecewise<SBasis> y3 = y ;
124 y3 -= boundingbox_Y.extent();
126 Piecewise<SBasis> x4 = x ;
127 Piecewise<SBasis> y4 = y ;
130 /*Scaling to the Bend Path length*/
131 double scaling1 = uskeleton1.cuts.back()/boundingbox_X.extent();
132 if (scaling1 != 1.0) {
133 x1*=scaling1;
134 }
136 double scaling2 = uskeleton2.cuts.back()/boundingbox_Y.extent();
137 if (scaling2 != 1.0) {
138 y2*=scaling2;
139 }
141 double scaling3 = uskeleton3.cuts.back()/boundingbox_X.extent();
142 if (scaling3 != 1.0) {
143 x3*=scaling3;
144 }
146 double scaling4 = uskeleton4.cuts.back()/boundingbox_Y.extent();
147 if (scaling4 != 1.0) {
148 y4*=scaling4;
149 }
153 Piecewise<SBasis> xbis = x;
154 Piecewise<SBasis> ybis = y;
155 xbis *= -1.0;
156 xbis += boundingbox_X.extent();
157 ybis *= -1.0;
158 ybis += boundingbox_Y.extent();
159 /* This is important : y + ybis = constant and x +xbis = constant */
161 Piecewise<D2<SBasis> > output;
162 Piecewise<D2<SBasis> > output1;
163 Piecewise<D2<SBasis> > output2;
164 Piecewise<D2<SBasis> > output_x;
165 Piecewise<D2<SBasis> > output_y;
167 /*
168 output_y : Deformation by Up and Down Bend Paths
169 We use weighting : The closer a point is to a Band Path, the more it will be affected by this Bend Path.
170 This is done by the line "ybis*Derformation1 + y*Deformation2"
171 The result is a mix between the 2 deformed paths
172 */
173 output_y = ybis*(compose((uskeleton1),x1) + y1*compose(n1,x1) )
174 + y*(compose((uskeleton3),x3) + y3*compose(n3,x3) );
175 output_y /= (boundingbox_Y.extent());
176 if(xx.get_value() == false && yy.get_value() == true)
177 {
178 return output_y;
179 }
181 /*output_x : Deformation by Left and Right Bend Paths*/
182 output_x = x*(compose((uskeleton2),y2) + -x2*compose(n2,y2) )
183 + xbis*(compose((uskeleton4),y4) + -x4*compose(n4,y4) );
184 output_x /= (boundingbox_X.extent());
185 if(xx.get_value() == true && yy.get_value() == false)
186 {
187 return output_x;
188 }
190 /*output : Deformation by Up, Left, Right and Down Bend Paths*/
191 if(xx.get_value() == true && yy.get_value() == true)
192 {
193 Piecewise<SBasis> xsqr = x*xbis; /* xsqr = x * (BBox_X - x) */
194 Piecewise<SBasis> ysqr = y*ybis; /* xsqr = y * (BBox_Y - y) */
195 Piecewise<SBasis> xsqrbis = xsqr;
196 Piecewise<SBasis> ysqrbis = ysqr;
197 xsqrbis *= -1;
198 xsqrbis += boundingbox_X.extent()*boundingbox_X.extent()/4.;
199 ysqrbis *= -1;
200 ysqrbis += boundingbox_Y.extent()*boundingbox_Y.extent()/4.;
201 /*This is important : xsqr + xsqrbis = constant*/
204 /*
205 Here we mix the last two results : output_x and output_y
206 output1 : The more a point is close to Up and Down, the less it will be affected by output_x.
207 (This is done with the polynomial function)
208 output2 : The more a point is close to Left and Right, the less it will be affected by output_y.
209 output : we do the mean between output1 and output2 for all points.
210 */
211 output1 = (ysqrbis*output_y) + (ysqr*output_x);
212 output1 /= (boundingbox_Y.extent()*boundingbox_Y.extent()/4.);
214 output2 = (xsqrbis*output_x) + (xsqr*output_y);
215 output2 /= (boundingbox_X.extent()*boundingbox_X.extent()/4.);
217 output = output1 + output2;
218 output /= 2.;
220 return output;
221 /*Of course, the result is not perfect, but on a graphical point of view, this is sufficent.*/
223 }
225 // do nothing when xx and yy are both false
226 return pwd2_in;
227 }
229 void
230 LPEEnvelope::resetDefaults(SPItem * item)
231 {
232 Effect::resetDefaults(item);
234 original_bbox(SP_LPE_ITEM(item));
236 Geom::Point Up_Left(boundingbox_X.min(), boundingbox_Y.min());
237 Geom::Point Up_Right(boundingbox_X.max(), boundingbox_Y.min());
238 Geom::Point Down_Left(boundingbox_X.min(), boundingbox_Y.max());
239 Geom::Point Down_Right(boundingbox_X.max(), boundingbox_Y.max());
241 Geom::Path path1;
242 path1.start( Up_Left );
243 path1.appendNew<Geom::LineSegment>( Up_Right );
244 bend_path1.set_new_value( path1.toPwSb(), true );
246 Geom::Path path2;
247 path2.start( Up_Right );
248 path2.appendNew<Geom::LineSegment>( Down_Right );
249 bend_path2.set_new_value( path2.toPwSb(), true );
251 Geom::Path path3;
252 path3.start( Down_Left );
253 path3.appendNew<Geom::LineSegment>( Down_Right );
254 bend_path3.set_new_value( path3.toPwSb(), true );
256 Geom::Path path4;
257 path4.start( Up_Left );
258 path4.appendNew<Geom::LineSegment>( Down_Left );
259 bend_path4.set_new_value( path4.toPwSb(), true );
260 }
263 } // namespace LivePathEffect
264 } /* namespace Inkscape */