Skip to content

Commit cf57f26

Browse files
committed
Code supplementing the Mandelbrot Set tutorial
1 parent edf753f commit cf57f26

18 files changed

+487
-0
lines changed

mandelbrot/01_scatter_plot.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import matplotlib.pyplot as plt
2+
import numpy as np
3+
4+
np.warnings.filterwarnings("ignore")
5+
6+
7+
def complex_matrix(xmin, xmax, ymin, ymax, pixel_density):
8+
re = np.linspace(xmin, xmax, int((xmax - xmin) * pixel_density))
9+
im = np.linspace(ymin, ymax, int((ymax - ymin) * pixel_density))
10+
return re[:, None] + im[None, :] * 1j
11+
12+
13+
def is_stable(c, num_iterations):
14+
z = 0
15+
for _ in range(num_iterations):
16+
z = z ** 2 + c
17+
return abs(z) < 2
18+
19+
20+
def get_members(c, num_iterations):
21+
return c[is_stable(c, num_iterations)]
22+
23+
24+
if __name__ == "__main__":
25+
c = complex_matrix(-2, 0.5, -1.5, 1.5, pixel_density=21)
26+
members = get_members(c, num_iterations=20)
27+
28+
plt.scatter(members.real, members.imag, color="black", marker=",", s=1)
29+
plt.gca().set_aspect("equal")
30+
plt.axis("off")
31+
plt.tight_layout()
32+
plt.show()

mandelbrot/02_bw_plot.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import matplotlib.pyplot as plt
2+
import numpy as np
3+
4+
np.warnings.filterwarnings("ignore")
5+
6+
7+
def complex_matrix(xmin, xmax, ymin, ymax, pixel_density):
8+
re = np.linspace(xmin, xmax, int((xmax - xmin) * pixel_density))
9+
im = np.linspace(ymin, ymax, int((ymax - ymin) * pixel_density))
10+
return re[:, None] + im[None, :] * 1j
11+
12+
13+
def is_stable(c, num_iterations):
14+
z = 0
15+
for _ in range(num_iterations):
16+
z = z ** 2 + c
17+
return abs(z) < 2
18+
19+
20+
def get_members(c, num_iterations):
21+
return c[is_stable(c, num_iterations)]
22+
23+
24+
if __name__ == "__main__":
25+
c = complex_matrix(-2, 0.5, -1.5, 1.5, pixel_density=1280)
26+
27+
plt.imshow(is_stable(c, num_iterations=20).T, cmap="binary")
28+
plt.gca().set_aspect("equal")
29+
plt.axis("off")
30+
plt.tight_layout()
31+
plt.show()

mandelbrot/03_bw.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from PIL import Image
2+
3+
from mandelbrot_01 import MandelbrotSet
4+
5+
if __name__ == "__main__":
6+
7+
mandelbrot_set = MandelbrotSet(max_iterations=20)
8+
9+
width, height = 512, 512
10+
scale = 0.0075
11+
black_and_white = "1"
12+
13+
image = Image.new(mode=black_and_white, size=(width, height))
14+
for y in range(height):
15+
for x in range(width):
16+
re = scale * (x - width / 2)
17+
im = scale * (height / 2 - y)
18+
image.putpixel((x, y), complex(re, im) not in mandelbrot_set)
19+
20+
image.show()

mandelbrot/04_grayscale.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from PIL import Image
2+
3+
from mandelbrot_02 import MandelbrotSet
4+
5+
if __name__ == "__main__":
6+
7+
mandelbrot_set = MandelbrotSet(max_iterations=20)
8+
9+
width, height = 512, 512
10+
scale = 0.0075
11+
grayscale = "L"
12+
13+
image = Image.new(mode=grayscale, size=(width, height))
14+
for y in range(height):
15+
for x in range(width):
16+
re = scale * (x - width / 2)
17+
im = scale * (height / 2 - y)
18+
probability = 1 - mandelbrot_set.probability(complex(re, im))
19+
image.putpixel((x, y), int(probability * 255))
20+
21+
image.show()

mandelbrot/05_grayscale_smooth.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from PIL import Image
2+
3+
from mandelbrot_03 import MandelbrotSet
4+
5+
if __name__ == "__main__":
6+
7+
mandelbrot_set = MandelbrotSet(max_iterations=20)
8+
9+
width, height = 512, 512
10+
scale = 0.0075
11+
grayscale = "L"
12+
13+
image = Image.new(mode=grayscale, size=(width, height))
14+
for y in range(height):
15+
for x in range(width):
16+
re = scale * (x - width / 2)
17+
im = scale * (height / 2 - y)
18+
c = complex(re, im)
19+
probability = 1 - mandelbrot_set.probability(c, smooth=True)
20+
image.putpixel((x, y), max(0, min(int(probability * 255), 255)))
21+
22+
image.show()

mandelbrot/06_viewport_aspect.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from PIL import Image
2+
3+
from mandelbrot_03 import MandelbrotSet
4+
from viewport_01 import Viewport
5+
6+
if __name__ == "__main__":
7+
8+
mandelbrot_set = MandelbrotSet(max_iterations=20)
9+
10+
image = Image.new(mode="1", size=(512, 512), color=1)
11+
for pixel in Viewport(image, xmin=-2.5, xmax=1):
12+
if complex(pixel) in mandelbrot_set:
13+
pixel.color = 0
14+
15+
image.show()

mandelbrot/07_viewport_center.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from PIL import Image
2+
from PIL import ImageEnhance
3+
4+
from mandelbrot_03 import MandelbrotSet
5+
from viewport_02 import Viewport
6+
7+
if __name__ == "__main__":
8+
print("This might take a while...")
9+
10+
mandelbrot_set = MandelbrotSet(max_iterations=256)
11+
12+
image = Image.new(mode="L", size=(512, 512))
13+
for pixel in Viewport(image, center=-0.7435 + 0.1314j, width=0.002):
14+
c = complex(pixel)
15+
probability = 1 - mandelbrot_set.probability(c, smooth=True)
16+
pixel.color = max(0, min(int(probability * 255), 255))
17+
18+
enhancer = ImageEnhance.Brightness(image)
19+
enhancer.enhance(1.25).show()
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import matplotlib.cm
2+
from PIL import Image
3+
4+
from mandelbrot_03 import MandelbrotSet
5+
from viewport_02 import Viewport
6+
7+
8+
def paint(mandelbrot_set, viewport, palette, smooth):
9+
for pixel in viewport:
10+
probability = mandelbrot_set.probability(complex(pixel), smooth)
11+
index = int(min(probability * len(palette), len(palette) - 1))
12+
pixel.color = palette[index % len(palette)]
13+
14+
15+
def denormalize(palette):
16+
return [tuple(int(channel * 255) for channel in color) for color in palette]
17+
18+
19+
if __name__ == "__main__":
20+
print("This might take a while...")
21+
22+
colormap = matplotlib.cm.get_cmap("twilight").colors
23+
palette = denormalize(colormap)
24+
25+
mandelbrot_set = MandelbrotSet(max_iterations=512)
26+
image = Image.new(mode="RGB", size=(512, 512))
27+
viewport = Viewport(image, center=-0.7435 + 0.1314j, width=0.002)
28+
paint(mandelbrot_set, viewport, palette, smooth=True)
29+
image.show()
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from PIL import Image
2+
3+
from mandelbrot_03 import MandelbrotSet
4+
from viewport_02 import Viewport
5+
6+
7+
def paint(mandelbrot_set, viewport, palette, smooth):
8+
for pixel in viewport:
9+
probability = mandelbrot_set.probability(complex(pixel), smooth)
10+
index = int(min(probability * len(palette), len(palette) - 1))
11+
pixel.color = palette[index % len(palette)]
12+
13+
14+
def denormalize(palette):
15+
return [tuple(int(channel * 255) for channel in color) for color in palette]
16+
17+
18+
if __name__ == "__main__":
19+
print("This might take a while...")
20+
21+
exterior = [(1, 1, 1)] * 50
22+
interior = [(1, 1, 1)] * 5
23+
gray_area = [(1 - i / 44,) * 3 for i in range(45)]
24+
palette = denormalize(exterior + gray_area + interior)
25+
26+
mandelbrot_set = MandelbrotSet(max_iterations=20)
27+
image = Image.new(mode="RGB", size=(512, 512))
28+
viewport = Viewport(image, center=-0.75, width=3.5)
29+
paint(mandelbrot_set, viewport, palette, smooth=True)
30+
image.show()

mandelbrot/10_color_gradient.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import numpy as np
2+
from PIL import Image
3+
from scipy.interpolate import interp1d
4+
5+
from mandelbrot_03 import MandelbrotSet
6+
from viewport_02 import Viewport
7+
8+
9+
def paint(mandelbrot_set, viewport, palette, smooth):
10+
for pixel in viewport:
11+
probability = mandelbrot_set.probability(complex(pixel), smooth)
12+
index = int(min(probability * len(palette), len(palette) - 1))
13+
pixel.color = palette[index % len(palette)]
14+
15+
16+
def denormalize(palette):
17+
return [tuple(int(channel * 255) for channel in color) for color in palette]
18+
19+
20+
def make_gradient(colors, interpolation="linear"):
21+
X = [i / (len(colors) - 1) for i in range(len(colors))]
22+
Y = [[color[i] for color in colors] for i in range(3)]
23+
channels = [interp1d(X, y, kind=interpolation) for y in Y]
24+
return lambda x: [np.clip(channel(x), 0, 1) for channel in channels]
25+
26+
27+
if __name__ == "__main__":
28+
print("This might take a while...")
29+
30+
black = (0, 0, 0)
31+
blue = (0, 0, 1)
32+
maroon = (0.5, 0, 0)
33+
navy = (0, 0, 0.5)
34+
red = (1, 0, 0)
35+
36+
colors = [black, navy, blue, maroon, red, black]
37+
gradient = make_gradient(colors, interpolation="cubic")
38+
39+
num_colors = 256
40+
palette = denormalize([gradient(i / num_colors) for i in range(num_colors)])
41+
42+
mandelbrot_set = MandelbrotSet(max_iterations=25)
43+
image = Image.new(mode="RGB", size=(512, 512))
44+
viewport = Viewport(image, center=-0.75, width=3.5)
45+
paint(mandelbrot_set, viewport, palette, smooth=True)
46+
image.show()

0 commit comments

Comments
 (0)