Fix bugs in previous commit that caused FTBFS in synfig and ETL FTBFS with older...
[synfig.git] / synfig-core / tags / synfig_0_61_04 / synfig-core / src / modules / mod_gradient / curvegradient.cpp
1 /*! ========================================================================
2 ** Synfig
3 ** Template File
4 ** $Id: curvegradient.cpp,v 1.2 2005/01/13 06:48:39 darco Exp $
5 **
6 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
7 **
8 **      This package is free software; you can redistribute it and/or
9 **      modify it under the terms of the GNU General Public License as
10 **      published by the Free Software Foundation; either version 2 of
11 **      the License, or (at your option) any later version.
12 **
13 **      This package is distributed in the hope that it will be useful,
14 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
15 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 **      General Public License for more details.
17 **
18 ** === N O T E S ===========================================================
19 **
20 ** ========================================================================= */
21
22 /* === H E A D E R S ======================================================= */
23
24 #ifdef USING_PCH
25 #       include "pch.h"
26 #else
27 #ifdef HAVE_CONFIG_H
28 #       include <config.h>
29 #endif
30
31 #include "curvegradient.h"
32
33 #include <synfig/string.h>
34 #include <synfig/time.h>
35 #include <synfig/context.h>
36 #include <synfig/paramdesc.h>
37 #include <synfig/renddesc.h>
38 #include <synfig/surface.h>
39 #include <synfig/value.h>
40 #include <synfig/valuenode.h>
41 #include <ETL/bezier>
42 #include <ETL/hermite>
43 #include <ETL/calculus>
44
45 #endif
46
47 /* === M A C R O S ========================================================= */
48
49 /* === G L O B A L S ======================================================= */
50
51 SYNFIG_LAYER_INIT(CurveGradient);
52 SYNFIG_LAYER_SET_NAME(CurveGradient,"curve_gradient");
53 SYNFIG_LAYER_SET_LOCAL_NAME(CurveGradient,_("Curve Gradient"));
54 SYNFIG_LAYER_SET_CATEGORY(CurveGradient,_("Gradients"));
55 SYNFIG_LAYER_SET_VERSION(CurveGradient,"0.0");
56 SYNFIG_LAYER_SET_CVS_ID(CurveGradient,"$Id: curvegradient.cpp,v 1.2 2005/01/13 06:48:39 darco Exp $");
57
58 /* === P R O C E D U R E S ================================================= */
59
60 inline float calculate_distance(const synfig::BLinePoint& a,const synfig::BLinePoint& b)
61 {
62 #if 1
63         const Point& c1(a.get_vertex());
64         const Point c2(a.get_vertex()+a.get_tangent2()/3);
65         const Point c3(b.get_vertex()-b.get_tangent1()/3);
66         const Point& c4(b.get_vertex());
67         return (c1-c2).mag()+(c2-c3).mag()+(c3-c4).mag();
68 #else
69 #endif
70 }
71
72 inline float calculate_distance(const std::vector<synfig::BLinePoint>& bline)
73 {
74         std::vector<synfig::BLinePoint>::const_iterator iter,next,ret;
75         std::vector<synfig::BLinePoint>::const_iterator end(bline.end());
76         
77         float dist(0);
78         
79         next=bline.begin();
80         
81         //if(loop)
82         //      iter=--bline.end();
83         //else
84                 iter=next++;
85         
86         for(;next!=end;iter=next++)
87         {
88                 // Setup the curve
89                 etl::hermite<Vector> curve(
90                         iter->get_vertex(),
91                         next->get_vertex(),
92                         iter->get_tangent2(),
93                         next->get_tangent1()
94                 );
95
96 //              dist+=calculate_distance(*iter,*next);
97                 dist+=curve.length();
98         }
99
100         return dist;
101 }
102
103 std::vector<synfig::BLinePoint>::const_iterator
104 find_closest(const std::vector<synfig::BLinePoint>& bline,const Point& p,bool loop=false,float *bline_dist_ret=0)
105 {
106         std::vector<synfig::BLinePoint>::const_iterator iter,next,ret;
107         std::vector<synfig::BLinePoint>::const_iterator end(bline.end());
108         
109         ret=bline.end();
110         float dist(100000000000.0);
111         
112         next=bline.begin();
113
114         float best_bline_dist(0);
115         float best_bline_len(0);
116         float total_bline_dist(0);
117         etl::hermite<Vector> best_curve;
118         
119         if(loop)
120                 iter=--bline.end();
121         else
122                 iter=next++;
123
124         Point bp;
125         
126         for(;next!=end;iter=next++)
127         {
128                 // Setup the curve
129                 etl::hermite<Vector> curve(
130                         iter->get_vertex(),
131                         next->get_vertex(),
132                         iter->get_tangent2(),
133                         next->get_tangent1()
134                 );
135
136                 /*
137                 const float t(curve.find_closest(p,6,0.01,0.99));
138                 bp=curve(t);if((bp-p).mag_squared()<dist) { ret=iter; dist=(bp-p).mag_squared(); ret_t=t; }             
139                 */
140
141                 float thisdist(0);
142                 float len(0);
143                 if(bline_dist_ret)
144                 {
145                         //len=calculate_distance(*iter,*next);
146                         len=curve.length();
147                 }
148                 
149 #define POINT_CHECK(x) bp=curve(x);     thisdist=(bp-p).mag_squared(); if(thisdist<dist) { ret=iter; dist=thisdist; best_bline_dist=total_bline_dist; best_bline_len=len; best_curve=curve; }
150
151                 POINT_CHECK(0.0001);
152                 POINT_CHECK((1.0/6.0));
153                 POINT_CHECK((2.0/6.0));
154                 POINT_CHECK((3.0/6.0));
155                 POINT_CHECK((4.0/6.0));
156                 POINT_CHECK((5.0/6.0));
157                 POINT_CHECK(0.9999);
158
159                 total_bline_dist+=len;
160         }
161
162         if(bline_dist_ret)
163         {
164                 *bline_dist_ret=best_bline_dist+best_curve.find_distance(0,best_curve.find_closest(p));
165 //              *bline_dist_ret=best_bline_dist+best_curve.find_closest(p)*best_bline_len;
166         }
167         
168         return ret;
169 }
170
171 /* === M E T H O D S ======================================================= */
172
173 inline void
174 CurveGradient::sync()
175 {
176         diff=(p2-p1);
177         const Real mag(diff.inv_mag());
178         diff*=mag*mag;
179
180         curve_length_=calculate_distance(bline);
181 }
182
183
184 CurveGradient::CurveGradient():
185         p1(1,1),
186         p2(-1,-1),
187         offset(0,0),
188         width(0.25),
189         gradient(Color::black(), Color::white()),
190         loop(false),
191         zigzag(false),
192         perpendicular(false)
193 {
194         bline.push_back(BLinePoint());
195         bline.push_back(BLinePoint());
196         bline.push_back(BLinePoint());
197         bline[0].set_vertex(Point(0,1));        
198         bline[1].set_vertex(Point(0,-1));       
199         bline[2].set_vertex(Point(1,0));
200         bline[0].set_tangent(bline[1].get_vertex()-bline[2].get_vertex()*0.5f); 
201         bline[1].set_tangent(bline[2].get_vertex()-bline[0].get_vertex()*0.5f); 
202         bline[2].set_tangent(bline[0].get_vertex()-bline[1].get_vertex()*0.5f); 
203         bline[0].set_width(1.0f);       
204         bline[1].set_width(1.0f);       
205         bline[2].set_width(1.0f);       
206         bline_loop=true;
207         
208         sync();
209 }
210
211 inline Color
212 CurveGradient::color_func(const Point &point_, int quality, float supersample)const
213 {
214         Vector tangent;
215         Vector diff;
216         Point p1;
217         Real thickness;
218         Real dist;
219
220         float perp_dist;
221         
222         if(bline.size()==0)
223                 return Color::alpha();
224         else if(bline.size()==1)
225         {
226                 tangent=bline.front().get_tangent1();
227                 p1=bline.front().get_vertex();
228                 thickness=bline.front().get_width();
229         }
230         else
231         {
232                 Point point(point_-offset);
233                 
234                 std::vector<synfig::BLinePoint>::const_iterator iter,next;
235                 
236                 // Figure out the BLinePoints we will be using,
237                 // Taking into account looping.
238                 if(perpendicular)
239                 {
240                         next=find_closest(bline,point,bline_loop,&perp_dist);
241                         perp_dist/=curve_length_;
242                 }
243                 else
244                 {
245                         next=find_closest(bline,point,bline_loop);
246                 }
247                         iter=next++;
248                         if(next==bline.end()) next=bline.begin();
249                         
250                         // Setup the curve
251                         etl::hermite<Vector> curve(
252                                 iter->get_vertex(),
253                                 next->get_vertex(),
254                                 iter->get_tangent2(),
255                                 next->get_tangent1()
256                         );
257                         
258                         // Setup the derivative function
259                         etl::derivative<etl::hermite<Vector> > deriv(curve);
260         
261                         int search_iterations(7);
262                         
263                         /*if(quality==0)search_iterations=8;
264                         else if(quality<=2)search_iterations=10;
265                         else if(quality<=4)search_iterations=8;
266                         */
267                         if(!perpendicular)
268                         {
269                                 if(quality<=6)search_iterations=7;
270                                 else if(quality<=7)search_iterations=6;
271                                 else if(quality<=8)search_iterations=5;
272                                 else search_iterations=4;
273                         }
274                         else
275                         {
276                                 if(quality>7)
277                                         search_iterations=4;
278                         }
279                         
280                         // Figure out the closest point on the curve
281                         const float t(curve.find_closest(point,search_iterations));
282                                 
283                         
284                         // Calculate our values
285                         p1=curve(t);
286                         tangent=deriv(t).norm();
287                         
288                         if(perpendicular)
289                         {
290                                 tangent*=curve_length_;
291                                 p1-=tangent*perp_dist;
292                                 tangent=-tangent.perp();
293                         }
294                         else
295                         {
296                                 thickness=(next->get_width()-iter->get_width())*t+iter->get_width();
297                         }
298                 //}             
299         }
300         
301
302         if(!perpendicular)
303         {
304                         diff=tangent.perp()*thickness*width;
305                         p1-=diff*0.5;
306                         const Real mag(diff.inv_mag());
307                         supersample=supersample*mag;
308                         diff*=mag*mag;
309                         dist=((point_-offset)*diff-p1*diff);
310         }
311         else
312         {
313                 if(quality>7)
314                 {
315                         dist=perp_dist;
316 /*                      diff=tangent.perp();
317                         const Real mag(diff.inv_mag());
318                         supersample=supersample*mag;
319 */
320                         supersample=0;
321                 }
322                 else
323                 {
324                 diff=tangent.perp();
325                 //p1-=diff*0.5;
326                 const Real mag(diff.inv_mag());
327                 supersample=supersample*mag;
328                 diff*=mag*mag;
329                 dist=((point_-offset)*diff-p1*diff);
330                 }
331         }
332         
333         if(loop)
334                 dist-=floor(dist);
335         
336         if(zigzag)
337         {
338                 dist*=2.0;
339                 supersample*=2.0;
340                 if(dist>1)dist=2.0-dist;
341         }
342
343         if(loop)
344         {
345                 if(dist+supersample*0.5>1.0)
346                 {
347                         Color pool(gradient(dist,supersample*0.5).premult_alpha()*(1.0-(dist-supersample*0.5)));
348                         pool+=gradient((dist+supersample*0.5)-1.0,supersample*0.5).premult_alpha()*((dist+supersample*0.5)-1.0);
349                         return pool.demult_alpha();
350                 }
351                 if(dist-supersample*0.5<0.0)
352                 {
353                         Color pool(gradient(dist,supersample*0.5).premult_alpha()*(dist+supersample*0.5));
354                         pool+=gradient(1.0-(dist-supersample*0.5),supersample*0.5).premult_alpha()*(-(dist-supersample*0.5));
355                         return pool.demult_alpha();
356                 }
357         }
358         return gradient(dist,supersample);
359 }
360
361 float
362 CurveGradient::calc_supersample(const synfig::Point &x, float pw,float ph)const
363 {
364 //      return pw/(p2-p1).mag();
365         return pw;
366 }
367
368 synfig::Layer::Handle
369 CurveGradient::hit_check(synfig::Context context, const synfig::Point &point)const
370 {
371         if(get_blend_method()==Color::BLEND_STRAIGHT && get_amount()>=0.5)
372                 return const_cast<CurveGradient*>(this);
373         if(get_amount()==0.0)
374                 return context.hit_check(point);
375         if((get_blend_method()==Color::BLEND_STRAIGHT || get_blend_method()==Color::BLEND_COMPOSITE|| get_blend_method()==Color::BLEND_ONTO) && color_func(point).get_a()>0.5)
376                 return const_cast<CurveGradient*>(this);
377         return context.hit_check(point);
378 }
379
380 bool
381 CurveGradient::set_param(const String & param, const ValueBase &value)
382 {
383         if(param=="p1" && value.same_as(p1))
384         {
385                 p1=value.get(p1);
386                 sync();
387                 return true;
388         }
389         if(param=="p2" && value.same_as(p2))
390         {
391                 p2=value.get(p2);
392                 sync();
393                 return true;
394         }
395         //IMPORT(p1);
396         //IMPORT(p2);
397         
398         
399         IMPORT(offset);
400         IMPORT(perpendicular);
401
402         if(param=="bline" && value.get_type()==ValueBase::TYPE_LIST)
403         {
404                 bline=value;
405                 bline_loop=value.get_loop();
406                 sync();
407
408                 return true;
409         }
410
411         IMPORT(width);
412         IMPORT(gradient);
413         IMPORT(loop);
414         IMPORT(zigzag);
415         return Layer_Composite::set_param(param,value); 
416 }
417
418 ValueBase
419 CurveGradient::get_param(const String & param)const
420 {
421         EXPORT(offset);
422         EXPORT(bline);
423         EXPORT(p1);
424         EXPORT(p2);
425         EXPORT(gradient);
426         EXPORT(loop);
427         EXPORT(zigzag);
428         EXPORT(width);
429         EXPORT(perpendicular);
430         
431         EXPORT_NAME();
432         EXPORT_VERSION();
433                 
434         return Layer_Composite::get_param(param);       
435 }
436
437 Layer::Vocab
438 CurveGradient::get_param_vocab()const
439 {
440         Layer::Vocab ret(Layer_Composite::get_param_vocab());
441
442                 ret.push_back(ParamDesc("offset")
443                 .set_local_name(_("Offset"))
444         );
445
446         ret.push_back(ParamDesc("width")
447                 .set_is_distance()
448                 .set_local_name(_("Width"))
449         );
450
451         ret.push_back(ParamDesc("bline")
452                 .set_local_name(_("Vertices"))
453                 .set_origin("offset")
454                 .set_scalar("width")
455                 .set_description(_("A list of BLine Points"))
456         );
457
458
459         ret.push_back(ParamDesc("gradient")
460                 .set_local_name(_("Gradient"))
461         );
462         ret.push_back(ParamDesc("loop")
463                 .set_local_name(_("Loop"))
464         );
465         ret.push_back(ParamDesc("zigzag")
466                 .set_local_name(_("ZigZag"))
467         );
468         ret.push_back(ParamDesc("perpendicular")
469                 .set_local_name(_("Perpendicular"))
470         );
471         
472         return ret;
473 }
474
475 Color
476 CurveGradient::get_color(Context context, const Point &point)const
477 {
478         const Color color(color_func(point,0));
479
480         if(get_amount()==1.0 && get_blend_method()==Color::BLEND_STRAIGHT)
481                 return color;
482         else
483                 return Color::blend(color,context.get_color(point),get_amount(),get_blend_method());
484 }
485
486 bool
487 CurveGradient::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const
488 {
489         SuperCallback supercb(cb,0,9500,10000);
490
491         if(get_amount()==1.0 && get_blend_method()==Color::BLEND_STRAIGHT)
492         {
493                 surface->set_wh(renddesc.get_w(),renddesc.get_h());
494         }
495         else
496         {
497                 if(!context.accelerated_render(surface,quality,renddesc,&supercb))
498                         return false;
499                 if(get_amount()==0)
500                         return true;
501         }
502
503                 
504         int x,y;
505
506         Surface::pen pen(surface->begin());
507         const Real pw(renddesc.get_pw()),ph(renddesc.get_ph());
508         Point pos;
509         Point tl(renddesc.get_tl());
510         const int w(surface->get_w());
511         const int h(surface->get_h());
512         
513         if(get_amount()==1.0 && get_blend_method()==Color::BLEND_STRAIGHT)
514         {
515                 for(y=0,pos[1]=tl[1];y<h;y++,pen.inc_y(),pen.dec_x(x),pos[1]+=ph)
516                         for(x=0,pos[0]=tl[0];x<w;x++,pen.inc_x(),pos[0]+=pw)
517                                 pen.put_value(color_func(pos,quality,calc_supersample(pos,pw,ph)));
518         }
519         else
520         {
521                 for(y=0,pos[1]=tl[1];y<h;y++,pen.inc_y(),pen.dec_x(x),pos[1]+=ph)
522                         for(x=0,pos[0]=tl[0];x<w;x++,pen.inc_x(),pos[0]+=pw)
523                                 pen.put_value(Color::blend(color_func(pos,quality,calc_supersample(pos,pw,ph)),pen.get_value(),get_amount(),get_blend_method()));
524         }
525
526         // Mark our progress as finished
527         if(cb && !cb->amount_complete(10000,10000))
528                 return false;
529
530         return true;
531 }