Skip to content

Commit 5ee6c48

Browse files
authored
Merge pull request #2 from TheAlgorithms/master
update fork
2 parents ec738b0 + 71b1202 commit 5ee6c48

File tree

1 file changed

+150
-0
lines changed

1 file changed

+150
-0
lines changed

graphics/mandelbrot.py

+150
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
"""
2+
The Mandelbrot set is the set of complex numbers "c" for which the series
3+
"z_(n+1) = z_n * z_n + c" does not diverge, i.e. remains bounded. Thus, a
4+
complex number "c" is a member of the Mandelbrot set if, when starting with
5+
"z_0 = 0" and applying the iteration repeatedly, the absolute value of
6+
"z_n" remains bounded for all "n > 0". Complex numbers can be written as
7+
"a + b*i": "a" is the real component, usually drawn on the x-axis, and "b*i"
8+
is the imaginary component, usually drawn on the y-axis. Most visualizations
9+
of the Mandelbrot set use a color-coding to indicate after how many steps in
10+
the series the numbers outside the set diverge. Images of the Mandelbrot set
11+
exhibit an elaborate and infinitely complicated boundary that reveals
12+
progressively ever-finer recursive detail at increasing magnifications, making
13+
the boundary of the Mandelbrot set a fractal curve.
14+
(description adapted from https://en.wikipedia.org/wiki/Mandelbrot_set )
15+
(see also https://en.wikipedia.org/wiki/Plotting_algorithms_for_the_Mandelbrot_set )
16+
"""
17+
18+
19+
import colorsys
20+
21+
from PIL import Image # type: ignore
22+
23+
24+
def get_distance(x: float, y: float, max_step: int) -> float:
25+
"""
26+
Return the relative distance (= step/max_step) after which the complex number
27+
constituted by this x-y-pair diverges. Members of the Mandelbrot set do not
28+
diverge so their distance is 1.
29+
30+
>>> get_distance(0, 0, 50)
31+
1.0
32+
>>> get_distance(0.5, 0.5, 50)
33+
0.061224489795918366
34+
>>> get_distance(2, 0, 50)
35+
0.0
36+
"""
37+
a = x
38+
b = y
39+
for step in range(max_step):
40+
a_new = a * a - b * b + x
41+
b = 2 * a * b + y
42+
a = a_new
43+
44+
# divergence happens for all complex number with an absolute value
45+
# greater than 4
46+
if a * a + b * b > 4:
47+
break
48+
return step / (max_step - 1)
49+
50+
51+
def get_black_and_white_rgb(distance: float) -> tuple:
52+
"""
53+
Black&white color-coding that ignores the relative distance. The Mandelbrot
54+
set is black, everything else is white.
55+
56+
>>> get_black_and_white_rgb(0)
57+
(255, 255, 255)
58+
>>> get_black_and_white_rgb(0.5)
59+
(255, 255, 255)
60+
>>> get_black_and_white_rgb(1)
61+
(0, 0, 0)
62+
"""
63+
if distance == 1:
64+
return (0, 0, 0)
65+
else:
66+
return (255, 255, 255)
67+
68+
69+
def get_color_coded_rgb(distance: float) -> tuple:
70+
"""
71+
Color-coding taking the relative distance into account. The Mandelbrot set
72+
is black.
73+
74+
>>> get_color_coded_rgb(0)
75+
(255, 0, 0)
76+
>>> get_color_coded_rgb(0.5)
77+
(0, 255, 255)
78+
>>> get_color_coded_rgb(1)
79+
(0, 0, 0)
80+
"""
81+
if distance == 1:
82+
return (0, 0, 0)
83+
else:
84+
return tuple(round(i * 255) for i in colorsys.hsv_to_rgb(distance, 1, 1))
85+
86+
87+
def get_image(
88+
image_width: int = 800,
89+
image_height: int = 600,
90+
figure_center_x: float = -0.6,
91+
figure_center_y: float = 0,
92+
figure_width: float = 3.2,
93+
max_step: int = 50,
94+
use_distance_color_coding: bool = True,
95+
) -> Image.Image:
96+
"""
97+
Function to generate the image of the Mandelbrot set. Two types of coordinates
98+
are used: image-coordinates that refer to the pixels and figure-coordinates
99+
that refer to the complex numbers inside and outside the Mandelbrot set. The
100+
figure-coordinates in the arguments of this function determine which section
101+
of the Mandelbrot set is viewed. The main area of the Mandelbrot set is
102+
roughly between "-1.5 < x < 0.5" and "-1 < y < 1" in the figure-coordinates.
103+
104+
>>> get_image().load()[0,0]
105+
(255, 0, 0)
106+
>>> get_image(use_distance_color_coding = False).load()[0,0]
107+
(255, 255, 255)
108+
"""
109+
img = Image.new("RGB", (image_width, image_height))
110+
pixels = img.load()
111+
112+
# loop through the image-coordinates
113+
for image_x in range(image_width):
114+
for image_y in range(image_height):
115+
116+
# determine the figure-coordinates based on the image-coordinates
117+
figure_height = figure_width / image_width * image_height
118+
figure_x = figure_center_x + (image_x / image_width - 0.5) * figure_width
119+
figure_y = figure_center_y + (image_y / image_height - 0.5) * figure_height
120+
121+
distance = get_distance(figure_x, figure_y, max_step)
122+
123+
# color the corresponding pixel based on the selected coloring-function
124+
if use_distance_color_coding:
125+
pixels[image_x, image_y] = get_color_coded_rgb(distance)
126+
else:
127+
pixels[image_x, image_y] = get_black_and_white_rgb(distance)
128+
129+
return img
130+
131+
132+
if __name__ == "__main__":
133+
import doctest
134+
135+
doctest.testmod()
136+
137+
# colored version, full figure
138+
img = get_image()
139+
140+
# uncomment for colored version, different section, zoomed in
141+
# img = get_image(figure_center_x = -0.6, figure_center_y = -0.4,
142+
# figure_width = 0.8)
143+
144+
# uncomment for black and white version, full figure
145+
# img = get_image(use_distance_color_coding = False)
146+
147+
# uncomment to save the image
148+
# img.save("mandelbrot.png")
149+
150+
img.show()

0 commit comments

Comments
 (0)