Lyapunov atom domains for negative multibrot sets
Back at the end of April last year I think I was futzing about on math.stackexchange.com answering a question about rendering negative multibrot sets, for example one produced by iterations of \(z \to z^{-2} + c\). I tried applying the atom domain colouring from the regular Mandelbrot set, but found it looked better if I accumulated all the partials with additive blending, not just the final domain. Here's a zoomed in view:
I implemented it as a GLSL fragment shader in Fragmentarium, here's the source code (which you can download too):
// Mandelbrot set for \( z \to z^{-n} + c \) coloured by Lyapunov atom domains // Created: Thu Apr 30 15:10:00 2015 #include "Progressive2D.frag" #include "Complex.frag" const float pi = 3.141592653589793; const float phi = (sqrt(5.0) + 1.0) / 2.0; #group Lyapunov atom domains uniform int Iterations; slider[10,200,5000] uniform int Power; slider[-16,-2,16] vec3 color(vec2 c) { // critical point is \( 0 \) for positive Power, and \( 0^Power + c = c \) // critical point is \( \infty \) for negative Power, and \( \infty^Power + c = c \) // so start iterating from \( c \) vec2 z = c; // Lyapunov exponent accumulator float le = 0.0; // atom domain accumulator float minle = 0.0; int mini = 1; // accumulated colour vec4 rgba = vec4(0.0); for (int i = 0; i < Iterations; ++i) { // \( zn1 \gets z^{Power - 1} \) vec2 zn1 = vec2(1.0, 0.0); for (int j = 0; j < abs(Power - 1); ++j) { zn1 = cMul(zn1, z); } if (Power < 0) { zn1 = cInverse(zn1); } // \( dz \gets Power z^{Power - 1} \) vec2 dz = float(Power) * zn1; // \( z \gets z^{Power} + c \) z = cMul(zn1,z) + c; // \( le \gets le + 2 log |dz| \) float dle = log(dot(dz, dz)); le += dle; // if the delta is smaller than any previous, accumulate the atom domain domain if (dle < minle) { minle = dle; mini = i + 1; float hue = 2.0 * pi / (36.0 + 1.0/(phi*phi)) * float(mini); vec3 rainbow = 2.0 * pi / 3.0 * vec3(0.0, 1.0, 2.0); vec3 domain = clamp(vec3(0.5) + 0.5 * sin(vec3(hue) + rainbow), 0.0, 1.0); rgba += vec4(domain, 1.0); } } // accumulated 'iterations' logs of squared magnitudes // so divide by 2 iterations le /= 2.0 * float(Iterations); // scale accumulated colour and blacken interior return mix(rgba.rgb / rgba.a, vec3(le < 0.001 ? 0.0 : tanh(exp(le))), 0.5); }
More negative powers than -2 don't look very good, though.