-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
105 lines (81 loc) · 3.12 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
from dataclasses import dataclass
from itertools import product
from PIL import Image
from math import log
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm
@dataclass
class MandelbrotSet: #defining the Mandelbrot set, transforming from maths formula to python code
max_iterations: int
escape_radius: float = 2.0
def __contains__(self, c: complex) -> bool:
return self.stability(c) == 1
def stability(self, c: complex, smooth=False, clamp=True) -> float:
value = self.escape_count(c, smooth) / self.max_iterations
return max(0.0, min(value, 1.0)) if clamp else value
def escape_count(self, c: complex, smooth=False) -> int:
z = 0
for iteration in range(self.max_iterations):
z = z ** 2 + c
if abs(z) > self.escape_radius:
if smooth:
return iteration + 1 - log(log(abs(z))) / log(2)
return iteration
return self.max_iterations
@dataclass
class Viewport: #defining the viewport, the area of the image that we want to see
image: Image.Image
center: complex
width: float
@property
def height(self):
return self.scale * self.image.height
@property
def offset(self):
return self.center + complex(-self.width, self.height) / 2
@property
def scale(self):
return self.width / self.image.width
def __iter__(self):
for y in range(self.image.height):
for x in range(self.image.width):
yield Pixel(self, x, y)
@dataclass
class Pixel: #defining the pixel, so the image can be painted
viewport: Viewport
x: int
y: int
@property
def color(self):
return self.viewport.image.getpixel((self.x, self.y))
@color.setter
def color(self, value):
self.viewport.image.putpixel((self.x, self.y), value)
def __complex__(self):
return (
complex(self.x, -self.y)
* self.viewport.scale
+ self.viewport.offset
)
def paint(mandelbrot_set, viewport, palette, smooth): #painting the image
for pixel in viewport:
stability = mandelbrot_set.stability(complex(pixel), smooth)
index = int(min(stability * len(palette), len(palette) - 1))
pixel.color = palette[index % len(palette)]
def denormalize(palette): #getting the RGB values back
return [
tuple(int(channel * 255) for channel in color)
for color in palette
]
width, height = 1024, 1024
BLACK_AND_WHITE = '1'
GRAYSCALE = 'L'
RGB = 'RGB'
image = Image.new(RGB, (width, height))
mandelbrot_set = MandelbrotSet(max_iterations=256, escape_radius=1000) #here you can modify the max_iterations and escape_radius to get different results
viewport = Viewport(image, center=-0.7435 + 0.1314j, width=0.002)
colormap = matplotlib.cm.get_cmap('cividis').colors #here you can change the colormap to get different colors
palette = denormalize(colormap)
paint(mandelbrot_set, viewport, palette, smooth=True)
image.show()