mirror of
https://github.com/TheAlgorithms/JavaScript.git
synced 2025-07-04 15:39:42 +08:00
Add Mandelbrot
This commit is contained in:
192
Maths/Mandelbrot.js
Normal file
192
Maths/Mandelbrot.js
Normal file
@ -0,0 +1,192 @@
|
||||
/**
|
||||
* The Mandelbrot set is the set of complex numbers "c" for which the series "z_(n+1) = z_n * z_n +
|
||||
* c" does not diverge, i.e. remains bounded. Thus, a complex number "c" is a member of the
|
||||
* Mandelbrot set if, when starting with "z_0 = 0" and applying the iteration repeatedly, the
|
||||
* absolute value of "z_n" remains bounded for all "n > 0". Complex numbers can be written as "a +
|
||||
* b*i": "a" is the real component, usually drawn on the x-axis, and "b*i" is the imaginary
|
||||
* component, usually drawn on the y-axis. Most visualizations of the Mandelbrot set use a
|
||||
* color-coding to indicate after how many steps in the series the numbers outside the set cross the
|
||||
* divergence threshold. Images of the Mandelbrot set exhibit an elaborate and infinitely
|
||||
* complicated boundary that reveals progressively ever-finer recursive detail at increasing
|
||||
* magnifications, making the boundary of the Mandelbrot set a fractal curve. (description adapted
|
||||
* from https://en.wikipedia.org/wiki/Mandelbrot_set ) (see also
|
||||
* https://en.wikipedia.org/wiki/Plotting_algorithms_for_the_Mandelbrot_set )
|
||||
*/
|
||||
|
||||
/*
|
||||
Doctests
|
||||
Test black and white
|
||||
Pixel outside the Mandelbrot set should be white.
|
||||
Pixel inside the Mandelbrot set should be black.
|
||||
> getRGBData(800, 600, -0.6, 0, 3.2, 50, false)[0][0]
|
||||
[255, 255, 255]
|
||||
> getRGBData(800, 600, -0.6, 0, 3.2, 50, false)[400][300]
|
||||
[0, 0, 0]
|
||||
|
||||
Test color-coding
|
||||
Pixel distant to the Mandelbrot set should be red.
|
||||
Pixel inside the Mandelbrot set should be black.
|
||||
> getRGBData(800, 600, -0.6, 0, 3.2, 50, true)[0][0]
|
||||
[255, 0, 0]
|
||||
> getRGBData(800, 600, -0.6, 0, 3.2, 50, true)[400][300]
|
||||
[0, 0, 0]
|
||||
*/
|
||||
|
||||
/**
|
||||
* Method to generate the image of the Mandelbrot set. Two types of coordinates are used:
|
||||
* image-coordinates that refer to the pixels and figure-coordinates that refer to the complex
|
||||
* numbers inside and outside the Mandelbrot set. The figure-coordinates in the arguments of this
|
||||
* method determine which section of the Mandelbrot set is viewed. The main area of the Mandelbrot
|
||||
* set is roughly between "-1.5 < x < 0.5" and "-1 < y < 1" in the figure-coordinates.
|
||||
*
|
||||
* @param {number} imageWidth The width of the rendered image.
|
||||
* @param {number} imageHeight The height of the rendered image.
|
||||
* @param {number} figureCenterX The x-coordinate of the center of the figure.
|
||||
* @param {number} figureCenterY The y-coordinate of the center of the figure.
|
||||
* @param {number} figureWidth The width of the figure.
|
||||
* @param {number} maxStep Maximum number of steps to check for divergent behavior.
|
||||
* @param {number} useDistanceColorCoding Render in color or black and white.
|
||||
* @return {object} The RGB-data of the rendered Mandelbrot set.
|
||||
*/
|
||||
function getRGBData (
|
||||
imageWidth = 800,
|
||||
imageHeight = 600,
|
||||
figureCenterX = -0.6,
|
||||
figureCenterY = 0,
|
||||
figureWidth = 3.2,
|
||||
maxStep = 50,
|
||||
useDistanceColorCoding = true) {
|
||||
if (imageWidth <= 0) {
|
||||
throw new Error('imageWidth should be greater than zero')
|
||||
}
|
||||
|
||||
if (imageHeight <= 0) {
|
||||
throw new Error('imageHeight should be greater than zero')
|
||||
}
|
||||
|
||||
if (maxStep <= 0) {
|
||||
throw new Error('maxStep should be greater than zero')
|
||||
}
|
||||
|
||||
const rgbData = []
|
||||
const figureHeight = figureWidth / imageWidth * imageHeight
|
||||
|
||||
// loop through the image-coordinates
|
||||
for (let imageX = 0; imageX < imageWidth; imageX++) {
|
||||
rgbData[imageX] = []
|
||||
for (let imageY = 0; imageY < imageHeight; imageY++) {
|
||||
// determine the figure-coordinates based on the image-coordinates
|
||||
const figureX = figureCenterX + (imageX / imageWidth - 0.5) * figureWidth
|
||||
const figureY = figureCenterY + (imageY / imageHeight - 0.5) * figureHeight
|
||||
|
||||
const distance = getDistance(figureX, figureY, maxStep)
|
||||
|
||||
// color the corresponding pixel based on the selected coloring-function
|
||||
rgbData[imageX][imageY] =
|
||||
useDistanceColorCoding
|
||||
? colorCodedColorMap(distance)
|
||||
: blackAndWhiteColorMap(distance)
|
||||
}
|
||||
}
|
||||
|
||||
return rgbData
|
||||
}
|
||||
|
||||
/**
|
||||
* Black and white color-coding that ignores the relative distance. The Mandelbrot set is black,
|
||||
* everything else is white.
|
||||
*
|
||||
* @param {number} distance Distance until divergence threshold
|
||||
* @return {object} The RGB-value corresponding to the distance.
|
||||
*/
|
||||
function blackAndWhiteColorMap (distance) {
|
||||
return distance >= 1 ? [0, 0, 0] : [255, 255, 255]
|
||||
}
|
||||
|
||||
/**
|
||||
* Color-coding taking the relative distance into account. The Mandelbrot set is black.
|
||||
*
|
||||
* @param {number} distance Distance until divergence threshold
|
||||
* @return {object} The RGB-value corresponding to the distance.
|
||||
*/
|
||||
function colorCodedColorMap (distance) {
|
||||
if (distance >= 1) {
|
||||
return [0, 0, 0]
|
||||
} else {
|
||||
// simplified transformation of HSV to RGB
|
||||
// distance determines hue
|
||||
const hue = 360 * distance
|
||||
const saturation = 1
|
||||
const val = 255
|
||||
const hi = (Math.floor(hue / 60)) % 6
|
||||
const f = hue / 60 - Math.floor(hue / 60)
|
||||
|
||||
const v = val
|
||||
const p = 0
|
||||
const q = Math.floor(val * (1 - f * saturation))
|
||||
const t = Math.floor(val * (1 - (1 - f) * saturation))
|
||||
|
||||
switch (hi) {
|
||||
case 0:
|
||||
return [v, t, p]
|
||||
case 1:
|
||||
return [q, v, p]
|
||||
case 2:
|
||||
return [p, v, t]
|
||||
case 3:
|
||||
return [p, q, v]
|
||||
case 4:
|
||||
return [t, p, v]
|
||||
default:
|
||||
return [v, p, q]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the relative distance (ratio of steps taken to maxStep) after which the complex number
|
||||
* constituted by this x-y-pair diverges. Members of the Mandelbrot set do not diverge so their
|
||||
* distance is 1.
|
||||
*
|
||||
* @param {number} figureX The x-coordinate within the figure.
|
||||
* @param {number} figureX The y-coordinate within the figure.
|
||||
* @param {number} maxStep Maximum number of steps to check for divergent behavior.
|
||||
* @return {number} The relative distance as the ratio of steps taken to maxStep.
|
||||
*/
|
||||
function getDistance (figureX, figureY, maxStep) {
|
||||
let a = figureX
|
||||
let b = figureY
|
||||
let currentStep = 0
|
||||
for (let step = 0; step < maxStep; step++) {
|
||||
currentStep = step
|
||||
const aNew = a * a - b * b + figureX
|
||||
b = 2 * a * b + figureY
|
||||
a = aNew
|
||||
|
||||
// divergence happens for all complex number with an absolute value
|
||||
// greater than 4 (= divergence threshold)
|
||||
if (a * a + b * b > 4) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return currentStep / (maxStep - 1)
|
||||
}
|
||||
|
||||
// plot the results if the script is executed in a browser with a window-object
|
||||
if (typeof window !== 'undefined') {
|
||||
const rgbData = getRGBData()
|
||||
const width = rgbData.length
|
||||
const height = rgbData[0].length
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.width = width
|
||||
canvas.height = height
|
||||
const ctx = canvas.getContext('2d')
|
||||
for (let x = 0; x < width; x++) {
|
||||
for (let y = 0; y < height; y++) {
|
||||
const rgb = rgbData[x][y]
|
||||
ctx.fillStyle = 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')'
|
||||
ctx.fillRect(x, y, 1, 1)
|
||||
}
|
||||
}
|
||||
document.body.append(canvas)
|
||||
}
|
Reference in New Issue
Block a user