From c973cb1aec19ccae9a623528d749d53bfa98cd01 Mon Sep 17 00:00:00 2001 From: CrispyPin Date: Sat, 26 Jun 2021 21:49:40 +0200 Subject: [PATCH] add fractal page from old site --- docs/fractals/fractals.js | 303 ++++++++++++++++++++++++++++++ docs/fractals/index.html | 42 ++++- docs/libraries/gpu-browser.min.js | 14 ++ docs/style.css | 15 +- docs/style.css.map | 2 +- docs/style.scss | 18 +- pages/fractals/index.html | 42 ++++- 7 files changed, 417 insertions(+), 19 deletions(-) create mode 100644 docs/fractals/fractals.js create mode 100644 docs/libraries/gpu-browser.min.js diff --git a/docs/fractals/fractals.js b/docs/fractals/fractals.js new file mode 100644 index 0000000..942414f --- /dev/null +++ b/docs/fractals/fractals.js @@ -0,0 +1,303 @@ +'use strict' + +const gpu = new GPU(); + +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.renderBtn2 = this.app.getElementsByClassName("controlbar")[0].getElementsByTagName("button")[1]; + + 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`; + } + + gpuRender() { + let start = new Date(); + + let kernel = gpu.createKernel(function(scaleX, scaleY, minX, minY, iter) { + let x0 = this.thread.x * scaleX + minX; + let y0 = this.thread.y * scaleY + minY; + let x = 0, y = 0; + let i = 0; + let x2 = 0, y2 = 0; + + while (x2 + y2 < 4 && i < iter) { + y = 2*x*y + y0; + x = x2 - y2 + x0; + x2 = x*x; + y2 = y*y; + i++; + } + if (i != iter) { + this.color(0, i/iter, i/iter, 1); + this.color(0, i/255, i/255, 1);//i is 0? + this.color(this.thread.x/512, this.thread.y/512, i/255); + + } else { + this.color(1, 0, 0, 1); + } + //this.color(this.thread.x/512, this.thread.y/512, i/256); + //this.color(x0, y0, 0, 1) + }, + {output:[512, 512], graphical:true, loopMaxIterations: this.iter}); + + kernel(this.scaleX, this.scaleY, this.minX, this.minY, this.iter); + + //let kernel = gpu.createKernel(function() {this.color(0, 0, 1);}).setOutput([512, 512]).setGraphical(true); + //kernel(); + + let result = kernel.getPixels(); + this.ctx.putImageData(new ImageData(result, 512, 512), 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(); \ No newline at end of file diff --git a/docs/fractals/index.html b/docs/fractals/index.html index 0c833a2..29b47b5 100644 --- a/docs/fractals/index.html +++ b/docs/fractals/index.html @@ -22,7 +22,47 @@

Fractals

-

this is real content i promise

+

Fractal tree

+

This is a fractal tree.

+

The tree splits into two parts, with an angle defined by the cursor's x positon. The number of iterations is defined by the first input field (which defaults to 12). Each branch is slightly smaller than its parent, as defined by the second input field. Click the canvas to freeze the image at any moment.

+
+ +
+ + +
+
+

Mandelbrot

+

The mandelbrot set is defined as the set of complex numbers for which a specific function (Fc(z) = z2 + c) stays within distance 2 from the origin when iterated from z=0.

+
+ +
+ + + +
+
+

Multibrot set

+

A multibrot set is a variant of the mandelbrot set where the functions exponent can be anything. If the exponent (third input field) is 2, you get the regular mandelbrot set.

+
+ +
+ + + +
+
+

Julia set

+

The julia set is similar to the mandelbrot set.

+
+ +
+ + +
+
+ +