This is a close-up view of 6cfcbff910216ee3abf8.
It is again necessary to be patient.
This is a close-up view of 6cfcbff910216ee3abf8.
It is again necessary to be patient.
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Reaction-Diffusion</title> | |
<style> | |
body { margin: auto; width: 960px; height: 500px; overflow: hidden; } | |
#bg { width: 100%; height: 100%; } | |
</style> | |
</head> | |
<body> | |
<script> | |
var W = 120, H = 63, N = W*H, M = 8; // Tile dimensions | |
var doc = document.documentElement, | |
canvas = document.createElement("canvas"), | |
cx = canvas.getContext("2d"), | |
tile = document.createElement("canvas"), | |
tcx = tile.getContext("2d"); | |
function init() { | |
tile.width = W; | |
tile.height = H; | |
canvas.width = W * M; | |
canvas.height = H * M; | |
document.body.appendChild(canvas); | |
initReactionDiffusion(); | |
startReactionDiffusion(); | |
} | |
function drawTiles() { | |
renderTile(); | |
// This draws it blurrily: | |
// cx.drawImage(tile, 0, 0, W*M, H*M); | |
var timd = tcx.getImageData(0, 0, W, H), | |
td = timd.data, | |
imd = cx.createImageData(W * M, H * M), | |
d = imd.data; | |
for (var y=0; y<H*M; y++) { | |
for (var x=0; x<W*M; x++) { | |
var i = y*(W*M) + x, | |
j = ((y / M)|0)*W + ((x / M)|0); | |
d[4*i + 0] = td[4*j + 0]; | |
d[4*i + 1] = td[4*j + 1]; | |
d[4*i + 2] = td[4*j + 2]; | |
d[4*i + 3] = td[4*j + 3]; | |
} | |
} | |
cx.putImageData(imd, 0, 0); | |
} | |
var α, β, af, α1, β1, α2, β2, α3, β3, α4, β4, α_temp, β_temp; | |
function initReactionDiffusion() { | |
α = []; β = []; | |
for (var i=0; i<N; i++) { | |
α[i] = Math.random() * 100 - 50; | |
β[i] = Math.random() * 100 - 50; | |
} | |
α1 = []; α2 = []; α3 = []; α4 = []; | |
β1 = []; β2 = []; β3 = []; β4 = []; | |
α_temp = []; β_temp = []; | |
} | |
function frame() { | |
rk4(f, 0.1); | |
drawTiles(); | |
af = requestAnimationFrame(frame); | |
} | |
function startReactionDiffusion() { | |
af = requestAnimationFrame(frame); | |
} | |
function renderTile() { | |
var imd = tcx.createImageData(W, H), | |
d = imd.data; | |
for (var i=0; i<N; i++) { | |
var v = ((α[i] + 2) * 25.5)|0; | |
d[4*i + 0] = v; | |
d[4*i + 1] = v/2; | |
d[4*i + 2] = v/2; | |
d[4*i + 3] = 0xFF; | |
} | |
tcx.putImageData(imd, 0, 0); | |
} | |
var clampMin = -10, | |
clampMax = 10, | |
D_a = 0.001, | |
D_b = 0.004, | |
abClampMin = -50, | |
abClampMax = 50; | |
function f(α_in, β_in, c, δα, δβ, δα_out, δβ_out) { | |
var α, β; | |
if (c == 0) { | |
α = α_in; | |
β = β_in; | |
} else { | |
α = α_temp; | |
β = β_temp; | |
for (var i=0; i<N; i++) { | |
α[i] = α_in[i] + c*δα[i]; | |
β[i] = β_in[i] + c*δβ[i]; | |
} | |
} | |
for (var y=0; y<H; y++) { | |
for (var x=0; x<W; x++) { | |
var i = W*y + x, | |
px = (x+W-1)%W, sx = (x+1)%W, | |
py = (y+H-1)%H, sy = (y+1)%H; | |
var laplacian_a = α[W*py+x] + α[W*y+sx] + α[W*sy+x] + α[W*y+px] - 4*α[i], | |
laplacian_b = β[W*py+x] + β[W*y+sx] + β[W*sy+x] + β[W*y+px] - 4*β[i]; | |
δα_out[i] = Math.max(clampMin, Math.min(clampMax, (D_a * (α[i]*α[i] - β[i]*β[i])) + laplacian_b)); | |
δβ_out[i] = Math.max(clampMin, Math.min(clampMax, (D_b * (2*α[i]*β[i])) - laplacian_a)); | |
} | |
} | |
} | |
function rk4(f, h) { | |
f(α, β, 0, null, null, α1, β1); | |
f(α, β, h/2, α1, β1, α2, β2); | |
f(α, β, h/2, α2, β2, α3, β3); | |
f(α, β, h, α3, β3, α4, β4); | |
for (var i=0; i<N; i++) { | |
α[i] = Math.max(abClampMin, Math.min(abClampMax, α[i] + (h/6) * (α1[i] + 2*α2[i] + 2*α3[i] + α4[i]) )); | |
β[i] = Math.max(abClampMin, Math.min(abClampMax, β[i] + (h/6) * (β1[i] + 2*β2[i] + 2*β3[i] + β4[i]) )); | |
} | |
} | |
init(); | |
</script> | |
</body> | |
</html> |