'use strict' // I made this in school and I had to 'use strict'. // I no longer use JS. class FractalTree { constructor(id, iterations, mod) { this.app = document.getElementById(id); this.canvas = this.app.getElementsByTagName("canvas")[0]; this.canvas.width = 512; this.canvas.height = 448; this.ctx = this.canvas.getContext("2d"); this.iterations = iterations; this.mod = mod; this.angle = Math.PI * 0.2; this.update = true; this.palette = ["#430", "#440", "#450", "#460", "#470", "#480", "#080"]; this.canvas.addEventListener("mousemove", this.mouseMove.bind(this)); this.canvas.addEventListener("mousedown", this.click.bind(this)); } static vector(l, angle) { return { x: Math.cos(angle) * l, y: Math.sin(angle) * l }; } tree(x, y, length, i, angle) { if (i==0) { return; } let dir = FractalTree.vector(length * this.mod, angle + this.angle); this.ctx.beginPath(); this.ctx.moveTo(x, y); this.ctx.lineWidth = i; this.ctx.strokeStyle = this.palette[this.iterations-i]; this.ctx.lineTo(x + dir.x, y - dir.y); this.ctx.stroke(); this.tree(x + dir.x, y - dir.y, length*this.mod, i-1, angle - this.angle); this.tree(x + dir.x, y - dir.y, length*this.mod, i-1, angle + this.angle); } render() { this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); this.tree(256, this.canvas.height-64, 128, this.iterations, Math.PI/2 - this.angle); } mouseMove(event) { if (this.update) { let x = event.clientX - this.canvas.getBoundingClientRect().left; this.angle = x * Math.PI / this.canvas.width; this.render(); } } click(event) { this.update = !this.update; if (this.update) { let x = event.clientX - this.canvas.getBoundingClientRect().left; this.angle = x * Math.PI / this.canvas.width; this.render(); } } setIter(i) { this.iterations = i; this.render(); } setMod(m) { this.mod = m; this.render(); } } class Mandelbrot { constructor(id, iter=255, width=512, height=512, minX=-2, minY=-1.5, maxX=1, maxY=1.5) { this.app = document.getElementById(id); this.canvas = this.app.getElementsByTagName("canvas")[0]; this.canvas.width = width; this.canvas.height = height; this.ctx = this.canvas.getContext("2d"); this.renderBtn = this.app.getElementsByClassName("controlbar")[0].getElementsByTagName("button")[0]; this.init(minX, minY, maxX, maxY); this.defaultZoom = [minX, minY, maxX, maxY]; this.iter = iter; this.palette = [16, 32, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128, 136, 144, 152, 160, 168, 176, 184, 192, 200, 208, 216, 224, 232, 240, 248]; this.canvas.addEventListener("mousedown", this.zoom.bind(this)); } zoom(event) { //zoom function let zoomf = 8; let x = this.reScaleX(event.clientX - this.canvas.getBoundingClientRect().left); let y = this.reScaleY(event.clientY - this.canvas.getBoundingClientRect().top); let dx = (this.maxX - this.minX)/zoomf; let dy = (this.maxY - this.minY)/zoomf; this.init(x - dx, y - dy, x + dx, y + dy); this.render(); } reset() { this.init(this.defaultZoom[0], this.defaultZoom[1], this.defaultZoom[2], this.defaultZoom[3]); this.render(); } setIter(x) { this.iter = x; this.render(); } init(minX, minY, maxX, maxY) { this.minX = minX; this.minY = minY; this.maxX = maxX; this.maxY = maxY; this.scaleX = (maxX - minX) / this.canvas.width; this.scaleY = (maxY - minY) / this.canvas.height; } reScaleX(x) { return x * this.scaleX + this.minX; } reScaleY(y) { return y * this.scaleY + this.minY; } point(x0, y0) { let x = 0, y = 0; let i = 0; let x2 = 0, y2 = 0; while (x2 + y2 <= 4 && i < this.iter) { y = 2*x*y + y0; x = x2 - y2 + x0; x2 = x*x; y2 = y*y; i++; } return i; } render() { let start = new Date(); let result = this.ctx.getImageData(0,0, this.canvas.width, this.canvas.height); let px = 0; let yp; for (let yPos = 0; yPos < this.canvas.height; yPos++) { yp = this.reScaleY(yPos); for (let xPos = 0; xPos < this.canvas.width; xPos++) { let i = this.point(this.reScaleX(xPos), yp); if (i != this.iter) { let col = this.palette[Math.min(i, this.palette.length-1)] result.data[px] = 0; result.data[px+1] = col; result.data[px+2] = col; } else { result.data[px] = 0; result.data[px+1] = 0; result.data[px+2] = 0; } result.data[px+3] = 255; px += 4; } } this.ctx.putImageData(result, 0, 0); this.renderBtn.textContent = `Reset ${new Date() - start}ms`; } } class Multibrot extends Mandelbrot { constructor(id, power, iter=255, width=512, height=512, minX=-2, minY=-2, maxX=2, maxY=2) { super(id, iter, width, height, minX, minY, maxX, maxY); this.power = power; } point(x0, y0) { let x = 0, y = 0; let i = 0; let xtmp, xxyyn, atn; let n2 = this.power/2; while (x*x + y*y <= 4 && i < this.iter) { xxyyn = (x*x+y*y)**(n2); atn = this.power*Math.atan2(y, x); xtmp = xxyyn * Math.cos(atn) + x0; y = xxyyn * Math.sin(atn) + y0; x = xtmp; i++; } return i; } setPower(x) { this.power = x; this.render(); } } class Julia extends Mandelbrot { constructor(id, iter=255, width=512, height=512, minX=-2, minY=-2, maxX=2, maxY=2) { super(id, iter, width, height, minX, minY, maxX, maxY); this.cx = this.reScaleX(width/2); this.cy = this.reScaleY(height/3.2); this.update = true; this.canvas.removeEventListener("mousedown", this.zoom); this.canvas.addEventListener("mousedown", this.click.bind(this)); this.canvas.addEventListener("mousemove", this.mouseMove.bind(this)); } click(event) { this.update = !this.update; this.mouseMove(event); } zoom() { return; } mouseMove(event) { if (this.update) { this.cx = this.reScaleX(event.clientX - this.canvas.getBoundingClientRect().left); this.cy = this.reScaleY(event.clientY - this.canvas.getBoundingClientRect().top); this.render(); } } point(zx, zy) { let i = 0, xtmp = 0; let zx2 = zx*zx, zy2 = zy*zy; while (zx2 + zy2 < 4 && i < this.iter) { xtmp = zx2 - zy2; zy = 2*zx*zy + this.cy; zx = xtmp + this.cx; zx2 = zx*zx; zy2 = zy*zy; i++; } return i; } } let fractalTree = new FractalTree("fractal-tree", 12, 0.75); let mandelbrot = new Mandelbrot("mandelbrot"); let multibrot = new Multibrot("multibrot", 4); let juliaSet = new Julia("julia-set", 64, 512, 512); fractalTree.render(); mandelbrot.render(); juliaSet.render();