<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link rel="stylesheet" href="normalize.css">
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<script >
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
window.canvasWidth = canvas.width
window.canvasHeight = canvas.height
window.canvasPixelsAmount = canvasWidth * canvasHeight
window.particles = new Float64Array(new ArrayBuffer(canvasPixelsAmount * 8))
// window.particles[10] = 100000;
canvas.addEventListener('click', (event) => {
particles[event.clientY * canvasWidth + event.clientX] += 1000000;
})
draw()
function draw() {
// console.time('draw')
let image = ctx.createImageData(canvasWidth, canvasHeight);
if (image.data.length / 4 !== canvasPixelsAmount) {
throw new Error('Image doesn't fit particles.');
}
particles.forEach((energy, energyIdx) => {
let roundedEnergy = ~~energy
let pixelIdx = energyIdx * 4;
// Red
image.data[pixelIdx] = 0;
// image.data[pixelIdx] = Math.min(255, roundedEnergy);
// Green
image.data[pixelIdx + 1] = roundedEnergy < 256 ? 0 : Math.min(256 * 2, roundedEnergy) - 256;
// Blue
image.data[pixelIdx + 2] = roundedEnergy < 256 * 2 ? 0 : Math.min(256 * 3, roundedEnergy) - 256 * 2;
image.data[pixelIdx + 3] = 255;
})
ctx.putImageData(image, 0, 0);
// console.timeEnd('draw')
requestAnimationFrame(draw)
}
</script>
<script>
const speedFactor = 0.5
const neighborsOffsets = [
-1, 1, -canvasWidth, canvasWidth,
-2, +2, -canvasWidth - 1, -canvasWidth + 1, canvasWidth - 1, canvasWidth + 1, 2 * canvasWidth, -2 * canvasWidth,
];
let prevTimestamp = 0
function work(timestamp) {
console.time('simple')
let energyTransferIndex = 8;
if (timestamp) {
let interval = timestamp - prevTimestamp;
if (!interval) {
requestAnimationFrame(work);
return;
}
energyTransferIndex = (8 / (100 / interval)) / speedFactor;
prevTimestamp = timestamp
} else {
requestAnimationFrame(work);
return;
}
let newParticles = new Float64Array(new ArrayBuffer(canvasPixelsAmount * 8));
let calcEnergyTransition = (energy, distanceCoefficient) => {
return energy / distanceCoefficient / energyTransferIndex;
}
particles.forEach((energy, particleIdx) => {
let energyModification = 0
neighborsOffsets.forEach((offset, offsetIdx) => {
let neighborIdx = particleIdx + offset
let distanceCoefficient = ~~(offsetIdx / 4) + 1;
if (particleIdx === 1) {
}
if (neighborIdx > 1 && neighborIdx < canvasPixelsAmount) {
if (energy < particles[neighborIdx]) {
energyModification += calcEnergyTransition(particles[neighborIdx], distanceCoefficient);
} else if (energy > particles[neighborIdx]) {
energyModification -= calcEnergyTransition(energy, distanceCoefficient);
}
} else {
energyModification -= calcEnergyTransition(energy, distanceCoefficient);
}
})
newParticles[particleIdx] = energy + energyModification;
});
particles = newParticles
console.timeEnd('simple')
// requestAnimationFrame(work)
}
// console.time('simple')
requestAnimationFrame(work);
// console.timeEnd('simple')
// console.time('simple')
// work()
// console.timeEnd('simple')
</script>
</body>
</html>