Skip to content

Commit 6181e01

Browse files
authored
Merge pull request #221 from realpython/mandelbrot
Mandelbrot
2 parents 8eee00f + 55f2eab commit 6181e01

16 files changed

+443
-0
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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[np.newaxis, :] + im[:, np.newaxis] * 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+
mask = is_stable(c, num_iterations)
22+
return c[mask]
23+
24+
25+
if __name__ == "__main__":
26+
c = complex_matrix(-2, 0.5, -1.5, 1.5, pixel_density=21)
27+
members = get_members(c, num_iterations=20)
28+
29+
plt.scatter(members.real, members.imag, color="black", marker=",", s=1)
30+
plt.gca().set_aspect("equal")
31+
plt.axis("off")
32+
plt.tight_layout()
33+
plt.show()
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[np.newaxis, :] + im[:, np.newaxis] * 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+
mask = is_stable(c, num_iterations)
22+
return c[mask]
23+
24+
25+
if __name__ == "__main__":
26+
c = complex_matrix(-2, 0.5, -1.5, 1.5, pixel_density=512)
27+
28+
plt.imshow(is_stable(c, num_iterations=20), cmap="binary")
29+
plt.gca().set_aspect("equal")
30+
plt.axis("off")
31+
plt.tight_layout()
32+
plt.show()

mandelbrot-set-python/03_bw.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+
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+
c = scale * complex(x - width / 2, height / 2 - y)
17+
image.putpixel((x, y), c not in mandelbrot_set)
18+
19+
image.show()
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_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+
c = scale * complex(x - width / 2, height / 2 - y)
17+
instability = 1 - mandelbrot_set.stability(c)
18+
image.putpixel((x, y), int(instability * 255))
19+
20+
image.show()
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, escape_radius=1000)
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+
instability = 1 - mandelbrot_set.stability(c, smooth=True)
20+
image.putpixel((x, y), int(instability * 255))
21+
22+
image.show()
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 import Viewport
6+
7+
if __name__ == "__main__":
8+
print("This might take a while...")
9+
10+
mandelbrot_set = MandelbrotSet(max_iterations=256, escape_radius=1000)
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+
instability = 1 - mandelbrot_set.stability(c, smooth=True)
16+
pixel.color = int(instability * 255)
17+
18+
enhancer = ImageEnhance.Brightness(image)
19+
enhancer.enhance(1.25).show()
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import matplotlib.cm
2+
from PIL import Image
3+
4+
from mandelbrot_03 import MandelbrotSet
5+
from viewport import Viewport
6+
7+
8+
def paint(mandelbrot_set, viewport, palette, smooth):
9+
for pixel in viewport:
10+
stability = mandelbrot_set.stability(complex(pixel), smooth)
11+
index = int(min(stability * len(palette), len(palette) - 1))
12+
pixel.color = palette[index % len(palette)]
13+
14+
15+
def denormalize(palette):
16+
return [
17+
tuple(int(channel * 255) for channel in color) for color in palette
18+
]
19+
20+
21+
if __name__ == "__main__":
22+
print("This might take a while...")
23+
24+
colormap = matplotlib.cm.get_cmap("twilight").colors
25+
palette = denormalize(colormap)
26+
27+
mandelbrot_set = MandelbrotSet(max_iterations=512, escape_radius=1000)
28+
image = Image.new(mode="RGB", size=(512, 512))
29+
viewport = Viewport(image, center=-0.7435 + 0.1314j, width=0.002)
30+
paint(mandelbrot_set, viewport, palette, smooth=True)
31+
image.show()
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from PIL import Image
2+
3+
from mandelbrot_03 import MandelbrotSet
4+
from viewport import Viewport
5+
6+
7+
def paint(mandelbrot_set, viewport, palette, smooth):
8+
for pixel in viewport:
9+
stability = mandelbrot_set.stability(complex(pixel), smooth)
10+
index = int(min(stability * len(palette), len(palette) - 1))
11+
pixel.color = palette[index % len(palette)]
12+
13+
14+
def denormalize(palette):
15+
return [
16+
tuple(int(channel * 255) for channel in color) for color in palette
17+
]
18+
19+
20+
if __name__ == "__main__":
21+
print("This might take a while...")
22+
23+
exterior = [(1, 1, 1)] * 50
24+
interior = [(1, 1, 1)] * 5
25+
gray_area = [(1 - i / 44,) * 3 for i in range(45)]
26+
palette = denormalize(exterior + gray_area + interior)
27+
28+
mandelbrot_set = MandelbrotSet(max_iterations=20, escape_radius=1000)
29+
image = Image.new(mode="RGB", size=(512, 512))
30+
viewport = Viewport(image, center=-0.75, width=3.5)
31+
paint(mandelbrot_set, viewport, palette, smooth=True)
32+
image.show()
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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 import Viewport
7+
8+
9+
def paint(mandelbrot_set, viewport, palette, smooth):
10+
for pixel in viewport:
11+
stability = mandelbrot_set.stability(complex(pixel), smooth)
12+
index = int(min(stability * len(palette), len(palette) - 1))
13+
pixel.color = palette[index % len(palette)]
14+
15+
16+
def denormalize(palette):
17+
return [
18+
tuple(int(channel * 255) for channel in color) for color in palette
19+
]
20+
21+
22+
def make_gradient(colors, interpolation="linear"):
23+
X = [i / (len(colors) - 1) for i in range(len(colors))]
24+
Y = [[color[i] for color in colors] for i in range(3)]
25+
channels = [interp1d(X, y, kind=interpolation) for y in Y]
26+
return lambda x: [np.clip(channel(x), 0, 1) for channel in channels]
27+
28+
29+
if __name__ == "__main__":
30+
print("This might take a while...")
31+
32+
black = (0, 0, 0)
33+
blue = (0, 0, 1)
34+
maroon = (0.5, 0, 0)
35+
navy = (0, 0, 0.5)
36+
red = (1, 0, 0)
37+
38+
colors = [black, navy, blue, maroon, red, black]
39+
gradient = make_gradient(colors, interpolation="cubic")
40+
41+
num_colors = 256
42+
palette = denormalize(
43+
[gradient(i / num_colors) for i in range(num_colors)]
44+
)
45+
46+
mandelbrot_set = MandelbrotSet(max_iterations=20, escape_radius=1000)
47+
image = Image.new(mode="RGB", size=(512, 512))
48+
viewport = Viewport(image, center=-0.75, width=3.5)
49+
paint(mandelbrot_set, viewport, palette, smooth=True)
50+
image.show()
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from PIL import Image
2+
from PIL.ImageColor import getrgb
3+
4+
from mandelbrot_03 import MandelbrotSet
5+
from viewport import Viewport
6+
7+
8+
def hsb(hue_degrees: int, saturation: float, brightness: float):
9+
return getrgb(
10+
f"hsv({hue_degrees % 360},"
11+
f"{saturation * 100}%,"
12+
f"{brightness * 100}%)"
13+
)
14+
15+
16+
if __name__ == "__main__":
17+
print("This might take a while...")
18+
19+
mandelbrot_set = MandelbrotSet(max_iterations=20, escape_radius=1000)
20+
image = Image.new(mode="RGB", size=(512, 512))
21+
for pixel in Viewport(image, center=-0.75, width=3.5):
22+
stability = mandelbrot_set.stability(complex(pixel), smooth=True)
23+
pixel.color = (
24+
(0, 0, 0)
25+
if stability == 1
26+
else hsb(
27+
hue_degrees=int(stability * 360),
28+
saturation=stability,
29+
brightness=1,
30+
)
31+
)
32+
33+
image.show()

0 commit comments

Comments
 (0)