'use strict'; import { html } from 'the-dom'; import { randomNumber, randomColor } from './utils'; const { body, create } = html(document); const content = body.find('#content'); const plotting = body.find('#plotting'); const ctx = plotting.node.getContext('2d'); const padding = 40; // padding between the canvas edges and the points let image, width, height; let lastUpdate = -Infinity; /** * Create a fractal of given width, height, based on * a polygon of given amount of vertices, using the * chaos game applied with given fraction * * @param {number} width Fractal width * @param {number} height Fractal height * @param {number} fraction Fraction to use * @param {Array} colors Color of each vertex * @return {ImageData} Generated pixel data */ const chaos = (width, height, fraction, colors) => { const cx = Math.floor(width / 2); const cy = Math.floor(height / 2); const radius = Math.min(cx, cy); const count = colors.length; const vertices = []; const angleStep = 2 * Math.PI / count; let initialAngle; // creating 0-width image data will throw an error if (width <= 0 || height <= 0) { return ctx.createImageData(1, 1); } const image = ctx.createImageData(width, height); const data = image.data; // we will rotate around an inscribed circle to calculate // the vertices' positions. We adapt the initial angle so // that usual polygons look better if (count === 3) { initialAngle = -Math.PI / 2; } else if (count === 4) { initialAngle = Math.PI / 4; } else { initialAngle = 0; } for (let i = 0; i < count; i += 1) { let current = angleStep * i + initialAngle; vertices.push([ Math.floor(Math.cos(current) * radius + cx), Math.floor(Math.sin(current) * radius + cy) ]); } // now we apply the chaos algorithm: // for any point, the next point is a `fraction` of the // distance between it and a random vertex let point = vertices[0]; let iterations = 200000; let drop = 1000; while (iterations--) { const vertexNumber = randomNumber(0, count); const vertex = vertices[vertexNumber], color = colors[vertexNumber]; point = [ Math.floor((point[0] - vertex[0]) * fraction + vertex[0]), Math.floor((point[1] - vertex[1]) * fraction + vertex[1]) ]; // skip the first 1000 points if (drop === 0) { const i = (point[1] * width + point[0]) * 4; data[i] = color[0]; data[i + 1] = color[1]; data[i + 2] = color[2]; data[i + 3] = 255; } else { drop--; } } return image; }; /** * Render the scene, recalculating the points * positions if they need to * * @return {null} */ const render = () => { // only recalculate every 16.67 ms if (+new Date() - lastUpdate > 16.67) { image = chaos( width - 2 * padding, height - 2 * padding, 1/2, [[255, 0, 0], [0, 255, 0], [0, 0, 255]] ); lastUpdate = +new Date(); } ctx.clearRect(0, 0, width, height); ctx.putImageData( image, padding, padding ); }; /** * Resize the canvas to fit the new * window size and redraw the scene * * @return {null} */ const resize = () => { width = content.node.clientWidth; height = content.node.clientHeight; plotting.setAttr('width', width); plotting.setAttr('height', height); render(); }; window.onresize = resize; resize();