Merge branch 'genete_static_values'
[synfig.git] / synfig-core / src / modules / lyr_std / mandelbrot.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file mandelbrot.cpp
3 **      \brief Implementation of the "Mandelbrot Set" layer
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **      Copyright (c) 2007 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 "mandelbrot.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
46 #endif
47
48 /* === M A C R O S ========================================================= */
49
50 #define LOG_OF_2                0.69314718055994528623
51
52 /* === G L O B A L S ======================================================= */
53
54 SYNFIG_LAYER_INIT(Mandelbrot);
55 SYNFIG_LAYER_SET_NAME(Mandelbrot,"mandelbrot");
56 SYNFIG_LAYER_SET_LOCAL_NAME(Mandelbrot,N_("Mandelbrot Set"));
57 SYNFIG_LAYER_SET_CATEGORY(Mandelbrot,N_("Fractals"));
58 SYNFIG_LAYER_SET_VERSION(Mandelbrot,"0.2");
59 SYNFIG_LAYER_SET_CVS_ID(Mandelbrot,"$Id$");
60
61 /* === P R O C E D U R E S ================================================= */
62
63 inline void
64 color_neg_flip(Color &color)
65 {
66         if(color.get_a()==0)
67         {
68                 color=Color::alpha();
69                 return;
70         }
71
72         if(color.get_a()<0)
73                 color=-color;
74
75         if(color.get_r()<0)
76         {
77                 color.set_g(color.get_g()-color.get_r());
78                 color.set_b(color.get_b()-color.get_r());
79                 color.set_r(0);
80         }
81         if(color.get_g()<0)
82         {
83                 color.set_r(color.get_r()-color.get_g());
84                 color.set_b(color.get_b()-color.get_g());
85                 color.set_g(0);
86         }
87         if(color.get_b()<0)
88         {
89                 color.set_r(color.get_r()-color.get_b());
90                 color.set_g(color.get_g()-color.get_b());
91                 color.set_b(0);
92         }
93 }
94
95 /* === M E T H O D S ======================================================= */
96
97 Mandelbrot::Mandelbrot():
98         gradient_offset_inside(0.0),
99         gradient_offset_outside(0.0),
100         gradient_loop_inside(true),
101         gradient_scale_outside(1.0),
102         gradient_inside(Color::alpha(),Color::black()),
103         gradient_outside(Color::alpha(),Color::black())
104 {
105         iterations=32;
106 //      color_shift=Angle::deg(0);
107
108         distort_inside=true;
109         distort_outside=true;
110         solid_inside=false;
111         solid_outside=false;
112         invert_inside=false;
113         invert_outside=false;
114         shade_inside=true;
115         shade_outside=true;
116
117         smooth_outside=true;
118         broken=false;
119
120         bailout=4;
121         lp=log(log(bailout));
122
123         Layer::Vocab voc(get_param_vocab());
124         Layer::fill_static(voc);
125 }
126
127 bool
128 Mandelbrot::set_param(const String & param, const ValueBase &value)
129 {
130
131 //      IMPORT(color_shift);
132
133         IMPORT(gradient_offset_inside);
134         IMPORT(gradient_offset_outside);
135         IMPORT(gradient_loop_inside);
136         IMPORT(gradient_scale_outside);
137
138         IMPORT(distort_inside);
139         IMPORT(distort_outside);
140         IMPORT(solid_inside);
141         IMPORT(solid_outside);
142         IMPORT(invert_inside);
143         IMPORT(invert_outside);
144         IMPORT(shade_inside);
145         IMPORT(shade_outside);
146
147         IMPORT(smooth_outside);
148         IMPORT(broken);
149
150         IMPORT(gradient_inside);
151         IMPORT(gradient_outside);
152
153 // TODO: Use IMPORT_PLUS
154         if(param=="iterations" && value.same_type_as(iterations))
155         {
156                 iterations=value.get(iterations);
157                 if(iterations<0)
158                         iterations=0;
159                 if(iterations>500000)
160                         iterations=500000;
161                 set_param_static(param, value.get_static());
162                 return true;
163         }
164         if(param=="bailout" && value.same_type_as(bailout))
165         {
166                 bailout=value.get(bailout);
167                 bailout*=bailout;
168                 lp=log(log(bailout));
169                 set_param_static(param, value.get_static());
170                 return true;
171         }
172
173         return false;
174 }
175
176 ValueBase
177 Mandelbrot::get_param(const String & param)const
178 {
179 //      EXPORT(icolor);
180 //      EXPORT(ocolor);
181 //      EXPORT(color_shift);
182         EXPORT(iterations);
183
184         EXPORT(gradient_offset_inside);
185         EXPORT(gradient_offset_outside);
186         EXPORT(gradient_loop_inside);
187         EXPORT(gradient_scale_outside);
188
189         EXPORT(distort_inside);
190         EXPORT(distort_outside);
191         EXPORT(solid_inside);
192         EXPORT(solid_outside);
193         EXPORT(invert_inside);
194         EXPORT(invert_outside);
195         EXPORT(shade_inside);
196         EXPORT(shade_outside);
197         EXPORT(smooth_outside);
198         EXPORT(broken);
199
200         EXPORT(gradient_inside);
201         EXPORT(gradient_outside);
202
203         if(param=="bailout")
204         {
205                 ValueBase ret(sqrt(bailout));
206                 ret.set_static(get_param_static(param));
207                 return ret;
208         }
209
210         EXPORT_NAME();
211         EXPORT_VERSION();
212
213         return ValueBase();
214 }
215
216 Layer::Vocab
217 Mandelbrot::get_param_vocab()const
218 {
219         Layer::Vocab ret;
220
221
222         ret.push_back(ParamDesc("iterations")
223                 .set_local_name(_("Iterations"))
224         );
225         ret.push_back(ParamDesc("bailout")
226                 .set_local_name(_("Bailout ValueBase"))
227         );
228
229         ret.push_back(ParamDesc("broken")
230                 .set_local_name(_("Break Set"))
231                 .set_description(_("Modify equation to achieve interesting results"))
232         );
233
234
235         ret.push_back(ParamDesc("distort_inside")
236                 .set_local_name(_("Distort Inside"))
237                 .set_group(_("Inside"))
238         );
239         ret.push_back(ParamDesc("shade_inside")
240                 .set_local_name(_("Shade Inside"))
241                 .set_group(_("Inside"))
242         );
243         ret.push_back(ParamDesc("solid_inside")
244                 .set_local_name(_("Solid Inside"))
245                 .set_group(_("Inside"))
246         );
247         ret.push_back(ParamDesc("invert_inside")
248                 .set_local_name(_("Invert Inside"))
249                 .set_group(_("Inside"))
250         );
251         ret.push_back(ParamDesc("gradient_inside")
252                 .set_local_name(_("Gradient Inside"))
253                 .set_group(_("Inside"))
254         );
255         ret.push_back(ParamDesc("gradient_offset_inside")
256                 .set_local_name(_("Offset Inside"))
257                 .set_group(_("Inside"))
258         );
259         ret.push_back(ParamDesc("gradient_loop_inside")
260                 .set_local_name(_("Loop Inside"))
261                 .set_group(_("Inside"))
262         );
263
264         ret.push_back(ParamDesc("distort_outside")
265                 .set_local_name(_("Distort Outside"))
266                 .set_group(_("Outside"))
267         );
268         ret.push_back(ParamDesc("shade_outside")
269                 .set_local_name(_("Shade Outside"))
270                 .set_group(_("Outside"))
271         );
272         ret.push_back(ParamDesc("solid_outside")
273                 .set_local_name(_("Solid Outside"))
274                 .set_group(_("Outside"))
275         );
276         ret.push_back(ParamDesc("invert_outside")
277                 .set_local_name(_("Invert Outside"))
278                 .set_group(_("Outside"))
279         );
280         ret.push_back(ParamDesc("gradient_outside")
281                 .set_local_name(_("Gradient outside"))
282                 .set_group(_("Outside"))
283         );
284         ret.push_back(ParamDesc("smooth_outside")
285                 .set_local_name(_("Smooth Outside"))
286                 .set_description(_("Smooth the coloration outside the set"))
287                 .set_group(_("Outside"))
288         );
289         ret.push_back(ParamDesc("gradient_offset_outside")
290                 .set_local_name(_("Offset Outside"))
291                 .set_group(_("Outside"))
292         );
293         ret.push_back(ParamDesc("gradient_scale_outside")
294                 .set_local_name(_("Scale Outside"))
295                 .set_group(_("Outside"))
296         );
297
298         return ret;
299 }
300
301 Color
302 Mandelbrot::get_color(Context context, const Point &pos)const
303 {
304         Real
305                 cr, ci,
306                 zr, zi,
307                 zr_hold;
308
309         ColorReal
310                 depth, mag(0);
311
312         Color
313                 ret;
314
315
316         zr=zi=0;
317         cr=pos[0];
318         ci=pos[1];
319
320         for(int i=0;i<iterations;i++)
321         {
322                 // Perform complex multiplication
323                 zr_hold=zr;
324                 zr=zr*zr-zi*zi + cr;
325                 if(broken)zr+=zi; // Use "broken" algorithm, if requested (looks weird)
326                 zi=zr_hold*zi*2 + ci;
327
328
329                 // Calculate Magnitude
330                 mag=zr*zr+zi*zi;
331
332                 if(mag>bailout)
333                 {
334                         if(smooth_outside)
335                         {
336                                 // Darco's original mandelbrot smoothing algo
337                                 // depth=((Point::value_type)i+(2.0-sqrt(mag))/PI);
338
339                                 // Linas Vepstas algo (Better than darco's)
340                                 // See (http://linas.org/art-gallery/escape/smooth.html)
341                                 depth= (ColorReal)i + LOG_OF_2*lp - log(log(sqrt(mag))) / LOG_OF_2;
342
343                                 // Clamp
344                                 if(depth<0) depth=0;
345                         }
346                         else
347                                 depth=static_cast<ColorReal>(i);
348
349                         ColorReal amount(depth/static_cast<ColorReal>(iterations));
350                         amount=amount*gradient_scale_outside+gradient_offset_outside;
351                         amount-=floor(amount);
352
353                         if(solid_outside)
354                                 ret=gradient_outside(amount);
355                         else
356                         {
357                                 if(distort_outside)
358                                         ret=context.get_color(Point(pos[0]+zr,pos[1]+zi));
359                                 else
360                                         ret=context.get_color(pos);
361
362                                 if(invert_outside)
363                                         ret=~ret;
364
365                                 if(shade_outside)
366                                         ret=Color::blend(gradient_outside(amount), ret, 1.0);
367                         }
368
369
370                         return ret;
371                 }
372         }
373
374         ColorReal amount(abs(mag+gradient_offset_inside));
375         if(gradient_loop_inside)
376                 amount-=floor(amount);
377
378         if(solid_inside)
379                 ret=gradient_inside(amount);
380         else
381         {
382                 if(distort_inside)
383                         ret=context.get_color(Point(pos[0]+zr,pos[1]+zi));
384                 else
385                         ret=context.get_color(pos);
386
387                 if(invert_inside)
388                         ret=~ret;
389
390                 if(shade_inside)
391                         ret=Color::blend(gradient_inside(amount), ret, 1.0);
392         }
393
394         return ret;
395 }