Mandelbrot set: Difference between revisions

→‎Normal Map Effect, Mercator Projection and Deep Zoom Images: The original coloring methods added and subheadings inserted; Comments shortened
m (→‎Normal Map Effect, Mercator Projection and Perturbation Theory: Made changes to match Arnaud Chéritat's image.)
(→‎Normal Map Effect, Mercator Projection and Deep Zoom Images: The original coloring methods added and subheadings inserted; Comments shortened)
Line 10,902:
 
===Normal Map Effect, Mercator Projection and Deep Zoom Images===
====Smoothing, Normalization and Distance Estimation====
The Mandelbrot set is represented by distance estimation and normal maps using NumPy and complex matrices (cf. Arnaud Chéritat: [https://www.math.univ-toulouse.fr/~cheritat/wiki-draw/index.php/Mandelbrot_set#Normal_map_effect ''Normal map effect'']). Note that the second derivative (ddZ) grows very fast, so the second method can only be used for small iteration numbers (n <= 400). See also [https://web.archive.org/web/20140618000747/http://jussiharkonen.com/files/on_fractal_coloring_techniques(lo-res).pdf ''On Smooth Fractal Coloring Techniques''] by Jussi Härkönen and [https://www.shadertoy.com/view/wtscDX Julia Stripes] on Shadertoy.
The Mandelbrot set is printed with smooth colors. The ''e^(-|z|)-smoothing'', ''normalized iteration count'' and ''exterior distance estimation'' algorithms are used with NumPy and complex matrices (see Javier Barrallo & Damien M. Jones: [http://www.mi.sanu.ac.rs/vismath/javier/index.html ''Coloring Algorithms for Dynamical Systems in the Complex Plane''] and Mikael Hvidtfeldt Christensen: [http://blog.hvidtfeldts.net/index.php/2011/09/distance-estimated-3d-fractals-v-the-mandelbulb-different-de-approximations ''Distance Estimated 3D Fractals (V): The Mandelbulb & Different DE Approximations'']).
<syntaxhighlight lang="python">import numpy as np
import matplotlib.pyplot as plt
 
d, h = 800, 500 # pixel density (= image width) and image height
n, r = 200, 500 # number of iterations and escape radius (r > 2)
 
x = np.linspace(0, 2, num=d+1)
y = np.linspace(0, 2 * h / d, num=h+1)
 
A, B = np.meshgrid(x - 1, y - h / d)
C = 2.0 * (A + B * 1j) - 0.5
 
Z, dZ = np.zeros_like(C), np.zeros_like(C)
D, S, T = np.zeros(C.shape), np.zeros(C.shape), np.zeros(C.shape)
 
for k in range(n):
M = abs(Z) < r
S[M], T[M] = S[M] + np.exp(- abs(Z[M])), T[M] + 1
Z[M], dZ[M] = Z[M] ** 2 + C[M], 2 * Z[M] * dZ[M] + 1
 
plt.imshow(S ** 0.1, cmap=plt.cm.twilight_shifted, origin="lower")
plt.savefig("Mandelbrot_set_1.png", dpi=200)
 
N = abs(Z) >= r # normalized iteration count
T[N] = T[N] - np.log2(np.log(np.abs(Z[N])) / np.log(r))
 
plt.imshow(T ** 0.1, cmap=plt.cm.twilight_shifted, origin="lower")
plt.savefig("Mandelbrot_set_2.png", dpi=200)
 
N = abs(Z) > 2 # exterior distance estimation
D[N] = np.log(abs(Z[N])) * abs(Z[N]) / abs(dZ[N])
 
plt.imshow(D ** 0.1, cmap=plt.cm.twilight_shifted, origin="lower")
plt.savefig("Mandelbrot_set_3.png", dpi=200)</syntaxhighlight>
 
====Normal Map Effect and Stripe Average Coloring====
The Mandelbrot set is represented byusing distanceNormal estimationMaps and normalStripe mapsAverage usingColoring NumPy andby complexJussi matricesHärkönen (cf. Arnaud Chéritat: [https://www.math.univ-toulouse.fr/~cheritat/wiki-draw/index.php/Mandelbrot_set#Normal_map_effect ''Normal map effect'']). Note that the second derivative (ddZ) grows very fast, so the second method can only be used for small iteration numbers (n <= 400). See also [https://web.archive.org/web/20140618000747/http://jussiharkonen.com/files/on_fractal_coloring_techniques(lo-res).pdf ''On Smooth Fractal Coloring Techniques''] by Jussi Härkönen and [https://www.shadertoy.com/view/wtscDX Julia Stripes] on Shadertoy.
<syntaxhighlight lang="python">import numpy as np
import matplotlib.pyplot as plt
Line 10,926 ⟶ 10,964:
Z[M], dZ[M], ddZ[M] = Z[M] ** 2 + C[M], 2 * Z[M] * dZ[M] + 1, 2 * (dZ[M] ** 2 + Z[M] * ddZ[M])
 
N = abs(Z) >= r # normal map effect 1 (potentialequipotential functionlines)
P, Q = S[N] / T[N], (S[N] + np.cos(stripes * np.angle(Z[N]))) / (T[N] + 1)
F = np.log2(np.log(np.abs(Z[N])) / np.log(r)) # fraction between 0 and 1 (for interpolationnormalization)
R = Q + (P - Q) * F * F * (3 - 2 * F) # hermite interpolation (r is between qlinear andor pHermite)
U, H = Z[N] / dZ[N], 1 + R / damping # normal vectors to the equipotential lines and height perturbationvariations
U, v = U / abs(U), np.exp(direction / 180 * np.pi * 1j) # unit normal vectors and vector in light direction
D[N] = np.maximum((U.real * v.real + U.imag * v.imag + H * height) / (1 + height), 0)
 
Line 10,937 ⟶ 10,975:
plt.savefig("Mandelbrot_normal_map_1.png", dpi=200)
 
N = abs(Z) >= r # normal map effect 2 (distanceequidistant estimationlines)
U = Z[N] * dZ[N] * ((1 + np.log(abs(Z[N]))) * np.conj(dZ[N] ** 2) - np.log(abs(Z[N])) * np.conj(Z[N] * ddZ[N]))
U, v = U / abs(U), np.exp(direction / 180 * np.pi * 1j) # unit normal vectors and vector in light direction
D[N] = np.maximum((U.real * v.real + U.imag * v.imag + height) / (1 + height), 0)
 
Line 10,945 ⟶ 10,983:
plt.savefig("Mandelbrot_normal_map_2.png", dpi=200)</syntaxhighlight>
 
====Mercator Mandelbrot Maps and Zoom Images====
A small change in the code above creates Mercator maps of the Mandelbrot set (see David Madore: [http://www.madore.org/~david/math/mandelbrot.html ''Mandelbrot set images and videos''] and Anders Sandberg: [https://www.flickr.com/photos/arenamontanus/sets/72157615740829949 ''Mercator Mandelbrot Maps'']). The maximum magnification is about <math>e ^ {2 \pi \cdot h / d} = e ^ {2 \pi \cdot 5.5} \approx 535.5 ^ {5.5} \approx 10 ^ {15}</math>, which is also the maximum for 64-bit arithmetic. Note that Anders Sandberg uses a different scaling. He uses <math>10 ^ {3 \cdot h / d} = 1000 ^ {h / d}</math> instead of <math>e ^ {2 \pi \cdot h / d} \approx 535.5 ^ {h / d}</math>, so his images appear somewhat compressed in comparison (but not much, because <math>1000 ^ {5.0} \approx 535.5 ^ {5.5}</math>). With the same pixel density and the same maximum magnification, the difference in height between the maps is only about 10 percent. By misusing a scatter plot, it is possible to create zoom images of the Mandelbrot set. See also [https://commons.wikimedia.org/wiki/File:Mandelbrot_sequence_new.gif ''Mandelbrot sequence new''] (Wikimedia) for a zoom animation to the given coordinates.
<syntaxhighlight lang="python">import numpy as np
Line 10,984 ⟶ 11,023:
plt.savefig("Mercator_Mandelbrot_zoom.png", dpi=100)</syntaxhighlight>
 
====Perturbation Theory and Deep Mercator Maps====
For deep zoom images it is sufficient to calculate a single point with high accuracy. A good approximation can then be found for all other points by means of a perturbation calculation with standard accuracy. Rebasing is used to reduce glitches. See [https://en.wikipedia.org/wiki/Plotting_algorithms_for_the_Mandelbrot_set#Perturbation_theory_and_series_approximation Perturbation theory] (Wikipedia) and [https://gbillotey.github.io/Fractalshades-doc/math.html#avoiding-loss-of-precision Avoiding loss of precision] (Fractalshades) for details. See also the image [https://www.flickr.com/photos/arenamontanus/3430921497/in/album-72157615740829949/ Deeper Mercator Mandelbrot] by Anders Sandberg.
<syntaxhighlight lang="python">import numpy as np
305

edits