The Mandelbrot Set

Sun, 11/27/2022 - 13:20

A port of the Mandelbrot set: https://en.wikipedia.org/wiki/Mandelbrot_set

I coded a slow version of this in Game Maker a long time ago. I never colored it and it was very slow.

 

 

 /** 
 * https://en.wikipedia.org/wiki/Mandelbrot_set
 * Ported from Coding Train Lesson #21
 * https://www.youtube.com/watch?v=6z7GQewK-Ks&list=PLRqwX-V7Uu6ZiZxtDDRCi6uhfTH4FilpH&index=25
 * */
const WH = 700
window.onload = function() {

    'use strict';

	const m = new Mandelbrot()

	document.getElementById('min').addEventListener('change',function() {
		document.getElementById('minv').innerHTML = this.value
		m.mnzoom = ~~this.value
		m.loop()
	})
	document.getElementById('max').addEventListener('change',function() {
		document.getElementById('maxv').innerHTML = this.value
		m.mxzoom = ~~this.value
		m.loop()
	})
	
	setTimeout(()=>{m.loop()},100)

}
class Mandelbrot {
	constructor() {
		this.max = 100
		this.infinity = 16
		this.tau = Math.PI*2
		this.mnzoom = ~~document.getElementById('min').value
		this.mxzoom = ~~document.getElementById('max').value
		this.pixels = []

		// create the canvas
	    let c = document.createElement('canvas')
	    c.setAttribute('hidpi','no')
	    c.setAttribute("width",WH)
	    c.setAttribute("height",WH)
	    document.getElementById('target').appendChild(c)
	    c.width = c.offsetWidth
	    c.height = c.offsetHeight
	    this.ctx = c.getContext("2d")
	    
	}
	/** 
	 * @method loop
	 * */
	loop()
	{
		const t2 = performance.now()
		for(let x=1;x<WH;x++) {
			for(let y=1;y<WH;y++) {
				let a = this.map(x, 0, WH, this.mnzoom, this.mxzoom)
				let b = this.map(y, 0, WH, this.mnzoom, this.mxzoom)
				let r = this.inSet(a, b)
				if(r === true) {
					this.rectangle(x,y,x,y,'#000000')
				}else{
					r = this.componentToHex(r)
					this.rectangle(x,y,x,y,this.rgbToHex((r * 2) % 255,(r * 3) % 255,(r * 4) % 255))
				}
			}
		}	
		const t3 = performance.now()
		console.log(`Call took ${t3 - t2} milliseconds.`)
	}
	/** 
	 * @method inSet
	 * @param {Array} a
	 * @param {Array} b
	 * @return {Mixed}
	 * */
	inSet(a,b)
	{
		let n = 0, z = 0, aorigin = a, borigin = b
		while(n < this.max)
		{
			let aa = a * a - b * b 
			let bb = 2 * a * b 
			a = aa + aorigin
			b = bb + borigin
			if(Math.abs(a + b) > this.infinity) {
				return (Math.abs(n * this.infinity) % 255)
			}
			n++
		}
		return true
	}
	/** 
	 * @method map
	 * @param  {Number} value  the incoming value to be converted
	 * @param  {Number} start1 lower bound of the value's current range
	 * @param  {Number} stop1  upper bound of the value's current range
	 * @param  {Number} start2 lower bound of the value's target range
	 * @param  {Number} stop2  upper bound of the value's target range
	 * @param  {Boolean} [withinBounds] constrain the value to the newly mapped range
	 * @return {Number}        remapped number
	* */
	map (n, start1, stop1, start2, stop2, withinBounds) {
	  var newval = (n - start1) / (stop1 - start1) * (stop2 - start2) + start2;
	  if (!withinBounds) {
	    return newval;
	  }
	  if (start2 < stop2) {
	    return this.constrain(newval, start2, stop2);
	  } else {
	    return this.constrain(newval, stop2, start2);
	  }
	}
	/** 
	 * @method constrain
	 * @param  {Number} n    number to constrain
	 * @param  {Number} low  minimum limit
	 * @param  {Number} high maximum limit
	 * @return {Number}      constrained number
	 * */
	constrain (n, low, high) {
	  return Math.max(Math.min(n, high), low);
	}
	/** 
	 * @method makePixel
	 * @param {Number}
	 * @param {Number}
	 * @param {String}
	 * @return {Object}
	 * */
	makePixel(x,y,c)
	{
		return {
			x:x,
			y:y,
			c:c
		}
	}
	/** 
	 * @method rectangle
	 * @param {Number}
	 * @param {Number}
	 * @param {Number}
	 * @param {Number}
	 * @param {String}
	 * */
	rectangle(x1,y1,x2,y2,c) 
	{
	    x1 = ~~x1
	    y1 = ~~y1
	    x2 = ~~x2
	    y2 = ~~y2
	    
	    this.ctx.fillStyle=c
	    this.ctx.fillRect(x1,y1,x2,y2)
	}
	/** 
	 * @method clear
	 * */
	clear()
	{
		this.ctx.save()
	    this.ctx.setTransform(1, 0, 0, 1, 0, 0)
	    this.ctx.clearRect(0, 0, WH, WH)
	    this.ctx.restore()
	}
	/**
	 * @method componentToHex
	 * returns a color hex value from an integer
	 * @param {Number}
	 * @return {String}
	 * */
	componentToHex(c) 
	{
	    c = ~~c
	    var hex = c.toString(16)
	    return hex.length == 1 ? "0" + hex : hex
	}
	/**
	 * returns a color hex value from an 3 integers
	 * @param {Number}
	 * @param {Number}
	 * @param {Number}
	 * @return {String}
	 * */
	rgbToHex(r, g, b) 
	{
	    return "#" + this.componentToHex(r) + this.componentToHex(g) + this.componentToHex(b)
	}
}
Categories