Initial Stable Commit
[synfig.git] / synfig-core / trunk / src / modules / mod_particle / plant.cpp
1 /* === S I N F G =========================================================== */
2 /*!     \file bline.cpp
3 **      \brief Template
4 **
5 **      $Id: plant.cpp,v 1.2 2005/01/13 06:48:39 darco Exp $
6 **
7 **      \legal
8 **      Copyright (c) 2002 Robert B. Quattlebaum Jr.
9 **
10 **      This software and associated documentation
11 **      are CONFIDENTIAL and PROPRIETARY property of
12 **      the above-mentioned copyright holder.
13 **
14 **      You may not copy, print, publish, or in any
15 **      other way distribute this software without
16 **      a prior written agreement with
17 **      the copyright holder.
18 **      \endlegal
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 <sinfg/angle.h>
32 #include "plant.h"
33 #include <sinfg/string.h>
34 #include <sinfg/time.h>
35 #include <sinfg/context.h>
36 #include <sinfg/paramdesc.h>
37 #include <sinfg/renddesc.h>
38 #include <sinfg/surface.h>
39 #include <sinfg/value.h>
40 #include <sinfg/valuenode.h>
41
42 #include <ETL/calculus>
43 #include <ETL/bezier>
44 #include <ETL/hermite>
45 #include <vector>
46
47 #include <sinfg/valuenode_bline.h>
48
49 #endif
50
51 using namespace etl;
52
53 /* === M A C R O S ========================================================= */
54
55 #define SAMPLES         300
56 #define ROUND_END_FACTOR        (4)
57 #define CUSP_THRESHOLD          (0.15)
58 #define NO_LOOP_COOKIE          sinfg::Vector(84951305,7836658)
59 #define EPSILON                         (0.000000001)
60 #define CUSP_TANGENT_ADJUST     (0.025)
61
62 /* === G L O B A L S ======================================================= */
63
64 SINFG_LAYER_INIT(Plant);
65 SINFG_LAYER_SET_NAME(Plant,"plant");
66 SINFG_LAYER_SET_LOCAL_NAME(Plant,_("Plant"));
67 SINFG_LAYER_SET_CATEGORY(Plant,_("Particle Systems"));
68 SINFG_LAYER_SET_VERSION(Plant,"0.1");
69 SINFG_LAYER_SET_CVS_ID(Plant,"$Id: plant.cpp,v 1.2 2005/01/13 06:48:39 darco Exp $");
70
71 /* === P R O C E D U R E S ================================================= */
72
73 /* === M E T H O D S ======================================================= */
74
75
76 Plant::Plant():
77         split_angle(Angle::deg(10)),
78         gravity(0,-0.1),
79         velocity(0.3),
80         step(0.01),
81         sprouts(10)
82 {
83         bounding_rect=Rect::zero();
84         random_factor=0.2;
85         random.set_seed(time(NULL));
86
87         bline.push_back(BLinePoint());
88         bline.push_back(BLinePoint());
89         bline.push_back(BLinePoint());
90         bline[0].set_vertex(Point(0,1));        
91         bline[1].set_vertex(Point(0,-1));       
92         bline[2].set_vertex(Point(1,0));
93         bline[0].set_tangent(bline[1].get_vertex()-bline[2].get_vertex()*0.5f); 
94         bline[1].set_tangent(bline[2].get_vertex()-bline[0].get_vertex()*0.5f); 
95         bline[2].set_tangent(bline[0].get_vertex()-bline[1].get_vertex()*0.5f); 
96         bline[0].set_width(1.0f);       
97         bline[1].set_width(1.0f);       
98         bline[2].set_width(1.0f);       
99         bline_loop=true;
100         mass=(0.5);
101         splits=5;
102         drag=0.1;
103         size=0.015;
104         sync();
105         size_as_alpha=false;
106 }
107
108 void
109 Plant::branch(int n,int depth,float t, float stunt_growth, sinfg::Point position,sinfg::Vector vel)const
110 {
111         float next_split((1.0-t)/(splits-depth)+t/*+random_factor*random(40+depth,t*splits,0,0)/splits*/);
112         for(;t<next_split;t+=step)
113         {
114                 vel[0]+=gravity[0]*step;
115                 vel[1]+=gravity[1]*step;
116                 vel*=(1.0-(drag)*step);
117                 position[0]+=vel[0]*step;
118                 position[1]+=vel[1]*step;
119
120                 particle_list.push_back(Particle(
121                         position,
122                         gradient(t)
123                 ));
124                 bounding_rect.expand(position);
125         }
126         
127         if(t>=1.0-stunt_growth)return;
128
129         sinfg::Real sin_v=sinfg::Angle::cos(split_angle).get();
130         sinfg::Real cos_v=sinfg::Angle::sin(split_angle).get();
131         
132         sinfg::Vector velocity1(vel[0]*sin_v-vel[1]*cos_v+random_factor*random(2,30+n+depth,t*splits,0.0f,0.0f),vel[0]*cos_v+vel[1]*sin_v+random_factor*random(2,32+n+depth,t*splits,0.0f,0.0f));
133         sinfg::Vector velocity2(vel[0]*sin_v+vel[1]*cos_v+random_factor*random(2,31+n+depth,t*splits,0.0f,0.0f),-vel[0]*cos_v+vel[1]*sin_v+random_factor*random(2,33+n+depth,t*splits,0.0f,0.0f));
134         
135         Plant::branch(n,depth+1,t,stunt_growth,position,velocity1);
136         Plant::branch(n,depth+1,t,stunt_growth,position,velocity2);
137 }
138
139 void
140 Plant::calc_bounding_rect()const
141 {
142         std::vector<sinfg::BLinePoint>::const_iterator iter,next;
143
144         bounding_rect=Rect::zero();
145
146         // Bline must have at least 2 points in it
147         if(bline.size()<=2)
148                 return;
149
150         next=bline.begin();
151
152         if(bline_loop)
153                 iter=--bline.end();
154         else
155                 iter=next++;
156         
157         for(;next!=bline.end();iter=next++)
158         {
159                 bounding_rect.expand(iter->get_vertex());
160                 bounding_rect.expand(next->get_vertex());
161                 bounding_rect.expand(iter->get_vertex()+iter->get_tangent2()*0.3333333333333);
162                 bounding_rect.expand(next->get_vertex()-next->get_tangent1()*0.3333333333333);
163                 bounding_rect.expand(next->get_vertex()+next->get_tangent2()*velocity);
164         }
165         bounding_rect.expand_x(gravity[0]);
166         bounding_rect.expand_y(gravity[1]);
167         bounding_rect.expand_x(size);
168         bounding_rect.expand_y(size);
169 }
170
171 void
172 Plant::sync()const
173 {
174         particle_list.clear();
175         
176         bounding_rect=Rect::zero();
177
178         // Bline must have at least 2 points in it
179         if(bline.size()<=2)
180                 return;
181         
182         std::vector<sinfg::BLinePoint>::const_iterator iter,next;
183
184         etl::hermite<Vector> curve;
185         
186         Real step(abs(this->step));
187         
188         int seg(0);
189
190         next=bline.begin();
191         
192         if(bline_loop)
193                 iter=--bline.end();
194         else
195                 iter=next++;
196         
197         for(;next!=bline.end();iter=next++,seg++)
198         {
199                 curve.p1()=iter->get_vertex();
200                 curve.t1()=iter->get_tangent2();
201                 curve.p2()=next->get_vertex();
202                 curve.t2()=next->get_tangent1();
203                 curve.sync();
204                 etl::derivative<etl::hermite<Vector> > deriv(curve);
205                 
206                 Real f;
207                 int i(0), b(round_to_int((1.0/step)/(float)sprouts-1));
208                 if(b<=0)b=1;
209                 for(f=0.0;f<1.0;f+=step,i++)
210                 {
211                         Point point(curve(f));
212                         
213                         particle_list.push_back(Particle(
214                                 point,
215                                 gradient(0)
216                         ));
217                         
218                         bounding_rect.expand(point);
219                         
220                         Real stunt_growth(random(2,i,f+seg,0.0f,0.0f)/2.0+0.5);
221                         stunt_growth*=stunt_growth;
222                         
223                         Vector branch_velocity(deriv(f).norm()*velocity);
224                         
225                         branch_velocity[0]+=random_factor*random(2,1,f*splits,0.0f,0.0f);
226                         branch_velocity[1]+=random_factor*random(2,2,f*splits,0.0f,0.0f);
227                         
228                         if(i%b==0)
229                                 branch(
230                                         i,
231                                         0,
232                                         0,      // time
233                                         stunt_growth, // stunt growth
234                                         point,branch_velocity
235                                 );
236                 }
237         }
238         
239         needs_sync_=false;
240 }
241
242 bool
243 Plant::set_param(const String & param, const ValueBase &value)
244 {
245         if(param=="bline" && value.get_type()==ValueBase::TYPE_LIST)
246         {
247                 bline=value;
248                 bline_loop=value.get_loop();
249                 needs_sync_=true;
250                 
251                 return true;
252         }
253         if(param=="seed" && value.same_as(int()))
254         {
255                 random.set_seed(value.get(int()));
256                 needs_sync_=true;
257                 return true;
258         }
259         IMPORT_PLUS(split_angle,needs_sync_=true);
260         IMPORT_PLUS(gravity,needs_sync_=true);
261         IMPORT_PLUS(gradient,needs_sync_=true);
262         IMPORT_PLUS(velocity,needs_sync_=true);
263         IMPORT_PLUS(step,needs_sync_=true);
264         IMPORT_PLUS(splits,needs_sync_=true);
265         IMPORT_PLUS(sprouts,needs_sync_=true);
266         IMPORT_PLUS(random_factor,needs_sync_=true);
267         IMPORT_PLUS(drag,needs_sync_=true);
268         IMPORT(size);
269         IMPORT(size_as_alpha);
270         
271         return Layer_Composite::set_param(param,value);
272 }
273 /*
274 void
275 Plant::set_time(Context context, Time time)const
276 {
277         if(needs_sync==true)
278         {
279                 sync();
280                 needs_sync_=false;
281         }
282         //const_cast<Plant*>(this)->sync();
283         context.set_time(time);
284 }
285
286 void
287 Plant::set_time(Context context, Time time, Vector pos)const
288 {
289         if(needs_sync==true)
290         {
291                 sync();
292                 needs_sync_=false;
293         }
294         //const_cast<Plant*>(this)->sync();
295         context.set_time(time,pos);
296 }
297 */
298 ValueBase
299 Plant::get_param(const String& param)const
300 {
301         if(param=="seed")
302                 return random.get_seed();
303         EXPORT(bline);
304         EXPORT(split_angle);
305         EXPORT(gravity);
306         EXPORT(velocity);
307         EXPORT(step);
308         EXPORT(gradient);
309         EXPORT(splits);
310         EXPORT(sprouts);
311         EXPORT(random_factor);
312         EXPORT(drag);
313         EXPORT(size);
314
315         EXPORT(size_as_alpha);
316         
317         EXPORT_NAME();
318         EXPORT_VERSION();
319
320         return Layer_Composite::get_param(param);
321 }
322
323 Layer::Vocab
324 Plant::get_param_vocab()const
325 {
326         Layer::Vocab ret(Layer_Composite::get_param_vocab());
327
328         ret.push_back(ParamDesc("bline")
329                 .set_local_name(_("Vertices"))
330                 //.set_origin("offset")
331                 //.set_scalar("width")
332                 .set_description(_("A list of BLine Points"))
333         );
334
335         ret.push_back(ParamDesc("gradient")
336                 .set_local_name(_("Gradient"))
337         );
338
339         ret.push_back(ParamDesc("split_angle")
340                 .set_local_name(_("Split Angle"))
341         );
342
343         ret.push_back(ParamDesc("gravity")
344                 .set_local_name(_("Gravity"))
345                 .set_is_distance()
346         );
347
348         ret.push_back(ParamDesc("velocity")
349                 .set_local_name(_("Velocity"))
350         );
351
352         ret.push_back(ParamDesc("size")
353                 .set_local_name(_("Stem Size"))
354                 .set_is_distance()
355         );
356
357         ret.push_back(ParamDesc("size_as_alpha")
358                 .set_local_name(_("SizeAsAlpha"))
359         );
360
361         ret.push_back(ParamDesc("step")
362                 .set_local_name(_("Step"))
363         );
364
365         ret.push_back(ParamDesc("seed")
366                 .set_local_name(_("Seed"))
367         );
368
369         ret.push_back(ParamDesc("splits")
370                 .set_local_name(_("Splits"))
371         );
372
373         ret.push_back(ParamDesc("sprouts")
374                 .set_local_name(_("Sprouts"))
375         );
376
377         ret.push_back(ParamDesc("random_factor")
378                 .set_local_name(_("Random Factor"))
379         );
380
381         ret.push_back(ParamDesc("drag")
382                 .set_local_name(_("Drag"))
383         );
384
385
386         return ret;
387 }
388
389 bool
390 Plant::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const
391 {       
392         bool ret(context.accelerated_render(surface,quality,renddesc,cb));
393         if(is_disabled() || !ret)
394                 return ret;
395         
396         Surface dest_surface;
397         dest_surface.set_wh(surface->get_w(),surface->get_h());
398         dest_surface.clear();
399         
400         const Point     tl(renddesc.get_tl());
401         const Point br(renddesc.get_br());
402
403         const int       w(renddesc.get_w());
404         const int       h(renddesc.get_h());
405
406         // Width and Height of a pixel
407         const Real pw = (br[0] - tl[0]) / w;
408         const Real ph = (br[1] - tl[1]) / h;
409
410         if(needs_sync_==true)
411                 sync();
412
413         std::vector<Particle>::reverse_iterator iter;
414         const float size_factor(1);
415         float radius(size_factor*size*sqrt(1.0f/(abs(pw)*abs(ph))));
416
417         if(radius>1.0f)
418         {
419                 radius*=1.0;
420                 int x1,y1,x2,y2;
421                 for(iter=particle_list.rbegin();iter!=particle_list.rend();++iter)
422                 {
423                         float radius(radius);
424                         Color color(iter->color);
425                         if(size_as_alpha)
426                         {
427                                 radius*=color.get_a();
428                                 color.set_a(1);
429                         }
430                         
431                         x1=ceil_to_int((iter->point[0]-tl[0])/pw-(radius*0.5));
432                         y1=ceil_to_int((iter->point[1]-tl[1])/ph-(radius*0.5));
433                         x2=x1+round_to_int(radius);
434                         y2=y1+round_to_int(radius);
435                                         
436                         if(x1>=surface->get_w() || y1>=surface->get_h())
437                                 continue;
438
439                         if(x2<0 || y2<0)
440                                 continue;
441
442                         if(x2>=surface->get_w())
443                                 x2=surface->get_w();
444                         if(y2>=surface->get_h())
445                                 y2=surface->get_h();
446                         
447                         if(x1<0)
448                                 x1=0;
449                         if(y1<0)
450                                 y1=0;
451                         
452                         int w(min(round_to_int(radius),x2-x1));
453                         int h(min(round_to_int(radius),y2-y1));
454                         
455                         if(w<=0 || h<=0)
456                                 continue;
457                                 
458                         Surface::alpha_pen surface_pen(dest_surface.get_pen(x1,y1),1.0f);
459                         
460                         dest_surface.fill(color,surface_pen,w,h);
461                 }
462         }
463         else
464         {
465                 //radius/=0.01;
466                 radius*=sqrt(step)*12.0f;
467                 int x,y;
468                 float a,b,c,d;
469                 for(iter=particle_list.rbegin();iter!=particle_list.rend();++iter)
470                 {
471                         float radius(radius);
472                         Color color(iter->color);
473                         if(size_as_alpha)
474                         {
475                                 radius*=color.get_a();
476                                 color.set_a(1);
477                         }
478
479                         x=floor_to_int((iter->point[0]-tl[0])/pw-0.5f);
480                         y=floor_to_int((iter->point[1]-tl[1])/ph-0.5f);
481                                         
482                         if(x>=surface->get_w()-1 || y>=surface->get_h()-1 || x<0 || y<0)
483                         {
484                                 continue;
485                         }
486         
487                         a=((iter->point[0]-tl[0])/pw-0.5f-x)*radius;
488                         b=((iter->point[1]-tl[1])/ph-0.5f-y)*radius;
489                         c=radius-a;
490                         d=radius-b;
491                         
492                         Surface::alpha_pen surface_pen(dest_surface.get_pen(x,y),1.0f);
493                         
494                         surface_pen.set_alpha(c*d);
495                         surface_pen.put_value(color);
496                         surface_pen.inc_x();
497                         surface_pen.set_alpha(a*d);
498                         surface_pen.put_value(color);
499                         surface_pen.inc_y();
500                         surface_pen.set_alpha(a*b);
501                         surface_pen.put_value(color);
502                         surface_pen.dec_x();
503                         surface_pen.set_alpha(c*b);
504                         surface_pen.put_value(color);           
505                 }
506         }
507                 
508         Surface::alpha_pen pen(surface->get_pen(0,0),get_amount(),get_blend_method());
509         dest_surface.blit_to(pen);
510         
511         return true;
512 }
513
514 Rect
515 Plant::get_bounding_rect(Context context)const
516 {
517         if(needs_sync_==true)
518                 sync();
519         
520         if(is_disabled())
521                 return Rect::zero();
522         
523         if(Color::is_onto(get_blend_method()))
524                 return context.get_full_bounding_rect() & bounding_rect;
525
526         //if(get_blend_method()==Color::BLEND_BEHIND)
527         //      return context.get_full_bounding_rect() | bounding_rect;
528         return bounding_rect;
529 }