initial commit
This commit is contained in:
		
							
								
								
									
										10
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
The Mandelbrot Set's Fractal Shape
 | 
			
		||||
==================================
 | 
			
		||||
 | 
			
		||||
I was drifting to sleep when it just hit me, as it always does: "how are fractal shapes drawn anyway"?
 | 
			
		||||
So I just looked it up and found out the wikipedia page on [The Mandelbrot set](https://en.wikipedia.org/wiki/Mandelbrot_set)
 | 
			
		||||
 | 
			
		||||
Here is a simple working rendering of the Mandelbrot set using HTML5 Canvas.
 | 
			
		||||
I wanted to write the recursion as well (zooming in indefinitely), but was not in the mood really, maybe sometime later.
 | 
			
		||||
 | 
			
		||||
[GIF](mandelbrot.gif)
 | 
			
		||||
							
								
								
									
										67
									
								
								conversion.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								conversion.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Converts an HSL color value to RGB. Conversion formula
 | 
			
		||||
 * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
 | 
			
		||||
 * Assumes h, s, and l are contained in the set [0, 1] and
 | 
			
		||||
 * returns r, g, and b in the set [0, 255].
 | 
			
		||||
 *
 | 
			
		||||
 * @param   {number}  h       The hue
 | 
			
		||||
 * @param   {number}  s       The saturation
 | 
			
		||||
 * @param   {number}  l       The lightness
 | 
			
		||||
 * @return  {Array}           The RGB representation
 | 
			
		||||
 */
 | 
			
		||||
function hslToRgb(h, s, l){
 | 
			
		||||
    var r, g, b;
 | 
			
		||||
 | 
			
		||||
    if(s == 0){
 | 
			
		||||
        r = g = b = l; // achromatic
 | 
			
		||||
    }else{
 | 
			
		||||
        var hue2rgb = function hue2rgb(p, q, t){
 | 
			
		||||
            if(t < 0) t += 1;
 | 
			
		||||
            if(t > 1) t -= 1;
 | 
			
		||||
            if(t < 1/6) return p + (q - p) * 6 * t;
 | 
			
		||||
            if(t < 1/2) return q;
 | 
			
		||||
            if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
 | 
			
		||||
            return p;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
 | 
			
		||||
        var p = 2 * l - q;
 | 
			
		||||
        r = hue2rgb(p, q, h + 1/3);
 | 
			
		||||
        g = hue2rgb(p, q, h);
 | 
			
		||||
        b = hue2rgb(p, q, h - 1/3);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Converts an RGB color value to HSL. Conversion formula
 | 
			
		||||
 * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
 | 
			
		||||
 * Assumes r, g, and b are contained in the set [0, 255] and
 | 
			
		||||
 * returns h, s, and l in the set [0, 1].
 | 
			
		||||
 *
 | 
			
		||||
 * @param   {number}  r       The red color value
 | 
			
		||||
 * @param   {number}  g       The green color value
 | 
			
		||||
 * @param   {number}  b       The blue color value
 | 
			
		||||
 * @return  {Array}           The HSL representation
 | 
			
		||||
 */
 | 
			
		||||
function rgbToHsl(r, g, b){
 | 
			
		||||
    r /= 255, g /= 255, b /= 255;
 | 
			
		||||
    var max = Math.max(r, g, b), min = Math.min(r, g, b);
 | 
			
		||||
    var h, s, l = (max + min) / 2;
 | 
			
		||||
 | 
			
		||||
    if(max == min){
 | 
			
		||||
        h = s = 0; // achromatic
 | 
			
		||||
    }else{
 | 
			
		||||
        var d = max - min;
 | 
			
		||||
        s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
 | 
			
		||||
        switch(max){
 | 
			
		||||
            case r: h = (g - b) / d + (g < b ? 6 : 0); break;
 | 
			
		||||
            case g: h = (b - r) / d + 2; break;
 | 
			
		||||
            case b: h = (r - g) / d + 4; break;
 | 
			
		||||
        }
 | 
			
		||||
        h /= 6;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return [h, s, l];
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								index.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
  <meta charset="UTF-8">
 | 
			
		||||
  <title>Mandelbrot Set</title>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
  <canvas id='board'></canvas>
 | 
			
		||||
 | 
			
		||||
  <form>
 | 
			
		||||
    <label name='max-iterations'>Maximum Iterations</label>
 | 
			
		||||
    <input id='max-iterations' type='range' min='5' max='500' value='500'>
 | 
			
		||||
 | 
			
		||||
    <label name='base-hue'>Base Hue</label>
 | 
			
		||||
    <input id='base-hue' type='range' min='0' max='255' value='0'>
 | 
			
		||||
  </form>
 | 
			
		||||
 | 
			
		||||
  <script src='conversion.js'></script>
 | 
			
		||||
  <script src='params.js'></script>
 | 
			
		||||
  <script src='lib.js'></script>
 | 
			
		||||
  <script src='main.js'></script>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										72
									
								
								lib.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								lib.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
// The Canvas Board
 | 
			
		||||
const board = document.getElementById('board');
 | 
			
		||||
const ctx = board.getContext('2d');
 | 
			
		||||
board.width = 900;
 | 
			
		||||
board.height = 500;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Complex number representation
 | 
			
		||||
class Complex {
 | 
			
		||||
  constructor(x, y) {
 | 
			
		||||
    this.x = x;
 | 
			
		||||
    this.y = y;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  multiply(other) {
 | 
			
		||||
    return new Complex(this.x * other.x - this.y * other.y,
 | 
			
		||||
                   this.x * other.y + this.y * other.x);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  add(other) {
 | 
			
		||||
    return new Complex(this.x + other.x, this.y + other.y);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  squaredAbsolute() {
 | 
			
		||||
    return this.x*this.x + this.y*this.y;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Take a number from one range to another
 | 
			
		||||
const rangeTransform = (v, oldMin, oldMax, newMin, newMax) =>
 | 
			
		||||
  (((v - oldMin) * (newMax - newMin)) / (oldMax - oldMin)) + newMin
 | 
			
		||||
 | 
			
		||||
// Get index in ImageData from coordinates
 | 
			
		||||
const coordinatesToDataIndex = (x, y) => 4 * (y * board.width + x);
 | 
			
		||||
 | 
			
		||||
// Put color in ImageData
 | 
			
		||||
const paintPixel = (image, x, y, color) => {
 | 
			
		||||
  const i = coordinatesToDataIndex(x, y);
 | 
			
		||||
 | 
			
		||||
  image.data[i] = color.r;
 | 
			
		||||
  image.data[i + 1] = color.g;
 | 
			
		||||
  image.data[i + 2] = color.b;
 | 
			
		||||
  image.data[i + 3] = color.a;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class Color {
 | 
			
		||||
  constructor(r, g, b, a) {
 | 
			
		||||
    this.r = r;
 | 
			
		||||
    this.g = g;
 | 
			
		||||
    this.b = b;
 | 
			
		||||
    this.a = a;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static random() {
 | 
			
		||||
    return new Color(Math.random() * 255, Math.random() * 255, Math.random() * 255, Math.random() * 255);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  hue(value) {
 | 
			
		||||
    const hsl = rgbToHsl(this.r, this.g, this.b);
 | 
			
		||||
    hsl[0] = value % 1;
 | 
			
		||||
    const rgb = hslToRgb.apply(null, hsl);
 | 
			
		||||
 | 
			
		||||
    this.r = rgb[0];
 | 
			
		||||
    this.g = rgb[1];
 | 
			
		||||
    this.b = rgb[2];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  clone() {
 | 
			
		||||
    return new Color(this.r, this.g, this.b, this.a);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										51
									
								
								main.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								main.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
// The mandelbrot set definition
 | 
			
		||||
const set = (c) =>
 | 
			
		||||
  (z) => z.multiply(z).add(c);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const BASE_COLOR = new Color(60, 60, 200, 255);
 | 
			
		||||
 | 
			
		||||
(function render() {
 | 
			
		||||
  requestAnimationFrame(() => {
 | 
			
		||||
    const image = ctx.createImageData(board.width, board.height);
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i < board.width; i++) {
 | 
			
		||||
      for (let j = 0; j < board.height; j++) {
 | 
			
		||||
        const scaledX = rangeTransform(i, 0, board.width, -2.5, 1);
 | 
			
		||||
        const scaledY = rangeTransform(j, 0, board.height, -1, 1);
 | 
			
		||||
 | 
			
		||||
        // constant: x_0 + iy_0
 | 
			
		||||
        const c = new Complex(scaledX, scaledY);
 | 
			
		||||
 | 
			
		||||
        // The mandelbrot set, with the constant value fixed
 | 
			
		||||
        const f = set(c);
 | 
			
		||||
        let value = new Complex(0, 0);
 | 
			
		||||
 | 
			
		||||
        let step = 0;
 | 
			
		||||
        while (value.squaredAbsolute() < 4 && step < maxIterations) {
 | 
			
		||||
          value = f(value);
 | 
			
		||||
          step++;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let color = BASE_COLOR.clone();
 | 
			
		||||
        color.hue((baseHue / 255) + step*step / maxIterations);
 | 
			
		||||
 | 
			
		||||
        paintPixel(image, i, j, color);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ctx.putImageData(image, 0, 0);
 | 
			
		||||
    
 | 
			
		||||
    render();
 | 
			
		||||
  });
 | 
			
		||||
}());
 | 
			
		||||
 | 
			
		||||
maxIterations = 0;
 | 
			
		||||
const mxi = setInterval(() => {
 | 
			
		||||
  maxIterations = Math.min(maxIterations + 10, 300);
 | 
			
		||||
  document.getElementById('max-iterations').value = maxIterations;
 | 
			
		||||
 | 
			
		||||
  if (maxIterations === 300) {
 | 
			
		||||
    clearInterval(mxi);
 | 
			
		||||
  }
 | 
			
		||||
}, 200);
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								mandelbrot.gif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								mandelbrot.gif
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 2.6 MiB  | 
							
								
								
									
										9
									
								
								params.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								params.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
let maxIterations = parseInt(document.getElementById('max-iterations').value, 10);
 | 
			
		||||
let baseHue = parseInt(document.getElementById('base-hue').value, 10);
 | 
			
		||||
 | 
			
		||||
document.getElementById('max-iterations').addEventListener('input', (e) => {
 | 
			
		||||
  maxIterations = parseInt(e.target.value, 10);
 | 
			
		||||
})
 | 
			
		||||
document.getElementById('base-hue').addEventListener('input', (e) => {
 | 
			
		||||
  baseHue = parseInt(e.target.value, 10);
 | 
			
		||||
})
 | 
			
		||||
		Reference in New Issue
	
	Block a user