119 lines
2.9 KiB
JavaScript
119 lines
2.9 KiB
JavaScript
'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;
|
|
|
|
let points = [[0, 0]];
|
|
|
|
/**
|
|
* 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 length = points.length;
|
|
const color = [0, 0, 0];
|
|
|
|
for (let i = 50; i < length; i += 1) {
|
|
const x = Math.floor(points[i][0] * zoom + center[0]);
|
|
const y = height - Math.floor(points[i][1] * zoom + center[1]);
|
|
|
|
if (x >= 0 && x < width && y >= 0 && y < height) {
|
|
const index = (y * width + x) * 4;
|
|
|
|
image.data[index] = color[0];
|
|
image.data[index + 1] = color[1];
|
|
image.data[index + 2] = color[2];
|
|
image.data[index + 3] = 255;
|
|
}
|
|
}
|
|
|
|
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 * Math.max(0, 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();
|
|
}
|
|
});
|
|
|
|
applyChaos(points, 200000, ...barnsley);
|
|
window.onresize = resize;
|
|
resize();
|