'use strict'; import { html } from 'the-dom'; import { applyChaos } from './chaos'; import { barnsley } from './ifs'; const { body } = html(document); const content = body.find('#content'); const plotting = body.find('#plotting').node; const ctx = plotting.getContext('2d'); let dragging = false; let center, zoom = 200; let width, height; /** * Re-render the scene from scratch * * @return {null} */ const render = () => { plotting.width = width; plotting.height = height; // do not plot (very) small sizes if (width < 1) { return; } // do the chaos game const image = ctx.getImageData(0, 0, width, height); const color = [0, 0, 0]; let skip = 50; applyChaos(image, [0, 0], 500000, ...barnsley, point => { const x = Math.floor(point[0] * zoom + center[0]); const y = height - Math.floor(point[1] * zoom + center[1]); if (x >= 0 && x < width && y >= 0 && y < height && skip <= 0) { const i = (y * width + x) * 4; image.data[i] = color[0]; image.data[i + 1] = color[1]; image.data[i + 2] = color[2]; image.data[i + 3] = 255; } skip -= 1; }); ctx.putImageData(image, 0, 0); }; /** * Update the scene when the window has been resized * * @return {null} */ const resize = () => { width = content.node.clientWidth; height = content.node.clientHeight; center = [Math.floor(width / 2), Math.floor(height / 2)]; render(); }; /** * Zoom on the cursor position when using mouse wheel */ content.on('wheel', event => { const delta = event.deltaMode === 0 ? event.deltaY / 53 : event.deltaY; const newZoom = zoom * (1 - delta * .035); // which (unprojected) point does the mouse point on? const mouse = [ (event.offsetX - center[0]) / zoom, (height - event.offsetY - center[1]) / zoom ]; // we need to set the center so that `mouse` stays at // the same position on screen, i.e (vectorially): // mouse * newZoom + newCenter = mouse * zoom + center // => newCenter = mouse * zoom - mouse * newZoom + center center = [ mouse[0] * zoom - mouse[0] * newZoom + center[0], mouse[1] * zoom - mouse[1] * newZoom + center[1] ]; zoom = newZoom; render(); event.preventDefault(); }); /** * Pan the content with click-drag action */ content.on('mousedown', event => dragging = [event.offsetX, event.offsetY]); content.on('mouseup', () => dragging = false); content.on('mousemove', event => { if (dragging !== false) { const newMouse = [event.offsetX, event.offsetY]; const movement = [newMouse[0] - dragging[0], newMouse[1] - dragging[1]]; center[0] += movement[0]; center[1] -= movement[1]; render(); dragging = newMouse; event.preventDefault(); } }); window.onresize = resize; resize();