mandelbrot-set
Version:
Web components and a web worker for painting Julia and Mandelbrot sets on a web page.
105 lines (99 loc) • 3.84 kB
JavaScript
// See https://en.wikipedia.org/wiki/Mandelbrot_set.
// Computes z := z^2 + c until |z| > 2. Returns the count of
// iterations run checking if the Mandelbrot set contains c,
// when z starts as [0 + 0i] and c is a complex number.
function iterationsToEscapeMandelbrotSet (iterationThreshold, cr, ci) {
let iterations = 1
// Complex number z to compute recursively
let zr = 0
let zi = 0
// Local variables to store parts of the computation of z^2,
// so that they cxan be reused for the computation of |z|.
// They are zr^2 and zi^2.
let zr2 = 0
let zi2 = 0
do {
// Compute z^2 := (zr * zr - zi * zi) + (2 * zr * zi)i
const z2r = zr2 - zi2
const z2i = 2 * zr * zi
// Compute the new z := z^2 + c
zr = z2r + cr
zi = z2i + ci
// Store square powers of zr and zi to compute |z| and z^2
zr2 = zr * zr
zi2 = zi * zi
// Check the condition of Mandelbrot set |z| <= 2 more
// efficiently by comaring z^2 <= 4.
} while (zr2 + zi2 <= 4 && ++iterations < iterationThreshold)
return iterations
}
// Computes z := z^2 + c until |z| > 2. Returns the count of
// iterations run checking if the Julia set for k contains z,
// when z is a complex number.
function iterationsToEscapeJuliaSet (iterationThreshold, zr, zi, kr, ki) {
let iterations = 1
// Local variables to store parts of the computation of z^2,
// so that they cxan be reused for the computation of |z|.
// They are zr^2 and zi^2.
let zr2 = zr * zr
let zi2 = zi * zi
do {
// Compute z^2 := (zr * zr - zi * zi) + (2 * zr * zi)i
const z2r = zr2 - zi2
const z2i = 2 * zr * zi
// Compute the new z := z^2 + k
zr = z2r + kr
zi = z2i + ki
// Store square powers of zr and zi to compute |z| and z^2
zr2 = zr * zr
zi2 = zi * zi
// Check the condition of Julia set |z| <= 2 more
// efficiently by comaring z^2 <= 4.
} while (zr2 + zi2 <= 4 && ++iterations < iterationThreshold)
return iterations
}
// Computes the count of interations to escape a Julia or the Mandelbrot
// set for each complex number between [-2 - 2i] and [2 + 2i].
function computeIterations ({ iterationThreshold, width, height, offsetX, offsetY, scale, type, kr, ki }) {
// Count of all pixels to compute iterations for.
const pointCount = width * height
const message = `Computing ${pointCount} points`
console.time(message)
const iterations = new Uint8Array(pointCount)
// Keep the width/height ratio of the set by cutting the content
// of the shorter size not to span over the full [-2, 2] interval.
const squareSize = Math.max(width, height)
// Deltas to increment real and imaginary parts of the complex number by.
// They lie between -2 and 2, which makes the width 4.
const delta = 4 / squareSize / scale
// The computation starts at -2 and will continue to 2.
const startR = -2 + offsetX
let r
let i = -2 + offsetY
// Position of the beginning of the current row of pixels
// in the result array of iterations for each pixel.
let row = 0
const iterationsToEscapeSet = type === 'mandelbrot'
? iterationsToEscapeMandelbrotSet : iterationsToEscapeJuliaSet
for (let y = 0; y < height; ++y) {
// Start with the initial x-coordinate.
r = startR
for (let x = 0; x < width; ++x) {
iterations[row + x] = iterationsToEscapeSet(iterationThreshold, r, i, kr, ki)
// Advance to the next pixel to compute the iterations for.
r += delta
}
// Advance to next rows in the graph and in the array of pixels.
i += delta
row += width
}
console.timeEnd(message)
return iterations
}
function receiveRequest ({ data }) {
const { number } = data
const iterations = computeIterations(data)
const buffer = iterations.buffer
postMessage({ number, buffer }, [buffer])
}
addEventListener('message', receiveRequest)