Remove a bunch of unused code.
[synfig.git] / synfig-core / trunk / src / modules / lyr_std / curvewarp.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file curvewarp.cpp
3 **      \brief Implementation of the "Curve Warp" layer
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **      Copyright (c) 2007-2008 Chris Moore
10 **
11 **      This package is free software; you can redistribute it and/or
12 **      modify it under the terms of the GNU General Public License as
13 **      published by the Free Software Foundation; either version 2 of
14 **      the License, or (at your option) any later version.
15 **
16 **      This package is distributed in the hope that it will be useful,
17 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
18 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 **      General Public License for more details.
20 **      \endlegal
21 **
22 ** === N O T E S ===========================================================
23 **
24 ** ========================================================================= */
25
26 /* === H E A D E R S ======================================================= */
27
28 #ifdef USING_PCH
29 #       include "pch.h"
30 #else
31 #ifdef HAVE_CONFIG_H
32 #       include <config.h>
33 #endif
34
35 #include "curvewarp.h"
36
37 #include <synfig/string.h>
38 #include <synfig/time.h>
39 #include <synfig/context.h>
40 #include <synfig/paramdesc.h>
41 #include <synfig/renddesc.h>
42 #include <synfig/surface.h>
43 #include <synfig/value.h>
44 #include <synfig/valuenode.h>
45 #include <ETL/bezier>
46 #include <ETL/hermite>
47 #include <ETL/calculus>
48
49 #endif
50
51 /* === M A C R O S ========================================================= */
52
53 #define FAKE_TANGENT_STEP 0.000001
54
55 /* === G L O B A L S ======================================================= */
56
57 SYNFIG_LAYER_INIT(CurveWarp);
58 SYNFIG_LAYER_SET_NAME(CurveWarp,"curve_warp");
59 SYNFIG_LAYER_SET_LOCAL_NAME(CurveWarp,N_("Curve Warp"));
60 SYNFIG_LAYER_SET_CATEGORY(CurveWarp,N_("Distortions"));
61 SYNFIG_LAYER_SET_VERSION(CurveWarp,"0.0");
62 SYNFIG_LAYER_SET_CVS_ID(CurveWarp,"$Id$");
63
64 /* === P R O C E D U R E S ================================================= */
65
66 inline float calculate_distance(const std::vector<synfig::BLinePoint>& bline)
67 {
68         std::vector<synfig::BLinePoint>::const_iterator iter,next,ret;
69         std::vector<synfig::BLinePoint>::const_iterator end(bline.end());
70
71         float dist(0);
72
73         if (bline.empty()) return dist;
74
75         next=bline.begin();
76         iter=next++;
77
78         for(;next!=end;iter=next++)
79         {
80                 // Setup the curve
81                 etl::hermite<Vector> curve(iter->get_vertex(), next->get_vertex(), iter->get_tangent2(), next->get_tangent1());
82                 dist+=curve.length();
83         }
84
85         return dist;
86 }
87
88 std::vector<synfig::BLinePoint>::const_iterator
89 find_closest_to_bline(bool fast, const std::vector<synfig::BLinePoint>& bline,const Point& p,float& t, float& len, bool& extreme)
90 {
91         std::vector<synfig::BLinePoint>::const_iterator iter,next,ret;
92         std::vector<synfig::BLinePoint>::const_iterator end(bline.end());
93
94         ret=bline.end();
95         float dist(100000000000.0);
96         next=bline.begin();
97         float best_pos(0), best_len(0);
98         etl::hermite<Vector> best_curve;
99         iter=next++;
100         Point bp;
101         float total_len(0);
102         bool first = true, last = false;
103         extreme = false;
104
105         for(;next!=end;iter=next++)
106         {
107                 // Setup the curve
108                 etl::hermite<Vector> curve(iter->get_vertex(), next->get_vertex(), iter->get_tangent2(), next->get_tangent1());
109                 float thisdist(0);
110                 last = false;
111
112                 if (fast)
113                 {
114 #define POINT_CHECK(x) bp=curve(x);     thisdist=(bp-p).mag_squared(); if(thisdist<dist) { extreme = (first&&x<0.01); ret=iter; best_len = total_len; dist=thisdist; best_curve=curve; last=true; best_pos=x;}
115                         POINT_CHECK(0.0001);  POINT_CHECK((1.0/6)); POINT_CHECK((2.0/6)); POINT_CHECK((3.0/6));
116                         POINT_CHECK((4.0/6)); POINT_CHECK((5.0/6)); POINT_CHECK(0.9999);
117                 }
118                 else
119                 {
120                         float pos = curve.find_closest(fast, p);
121                         thisdist=(curve(pos)-p).mag_squared();
122                         if(thisdist<dist)
123                         {
124                                 extreme = (first && pos == 0);
125                                 ret=iter;
126                                 dist=thisdist;
127                                 best_pos = pos;
128                                 best_curve = curve;
129                                 best_len = total_len;
130                                 last = true;
131                         }
132                 }
133                 total_len += curve.length();
134                 first = false;
135         }
136
137         t = best_pos;
138         if (fast)
139         {
140                 len = best_len + best_curve.find_distance(0,best_curve.find_closest(fast, p));
141                 if (last && t > .99) extreme = true;
142         }
143         else
144         {
145                 len = best_len + best_curve.find_distance(0,best_pos);
146                 if (last && t == 1) extreme = true;
147         }
148         return ret;
149 }
150
151 /* === M E T H O D S ======================================================= */
152
153 inline void
154 CurveWarp::sync()
155 {
156         curve_length_=calculate_distance(bline);
157         perp_ = (end_point - start_point).perp().norm();
158 }
159
160 CurveWarp::CurveWarp():
161         origin(0,0),
162         width(1),
163         start_point(-3,-1),
164         end_point(3,1),
165         fast(true)
166 {
167         bline.push_back(BLinePoint());
168         bline.push_back(BLinePoint());
169         bline[0].set_vertex(Point(-2.5,0));
170         bline[1].set_vertex(Point(2.5,0));
171         bline[0].set_tangent(Point(1, 1));
172         bline[1].set_tangent(Point(1, -1));
173         bline[0].set_width(1.0f);
174         bline[1].set_width(1.0f);
175
176         sync();
177 }
178
179 inline Point
180 CurveWarp::transform(const Point &point_, int quality, float supersample)const
181 {
182         Vector tangent;
183         Vector diff;
184         Point p1;
185         Real thickness;
186         Real dist;
187         bool edge_case = false;
188         float len(0);
189         bool extreme;
190         float t;
191
192         if(bline.size()==0)
193                 return Point();
194         else if(bline.size()==1)
195         {
196                 tangent=bline.front().get_tangent1();
197                 p1=bline.front().get_vertex();
198                 thickness=bline.front().get_width();
199                 t = 0.5;
200                 extreme = false;
201         }
202         else
203         {
204                 Point point(point_-origin);
205
206                 std::vector<synfig::BLinePoint>::const_iterator iter,next;
207
208                 // Figure out the BLinePoint we will be using,
209                 next=find_closest_to_bline(fast,bline,point,t,len,extreme);
210
211                 iter=next++;
212                 if(next==bline.end()) next=bline.begin();
213
214                 // Setup the curve
215                 etl::hermite<Vector> curve(iter->get_vertex(), next->get_vertex(), iter->get_tangent2(), next->get_tangent1());
216
217                 // Setup the derivative function
218                 etl::derivative<etl::hermite<Vector> > deriv(curve);
219
220                 int search_iterations(7);
221
222                 if(quality<=6)search_iterations=7;
223                 else if(quality<=7)search_iterations=6;
224                 else if(quality<=8)search_iterations=5;
225                 else search_iterations=4;
226
227                 // Figure out the closest point on the curve
228                 if (fast) t = curve.find_closest(fast, point,search_iterations);
229
230                 // Calculate our values
231                 p1=curve(t);                     // the closest point on the curve
232                 tangent=deriv(t);                // the tangent at that point
233
234                 // if the point we're nearest to is at either end of the
235                 // bline, our distance from the curve is the distance from the
236                 // point on the curve.  we need to know which side of the
237                 // curve we're on, so find the average of the two tangents at
238                 // this point
239                 if (t<0.00001 || t>0.99999)
240                 {
241                         bool zero_tangent = (tangent[0] == 0 && tangent[1] == 0);
242
243                         if (t<0.5)
244                         {
245                                 if (iter->get_split_tangent_flag() || zero_tangent)
246                                 {
247                                         // fake the current tangent if we need to
248                                         if (zero_tangent) tangent = curve(FAKE_TANGENT_STEP) - curve(0);
249
250                                         // calculate the other tangent
251                                         Vector other_tangent(iter->get_tangent1());
252                                         if (other_tangent[0] == 0 && other_tangent[1] == 0)
253                                         {
254                                                 // find the previous blinepoint
255                                                 std::vector<synfig::BLinePoint>::const_iterator prev;
256                                                 if (iter != bline.begin()) (prev = iter)--;
257                                                 else prev = iter;
258
259                                                 etl::hermite<Vector> other_curve(prev->get_vertex(), iter->get_vertex(), prev->get_tangent2(), iter->get_tangent1());
260                                                 other_tangent = other_curve(1) - other_curve(1-FAKE_TANGENT_STEP);
261                                         }
262
263                                         // normalise and sum the two tangents
264                                         tangent=(other_tangent.norm()+tangent.norm());
265                                         edge_case=true;
266                                 }
267                         }
268                         else
269                         {
270                                 if (next->get_split_tangent_flag() || zero_tangent)
271                                 {
272                                         // fake the current tangent if we need to
273                                         if (zero_tangent) tangent = curve(1) - curve(1-FAKE_TANGENT_STEP);
274
275                                         // calculate the other tangent
276                                         Vector other_tangent(next->get_tangent2());
277                                         if (other_tangent[0] == 0 && other_tangent[1] == 0)
278                                         {
279                                                 // find the next blinepoint
280                                                 std::vector<synfig::BLinePoint>::const_iterator next2(next);
281                                                 if (++next2 == bline.end())
282                                                         next2 = next;
283
284                                                 etl::hermite<Vector> other_curve(next->get_vertex(), next2->get_vertex(), next->get_tangent2(), next2->get_tangent1());
285                                                 other_tangent = other_curve(FAKE_TANGENT_STEP) - other_curve(0);
286                                         }
287
288                                         // normalise and sum the two tangents
289                                         tangent=(other_tangent.norm()+tangent.norm());
290                                         edge_case=true;
291                                 }
292                         }
293                 }
294                 tangent = tangent.norm();
295
296                 // the width of the bline at the closest point on the curve
297                 thickness=(next->get_width()-iter->get_width())*t+iter->get_width();
298         }
299
300         if (extreme)
301         {
302                 Vector tangent, perp;
303
304                 if (t < 0.5)
305                 {
306                         synfig::BLinePoint start(bline[0]);
307                         // Point a(start.get_vertex());
308                         tangent = start.get_tangent1().norm();
309                         diff = tangent.perp()*thickness*width;
310                         len = (point_-origin - p1)*tangent;
311                 }
312                 else
313                 {
314                         std::vector<synfig::BLinePoint>::const_iterator iter;
315                         iter=bline.end();
316                         iter--;
317                         tangent = iter->get_tangent2().norm();
318                         diff = tangent.perp()*thickness*width;
319                         len = (point_-origin - p1)*tangent + curve_length_;
320                 }
321         }
322         else if (edge_case)
323         {
324                 diff=(p1-(point_-origin));
325                 if(diff*tangent.perp()<0) diff=-diff;
326                 diff=diff.norm()*thickness*width;
327         }
328         else
329                 diff=tangent.perp()*thickness*width;
330
331                 const Real mag(diff.inv_mag());
332                 supersample=supersample*mag;
333                 diff*=mag*mag;
334                 dist=(point_-origin - p1)*diff;
335
336         len /= curve_length_;
337
338         return (start_point + (end_point - start_point) * len) + perp_ * dist;
339 }
340
341 float
342 CurveWarp::calc_supersample(const synfig::Point &/*x*/, float pw,float /*ph*/)const
343 {
344         return pw;
345 }
346
347 synfig::Layer::Handle
348 CurveWarp::hit_check(synfig::Context context, const synfig::Point &point)const
349 {
350         return context.hit_check(transform(point));
351 }
352
353 bool
354 CurveWarp::set_param(const String & param, const ValueBase &value)
355 {
356         IMPORT(origin);
357         IMPORT(start_point);
358         IMPORT(end_point);
359         IMPORT(fast);
360         IMPORT(width);
361
362         if(param=="bline" && value.get_type()==ValueBase::TYPE_LIST)
363         {
364                 bline=value;
365                 sync();
366
367                 return true;
368         }
369
370         IMPORT_AS(origin,"offset");
371
372         return false;
373 }
374
375 ValueBase
376 CurveWarp::get_param(const String & param)const
377 {
378         EXPORT(origin);
379         EXPORT(start_point);
380         EXPORT(end_point);
381         EXPORT(bline);
382         EXPORT(fast);
383         EXPORT(width);
384
385         EXPORT_NAME();
386         EXPORT_VERSION();
387
388         return ValueBase();
389 }
390
391 Layer::Vocab
392 CurveWarp::get_param_vocab()const
393 {
394         Layer::Vocab ret;
395
396         ret.push_back(ParamDesc("origin")
397                                   .set_local_name(_("Origin")));
398
399         ret.push_back(ParamDesc("width")
400                                   .set_local_name(_("Width")));
401
402         ret.push_back(ParamDesc("start_point")
403                                   .set_local_name(_("Start Point")));
404
405         ret.push_back(ParamDesc("end_point")
406                                   .set_local_name(_("End Point")));
407
408         ret.push_back(ParamDesc("bline")
409                                   .set_local_name(_("Vertices"))
410                                   .set_origin("origin")
411                                   .set_hint("width")
412                                   .set_description(_("A list of BLine Points")));
413
414         ret.push_back(ParamDesc("fast")
415                                   .set_local_name(_("Fast")));
416
417         return ret;
418 }
419
420 Color
421 CurveWarp::get_color(Context context, const Point &point)const
422 {
423         return context.get_color(transform(point));
424 }
425
426 bool
427 CurveWarp::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const
428 {
429         SuperCallback supercb(cb,0,9500,10000);
430
431         if(!context.accelerated_render(surface,quality,renddesc,&supercb))
432                 return false;
433
434         int x,y;
435
436         const Real pw(renddesc.get_pw()),ph(renddesc.get_ph());
437         Point pos;
438         Point tl(renddesc.get_tl());
439         const int w(surface->get_w());
440         const int h(surface->get_h());
441
442         for(y=0,pos[1]=tl[1];y<h;y++,pos[1]+=ph)
443                 for(x=0,pos[0]=tl[0];x<w;x++,pos[0]+=pw)
444                         if (1)
445                                 (*surface)[y][x]=context.get_color(transform(pos));
446                         else
447                                 (*surface)[y][x]=context.get_color(transform(pos));
448
449         // Mark our progress as finished
450         if(cb && !cb->amount_complete(10000,10000))
451                 return false;
452
453         return true;
454 }