|
|
@ -2,6 +2,7 @@ |
|
|
|
|
|
|
|
import { html } from 'the-dom'; |
|
|
|
import { applyChaos } from './chaos'; |
|
|
|
import { barnsley } from './ifs'; |
|
|
|
|
|
|
|
const { body } = html(document); |
|
|
|
|
|
|
@ -9,22 +10,18 @@ const content = body.find('#content'); |
|
|
|
const plotting = body.find('#plotting').node; |
|
|
|
const ctx = plotting.getContext('2d'); |
|
|
|
|
|
|
|
const padding = 40; // padding between the canvas edges and the points
|
|
|
|
let dragging = false; |
|
|
|
let center, zoom = 200; |
|
|
|
let width, height; |
|
|
|
|
|
|
|
const linearTransform = (a, b, c, d, e, f) => point => [ |
|
|
|
a * point[0] + b * point[1] + e, |
|
|
|
c * point[0] + d * point[1] + f |
|
|
|
]; |
|
|
|
|
|
|
|
/** |
|
|
|
* Re-render the scene from scratch |
|
|
|
* |
|
|
|
* @return {null} |
|
|
|
*/ |
|
|
|
const render = () => { |
|
|
|
plotting.width = width + 2 * padding; |
|
|
|
plotting.height = height + 2 * padding; |
|
|
|
plotting.width = width; |
|
|
|
plotting.height = height; |
|
|
|
|
|
|
|
// do not plot (very) small sizes
|
|
|
|
if (width < 1) { |
|
|
@ -32,16 +29,27 @@ const render = () => { |
|
|
|
} |
|
|
|
|
|
|
|
// do the chaos game
|
|
|
|
const image = ctx.getImageData(padding, padding, width, height); |
|
|
|
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; |
|
|
|
|
|
|
|
applyChaos(image, [0, 0], 500000, [ |
|
|
|
linearTransform(0, 0, 0, 0.16, 0, 0), |
|
|
|
linearTransform(.85, .04, -.04, .85, 0, 1.6), |
|
|
|
linearTransform(.20, -.26, .23, .22, 0, 1.6), |
|
|
|
linearTransform(-.15, .28, .26, .24, 0, .44) |
|
|
|
], [.01, .85, .07, .07]); |
|
|
|
image.data[i] = color[0]; |
|
|
|
image.data[i + 1] = color[1]; |
|
|
|
image.data[i + 2] = color[2]; |
|
|
|
image.data[i + 3] = 255; |
|
|
|
} |
|
|
|
|
|
|
|
ctx.putImageData(image, padding, padding); |
|
|
|
skip -= 1; |
|
|
|
}); |
|
|
|
|
|
|
|
ctx.putImageData(image, 0, 0); |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
@ -50,11 +58,60 @@ const render = () => { |
|
|
|
* @return {null} |
|
|
|
*/ |
|
|
|
const resize = () => { |
|
|
|
width = content.node.clientWidth - 2 * padding; |
|
|
|
height = content.node.clientHeight - 2 * padding; |
|
|
|
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(); |
|
|
|