-
Notifications
You must be signed in to change notification settings - Fork 0
/
mandelbrot.js
170 lines (142 loc) · 4.62 KB
/
mandelbrot.js
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// The function gets called when the window is fully loaded
window.onload = function () {
// Get the canvas and context
var canvas = document.getElementById("mandelbrot_canvas");
var context = canvas.getContext("2d");
// Width and height of the image
var imagew = canvas.width;
var imageh = canvas.height;
// Image Data (RGBA)
var imagedata = context.createImageData(imagew, imageh);
// Pan and zoom parameters
var offsetx = -imagew / 2;
var offsety = -imageh / 2;
var panx = -100;
var pany = 0;
var zoom = 150;
// Palette array of 256 colors
var palette = [];
// The maximum number of iterations per pixel
var maxiterations = 250;
// Initialize the game
function init() {
// Add mouse events
canvas.addEventListener("mousedown", onMouseDown);
// Generate palette
generatePalette();
// Generate image
generateImage();
// Enter main loop
main(0);
}
// Main loop
function main(tframe) {
// Request animation frames
window.requestAnimationFrame(main);
// Draw the generate image
context.putImageData(imagedata, 0, 0);
}
// Generate palette
function generatePalette() {
// Calculate a gradient
var roffset = 24;
var goffset = 16;
var boffset = 0;
for (var i = 0; i < 256; i++) {
palette[i] = { r: roffset, g: goffset, b: boffset };
if (i < 64) {
roffset += 3;
} else if (i < 128) {
goffset += 3;
} else if (i < 192) {
boffset += 3;
}
}
}
// Generate the fractal image
function generateImage() {
// Iterate over the pixels
for (var y = 0; y < imageh; y++) {
for (var x = 0; x < imagew; x++) {
iterate(x, y, maxiterations);
}
}
}
// Calculate the color of a specific pixel
function iterate(x, y, maxiterations) {
// Convert the screen coordinate to a fractal coordinate
var x0 = (x + offsetx + panx) / zoom;
var y0 = (y + offsety + pany) / zoom;
// Iteration variables
var a = 0;
var b = 0;
var rx = 0;
var ry = 0;
// Iterate
var iterations = 0;
while (iterations < maxiterations && (rx * rx + ry * ry <= 4)) {
rx = a * a - b * b + x0;
ry = 2 * a * b + y0;
// Next iteration
a = rx;
b = ry;
iterations++;
}
// Get palette color based on the number of iterations
var color;
if (iterations == maxiterations) {
color = { r: 0, g: 0, b: 0 }; // Black
} else {
var index = Math.floor((iterations / (maxiterations - 1)) * 255);
color = palette[index];
}
// Apply the color
var pixelindex = (y * imagew + x) * 4;
imagedata.data[pixelindex] = color.r;
imagedata.data[pixelindex + 1] = color.g;
imagedata.data[pixelindex + 2] = color.b;
imagedata.data[pixelindex + 3] = 255;
}
// Zoom the fractal
function zoomFractal(x, y, factor, zoomin) {
if (zoomin) {
// Zoom in
zoom *= factor;
panx = factor * (x + offsetx + panx);
pany = factor * (y + offsety + pany);
} else {
// Zoom out
zoom /= factor;
panx = (x + offsetx + panx) / factor;
pany = (y + offsety + pany) / factor;
}
}
// Mouse event handlers
function onMouseDown(e) {
var pos = getMousePos(canvas, e);
// Zoom out with Control
var zoomin = true;
if (e.ctrlKey) {
zoomin = false;
}
// Pan with Shift
var zoomfactor = 2;
if (e.shiftKey) {
zoomfactor = 1;
}
// Zoom the fractal at the mouse position
zoomFractal(pos.x, pos.y, zoomfactor, zoomin);
// Generate a new image
generateImage();
}
// Get the mouse position
function getMousePos(canvas, e) {
var rect = canvas.getBoundingClientRect();
return {
x: Math.round((e.clientX - rect.left) / (rect.right - rect.left) * canvas.width),
y: Math.round((e.clientY - rect.top) / (rect.bottom - rect.top) * canvas.height)
};
}
// Call init to start the game
init();
};