/* Apophysis Plugin Mandelbrot Set Plugin v2 - Copyright 2008,2009 Jed Kelsey Changed to work with Apophysis 7X / DC in 2010 by Georg Kiehne This program 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 program 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ typedef struct { int dcm_iter; int dcm_miniter; int dcm_smooth_iter; int dcm_retries; int dcm_mode; int dcm_pow; int dcm_color_method; double dcm_invert; double dcm_xmin; double dcm_xmax; double dcm_ymin; double dcm_ymax; double dcm_scatter; double dcm_sx; double dcm_sy; double dcm_zscale; double x0, y0; double zs, sc; } Variables; #define _USE_MATH_DEFINES #include "apoplugin.h" APO_PLUGIN("dc_mandelbrot"); APO_VARIABLES( VAR_INTEGER_RANGE(dcm_iter, 5, INT_MAX, 25), VAR_INTEGER_RANGE(dcm_miniter, 0, INT_MAX, 1), VAR_INTEGER_RANGE(dcm_smooth_iter, 0, INT_MAX, 0), VAR_INTEGER_RANGE(dcm_retries, 0, INT_MAX, 50), VAR_INTEGER_RANGE(dcm_mode, 0, 5, 0), VAR_INTEGER_RANGE(dcm_pow, -6, 6, 2), // TODO: negative powers VAR_INTEGER_RANGE(dcm_color_method, 0, 7, 0), VAR_REAL_RANGE(dcm_invert, 0.0, 1.0, 0.0), VAR_REAL(dcm_xmin, -2.0), VAR_REAL(dcm_xmax, 2.0), VAR_REAL(dcm_ymin, -1.5), VAR_REAL(dcm_ymax, 1.5), VAR_REAL_RANGE(dcm_scatter, -1000.0, 1000.0, 0.0), // Following parameters don't affect iterations, just the output point positions. VAR_REAL(dcm_sx, 0.0), VAR_REAL(dcm_sy, 0.0), VAR_REAL(dcm_zscale, 0.0) ); int PluginVarPrepare(Variation* vp) { VAR(zs) = VVAR * VAR(dcm_zscale) / VAR(dcm_iter); VAR(sc) = VAR(dcm_scatter) / 10.0; return TRUE; } inline double fmod2(double h, double q) { return fmod(fabs(h),q); } int PluginVarCalc(Variation* vp) { double x=0.0, y=0.0, x1=0.0, y1=0.0, x2, y2, xtemp=0.0; double cx=0.0, cy=0.0; int maxiter = VAR(dcm_iter); int miniter = VAR(dcm_miniter); // = 0.1*(maxiter*(1-scatter)); double xmax = VAR(dcm_xmax); double xmin = VAR(dcm_xmin); double ymax = VAR(dcm_ymax); double ymin = VAR(dcm_ymin); int maxRetries = VAR(dcm_retries); int mode = VAR(dcm_mode); /* 0=Mandelbrot, 1=Julia, 3=Tricorn(Mandelbar) */ int color_method = VAR(dcm_color_method); double xp = 0.0, yp = 0.0; int smooth_iter=0, max_smooth_iter=VAR(dcm_smooth_iter); int inverted, iter=0, retries=0; int isblur = (VAR(sc)>=0); double smoothed_iter = 0.0, inv_iter = 1.0; double mag2 = 0.0; int m_power = VAR(dcm_pow); int m_power_abs = ((m_power<0) ? -m_power : m_power); inverted = random01() < VAR(dcm_invert); if (!isblur) { VAR(x0) = FTx; VAR(y0) = FTy; } do { if (VAR(sc)==0) { // Force selection of point at random VAR(x0) = VAR(y0) = 0; } if (VAR(x0)==0 && VAR(y0)==0) { // Choose a point at random VAR(x0) = (xmax-xmin)*random01() + xmin; VAR(y0) = (ymax-ymin)*random01() + ymin; } else { // Choose a point close to previous point VAR(x0) += VAR(sc)*(random01()-0.5); VAR(y0) += VAR(sc)*(random01()-0.5); } // default to Mandelbrot Set cx = x1 = x = xp = VAR(x0); cy = y1 = y = yp = VAR(y0); switch(mode) { case 1: // Julia Set cx = TM(e); cy = TM(f); break; case 2: // tricorn (Mandelbar) // leave as-is, handled below break; default: // Regular Mandelbrot set // Leave as-is break; } iter = smooth_iter = 0; while ( (((x2=x*x) + (y2=y*y) < 4) && (iter < maxiter)) || (smooth_iter++0) && (iter0) { x = x/xtemp; y = -y/xtemp; } iter++; } iter -= (smooth_iter-1); // could probably bypass check and always select next point at random if ( (miniter==0) || (!inverted && (iter>=maxiter)) /*|| (iter < miniter)*/ ) { VAR(x0) = VAR(y0) = 0; // Random point next time } else if ( (iter < miniter) || (inverted && (itermaxRetries-5) { // VAR(x0) /= 100; // VAR(y0) /= 100; //} else VAR(x0) = VAR(y0) = 0; } if (++retries > maxRetries) break; } while ((inverted && (iter < maxiter)) || (!inverted && ((iter >= maxiter) || ((miniter>0) && (iter < miniter))))); smoothed_iter = iter; if (max_smooth_iter>0) { // use Normalized Iteration Count Algorithm for smoothing mag2 = x2 + y2; if (mag2 > 1.1) //FIXME: change this back to if(mag2>4) ? smoothed_iter += 1 - log(log(mag2)/2)/M_LN2; } if (smoothed_iter>0) inv_iter = 1/smoothed_iter; else inv_iter = 1; // Adjust location of point according to sx,sy and final iterated point (x,y) // (use of inv_iter reduces effect of factor near regions of high gradient FPx += VVAR*(x1 + VAR(dcm_sx)*x*inv_iter); FPy += VVAR*(y1 + VAR(dcm_sy)*y*inv_iter); //FPx += VVAR*(x1 + VAR(dcm_sx)*x); //FPy += VVAR*(y1 + VAR(dcm_sy)*y); // TODO: add check to see whether this Apo supports 3D? if (VAR(dcm_zscale)) { FPz += smoothed_iter * VAR(zs); } // Allow plugin to influence coloring (-X- changed for Apo7X/DC) if (smoothed_iter<0) smoothed_iter=0; if (smoothed_iter>maxiter) smoothed_iter=maxiter; switch (color_method) { case 1: // scale colormap indexing extent by the final angle of the iterated point // after the extra "smoothing" iterations complete xtemp = 0.0; if (y != 0.0) xtemp = atan2(x,y) * M_1_2PI; TC = fmod2(xtemp, 1); break; case 2: // scale colormap indexing extent by the angle of the iterated point at time of escape xtemp = 0.0; if (yp != 0.0) xtemp = atan2(xp,yp) * M_1_2PI; TC = fmod2(xtemp, 1); break; case 3: // combination of mode 4 and 2 xtemp = 0.0; if (y-yp != 0.0) xtemp = atan2(x-xp,y-yp) * M_1_2PI; TC = fmod2((smoothed_iter/maxiter * xtemp), 1); break; case 4: // scale colormap indexing extent by the product of the scaled iteration count and // the angle of the iterated point at time of escape xtemp = 0.0; if (yp != 0.0) xtemp = atan2(xp,yp) * M_1_2PI; TC = fmod2((smoothed_iter/maxiter * xtemp), 1); break; case 5: // scale colormap indexing extent by a combination of scaled iteration count, // the squared magnitude and adjusted angle of the iterated point at time of escape xtemp = 0.0; if (yp != 0.0) xtemp = (0.5 + atan2(xp,yp) * M_1_2PI) * (xp*xp+yp*yp); TC = fmod2((smoothed_iter/maxiter * xtemp), 1); break; case 6: // scale colormap indexing extent by a combination of scaled iteration count and // the squared magnitude of the iterated point at time of escape xtemp = xp*xp+yp*yp; TC = fmod2((smoothed_iter/maxiter * xtemp), 1); break; case 7: // scale colormap indexing extent by a combination of scaled iteration count and // the squared magnitude of the iterated point at time of escape // (slightly more relaxed color rolloff than case 6) xtemp = sqrt(xp*xp+yp*yp); TC = fmod2((smoothed_iter/maxiter * xtemp), 1); break; case 0: // default coloring method: scale colormap indexing extent by the scaled "escape time" // (iteration count) default: TC = fmod2(smoothed_iter/maxiter, 1); break; } return TRUE; }