/* === S Y N F I G ========================================================= */ /*! \file mandelbrot.cpp ** \brief Implementation of the "Mandelbrot Set" layer ** ** $Id$ ** ** \legal ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley ** Copyright (c) 2007 Chris Moore ** ** This package is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public License as ** published by the Free Software Foundation; either version 2 of ** the License, or (at your option) any later version. ** ** This package is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ** General Public License for more details. ** \endlegal ** ** === N O T E S =========================================================== ** ** ========================================================================= */ /* === H E A D E R S ======================================================= */ #ifdef USING_PCH # include "pch.h" #else #ifdef HAVE_CONFIG_H # include #endif #include "mandelbrot.h" #include #include #include #include #include #include #include #include #endif /* === M A C R O S ========================================================= */ #define LOG_OF_2 0.69314718055994528623 /* === G L O B A L S ======================================================= */ SYNFIG_LAYER_INIT(Mandelbrot); SYNFIG_LAYER_SET_NAME(Mandelbrot,"mandelbrot"); SYNFIG_LAYER_SET_LOCAL_NAME(Mandelbrot,N_("Mandelbrot Set")); SYNFIG_LAYER_SET_CATEGORY(Mandelbrot,N_("Fractals")); SYNFIG_LAYER_SET_VERSION(Mandelbrot,"0.2"); SYNFIG_LAYER_SET_CVS_ID(Mandelbrot,"$Id$"); /* === P R O C E D U R E S ================================================= */ inline void color_neg_flip(Color &color) { if(color.get_a()==0) { color=Color::alpha(); return; } if(color.get_a()<0) color=-color; if(color.get_r()<0) { color.set_g(color.get_g()-color.get_r()); color.set_b(color.get_b()-color.get_r()); color.set_r(0); } if(color.get_g()<0) { color.set_r(color.get_r()-color.get_g()); color.set_b(color.get_b()-color.get_g()); color.set_g(0); } if(color.get_b()<0) { color.set_r(color.get_r()-color.get_b()); color.set_g(color.get_g()-color.get_b()); color.set_b(0); } } /* === M E T H O D S ======================================================= */ Mandelbrot::Mandelbrot(): gradient_offset_inside(0.0), gradient_offset_outside(0.0), gradient_loop_inside(true), gradient_scale_outside(1.0), gradient_inside(Color::alpha(),Color::black()), gradient_outside(Color::alpha(),Color::black()) { iterations=32; // color_shift=Angle::deg(0); distort_inside=true; distort_outside=true; solid_inside=false; solid_outside=false; invert_inside=false; invert_outside=false; shade_inside=true; shade_outside=true; smooth_outside=true; broken=false; bailout=4; lp=log(log(bailout)); Layer::Vocab voc(get_param_vocab()); Layer::fill_static(voc); } bool Mandelbrot::set_param(const String & param, const ValueBase &value) { // IMPORT(color_shift); IMPORT(gradient_offset_inside); IMPORT(gradient_offset_outside); IMPORT(gradient_loop_inside); IMPORT(gradient_scale_outside); IMPORT(distort_inside); IMPORT(distort_outside); IMPORT(solid_inside); IMPORT(solid_outside); IMPORT(invert_inside); IMPORT(invert_outside); IMPORT(shade_inside); IMPORT(shade_outside); IMPORT(smooth_outside); IMPORT(broken); IMPORT(gradient_inside); IMPORT(gradient_outside); // TODO: Use IMPORT_PLUS if(param=="iterations" && value.same_type_as(iterations)) { iterations=value.get(iterations); if(iterations<0) iterations=0; if(iterations>500000) iterations=500000; set_param_static(param, value.get_static()); return true; } if(param=="bailout" && value.same_type_as(bailout)) { bailout=value.get(bailout); bailout*=bailout; lp=log(log(bailout)); set_param_static(param, value.get_static()); return true; } return false; } ValueBase Mandelbrot::get_param(const String & param)const { // EXPORT(icolor); // EXPORT(ocolor); // EXPORT(color_shift); EXPORT(iterations); EXPORT(gradient_offset_inside); EXPORT(gradient_offset_outside); EXPORT(gradient_loop_inside); EXPORT(gradient_scale_outside); EXPORT(distort_inside); EXPORT(distort_outside); EXPORT(solid_inside); EXPORT(solid_outside); EXPORT(invert_inside); EXPORT(invert_outside); EXPORT(shade_inside); EXPORT(shade_outside); EXPORT(smooth_outside); EXPORT(broken); EXPORT(gradient_inside); EXPORT(gradient_outside); if(param=="bailout") { ValueBase ret(sqrt(bailout)); ret.set_static(get_param_static(param)); return ret; } EXPORT_NAME(); EXPORT_VERSION(); return ValueBase(); } Layer::Vocab Mandelbrot::get_param_vocab()const { Layer::Vocab ret; ret.push_back(ParamDesc("iterations") .set_local_name(_("Iterations")) ); ret.push_back(ParamDesc("bailout") .set_local_name(_("Bailout ValueBase")) ); ret.push_back(ParamDesc("broken") .set_local_name(_("Break Set")) .set_description(_("Modify equation to achieve interesting results")) ); ret.push_back(ParamDesc("distort_inside") .set_local_name(_("Distort Inside")) .set_group(_("Inside")) ); ret.push_back(ParamDesc("shade_inside") .set_local_name(_("Shade Inside")) .set_group(_("Inside")) ); ret.push_back(ParamDesc("solid_inside") .set_local_name(_("Solid Inside")) .set_group(_("Inside")) ); ret.push_back(ParamDesc("invert_inside") .set_local_name(_("Invert Inside")) .set_group(_("Inside")) ); ret.push_back(ParamDesc("gradient_inside") .set_local_name(_("Gradient Inside")) .set_group(_("Inside")) ); ret.push_back(ParamDesc("gradient_offset_inside") .set_local_name(_("Offset Inside")) .set_group(_("Inside")) ); ret.push_back(ParamDesc("gradient_loop_inside") .set_local_name(_("Loop Inside")) .set_group(_("Inside")) ); ret.push_back(ParamDesc("distort_outside") .set_local_name(_("Distort Outside")) .set_group(_("Outside")) ); ret.push_back(ParamDesc("shade_outside") .set_local_name(_("Shade Outside")) .set_group(_("Outside")) ); ret.push_back(ParamDesc("solid_outside") .set_local_name(_("Solid Outside")) .set_group(_("Outside")) ); ret.push_back(ParamDesc("invert_outside") .set_local_name(_("Invert Outside")) .set_group(_("Outside")) ); ret.push_back(ParamDesc("gradient_outside") .set_local_name(_("Gradient outside")) .set_group(_("Outside")) ); ret.push_back(ParamDesc("smooth_outside") .set_local_name(_("Smooth Outside")) .set_description(_("Smooth the coloration outside the set")) .set_group(_("Outside")) ); ret.push_back(ParamDesc("gradient_offset_outside") .set_local_name(_("Offset Outside")) .set_group(_("Outside")) ); ret.push_back(ParamDesc("gradient_scale_outside") .set_local_name(_("Scale Outside")) .set_group(_("Outside")) ); return ret; } Color Mandelbrot::get_color(Context context, const Point &pos)const { Real cr, ci, zr, zi, zr_hold; ColorReal depth, mag(0); Color ret; zr=zi=0; cr=pos[0]; ci=pos[1]; for(int i=0;ibailout) { if(smooth_outside) { // Darco's original mandelbrot smoothing algo // depth=((Point::value_type)i+(2.0-sqrt(mag))/PI); // Linas Vepstas algo (Better than darco's) // See (http://linas.org/art-gallery/escape/smooth.html) depth= (ColorReal)i + LOG_OF_2*lp - log(log(sqrt(mag))) / LOG_OF_2; // Clamp if(depth<0) depth=0; } else depth=static_cast(i); ColorReal amount(depth/static_cast(iterations)); amount=amount*gradient_scale_outside+gradient_offset_outside; amount-=floor(amount); if(solid_outside) ret=gradient_outside(amount); else { if(distort_outside) ret=context.get_color(Point(pos[0]+zr,pos[1]+zi)); else ret=context.get_color(pos); if(invert_outside) ret=~ret; if(shade_outside) ret=Color::blend(gradient_outside(amount), ret, 1.0); } return ret; } } ColorReal amount(abs(mag+gradient_offset_inside)); if(gradient_loop_inside) amount-=floor(amount); if(solid_inside) ret=gradient_inside(amount); else { if(distort_inside) ret=context.get_color(Point(pos[0]+zr,pos[1]+zi)); else ret=context.get_color(pos); if(invert_inside) ret=~ret; if(shade_inside) ret=Color::blend(gradient_inside(amount), ret, 1.0); } return ret; }