bacbbdaac77009c7001a4c67b6bdfa531e989c75
[synfig.git] /
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
124 bool
125 Mandelbrot::set_param(const String & param, const ValueBase &value)
126 {
127
128 //      IMPORT(color_shift);
129
130         IMPORT(gradient_offset_inside);
131         IMPORT(gradient_offset_outside);
132         IMPORT(gradient_loop_inside);
133         IMPORT(gradient_scale_outside);
134
135         IMPORT(distort_inside);
136         IMPORT(distort_outside);
137         IMPORT(solid_inside);
138         IMPORT(solid_outside);
139         IMPORT(invert_inside);
140         IMPORT(invert_outside);
141         IMPORT(shade_inside);
142         IMPORT(shade_outside);
143
144         IMPORT(smooth_outside);
145         IMPORT(broken);
146
147         IMPORT(gradient_inside);
148         IMPORT(gradient_outside);
149
150         if(param=="iterations" && value.same_type_as(iterations))
151         {
152                 iterations=value.get(iterations);
153                 if(iterations<0)
154                         iterations=0;
155                 if(iterations>500000)
156                         iterations=500000;
157                 return true;
158         }
159         if(param=="bailout" && value.same_type_as(bailout))
160         {
161                 bailout=value.get(bailout);
162                 bailout*=bailout;
163                 lp=log(log(bailout));
164                 return true;
165         }
166
167         return false;
168 }
169
170 ValueBase
171 Mandelbrot::get_param(const String & param)const
172 {
173 //      EXPORT(icolor);
174 //      EXPORT(ocolor);
175 //      EXPORT(color_shift);
176         EXPORT(iterations);
177
178         EXPORT(gradient_offset_inside);
179         EXPORT(gradient_offset_outside);
180         EXPORT(gradient_loop_inside);
181         EXPORT(gradient_scale_outside);
182
183         EXPORT(distort_inside);
184         EXPORT(distort_outside);
185         EXPORT(solid_inside);
186         EXPORT(solid_outside);
187         EXPORT(invert_inside);
188         EXPORT(invert_outside);
189         EXPORT(shade_inside);
190         EXPORT(shade_outside);
191         EXPORT(smooth_outside);
192         EXPORT(broken);
193
194         EXPORT(gradient_inside);
195         EXPORT(gradient_outside);
196
197         if(param=="bailout")
198                 return sqrt(bailout);
199
200         EXPORT_NAME();
201         EXPORT_VERSION();
202
203         return ValueBase();
204 }
205
206 Layer::Vocab
207 Mandelbrot::get_param_vocab()const
208 {
209         Layer::Vocab ret;
210
211
212         ret.push_back(ParamDesc("iterations")
213                 .set_local_name(_("Iterations"))
214         );
215         ret.push_back(ParamDesc("bailout")
216                 .set_local_name(_("Bailout ValueBase"))
217         );
218
219         ret.push_back(ParamDesc("broken")
220                 .set_local_name(_("Break Set"))
221                 .set_description(_("Modify equation to achieve interesting results"))
222         );
223
224
225         ret.push_back(ParamDesc("distort_inside")
226                 .set_local_name(_("Distort Inside"))
227                 .set_group(_("Inside"))
228         );
229         ret.push_back(ParamDesc("shade_inside")
230                 .set_local_name(_("Shade Inside"))
231                 .set_group(_("Inside"))
232         );
233         ret.push_back(ParamDesc("solid_inside")
234                 .set_local_name(_("Solid Inside"))
235                 .set_group(_("Inside"))
236         );
237         ret.push_back(ParamDesc("invert_inside")
238                 .set_local_name(_("Invert Inside"))
239                 .set_group(_("Inside"))
240         );
241         ret.push_back(ParamDesc("gradient_inside")
242                 .set_local_name(_("Gradient Inside"))
243                 .set_group(_("Inside"))
244         );
245         ret.push_back(ParamDesc("gradient_offset_inside")
246                 .set_local_name(_("Offset Inside"))
247                 .set_group(_("Inside"))
248         );
249         ret.push_back(ParamDesc("gradient_loop_inside")
250                 .set_local_name(_("Loop Inside"))
251                 .set_group(_("Inside"))
252         );
253
254         ret.push_back(ParamDesc("distort_outside")
255                 .set_local_name(_("Distort Outside"))
256                 .set_group(_("Outside"))
257         );
258         ret.push_back(ParamDesc("shade_outside")
259                 .set_local_name(_("Shade Outside"))
260                 .set_group(_("Outside"))
261         );
262         ret.push_back(ParamDesc("solid_outside")
263                 .set_local_name(_("Solid Outside"))
264                 .set_group(_("Outside"))
265         );
266         ret.push_back(ParamDesc("invert_outside")
267                 .set_local_name(_("Invert Outside"))
268                 .set_group(_("Outside"))
269         );
270         ret.push_back(ParamDesc("gradient_outside")
271                 .set_local_name(_("Gradient outside"))
272                 .set_group(_("Outside"))
273         );
274         ret.push_back(ParamDesc("smooth_outside")
275                 .set_local_name(_("Smooth Outside"))
276                 .set_description(_("Smooth the coloration outside the set"))
277                 .set_group(_("Outside"))
278         );
279         ret.push_back(ParamDesc("gradient_offset_outside")
280                 .set_local_name(_("Offset Outside"))
281                 .set_group(_("Outside"))
282         );
283         ret.push_back(ParamDesc("gradient_scale_outside")
284                 .set_local_name(_("Scale Outside"))
285                 .set_group(_("Outside"))
286         );
287
288         return ret;
289 }
290
291 Color
292 Mandelbrot::get_color(Context context, const Point &pos)const
293 {
294         Real
295                 cr, ci,
296                 zr, zi,
297                 zr_hold;
298
299         ColorReal
300                 depth, mag(0);
301
302         Color
303                 ret;
304
305
306         zr=zi=0;
307         cr=pos[0];
308         ci=pos[1];
309
310         for(int i=0;i<iterations;i++)
311         {
312                 // Perform complex multiplication
313                 zr_hold=zr;
314                 zr=zr*zr-zi*zi + cr;
315                 if(broken)zr+=zi; // Use "broken" algorithm, if requested (looks weird)
316                 zi=zr_hold*zi*2 + ci;
317
318
319                 // Calculate Magnitude
320                 mag=zr*zr+zi*zi;
321
322                 if(mag>bailout)
323                 {
324                         if(smooth_outside)
325                         {
326                                 // Darco's original mandelbrot smoothing algo
327                                 // depth=((Point::value_type)i+(2.0-sqrt(mag))/PI);
328
329                                 // Linas Vepstas algo (Better than darco's)
330                                 // See (http://linas.org/art-gallery/escape/smooth.html)
331                                 depth= (ColorReal)i + LOG_OF_2*lp - log(log(sqrt(mag))) / LOG_OF_2;
332
333                                 // Clamp
334                                 if(depth<0) depth=0;
335                         }
336                         else
337                                 depth=static_cast<ColorReal>(i);
338
339                         ColorReal amount(depth/static_cast<ColorReal>(iterations));
340                         amount=amount*gradient_scale_outside+gradient_offset_outside;
341                         amount-=floor(amount);
342
343                         if(solid_outside)
344                                 ret=gradient_outside(amount);
345                         else
346                         {
347                                 if(distort_outside)
348                                         ret=context.get_color(Point(pos[0]+zr,pos[1]+zi));
349                                 else
350                                         ret=context.get_color(pos);
351
352                                 if(invert_outside)
353                                         ret=~ret;
354
355                                 if(shade_outside)
356                                         ret=Color::blend(gradient_outside(amount), ret, 1.0);
357                         }
358
359
360                         return ret;
361                 }
362         }
363
364         ColorReal amount(abs(mag+gradient_offset_inside));
365         if(gradient_loop_inside)
366                 amount-=floor(amount);
367
368         if(solid_inside)
369                 ret=gradient_inside(amount);
370         else
371         {
372                 if(distort_inside)
373                         ret=context.get_color(Point(pos[0]+zr,pos[1]+zi));
374                 else
375                         ret=context.get_color(pos);
376
377                 if(invert_inside)
378                         ret=~ret;
379
380                 if(shade_inside)
381                         ret=Color::blend(gradient_inside(amount), ret, 1.0);
382         }
383
384         return ret;
385 }