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