|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<meta charset="utf-8"> |
|
<title>Reaction-Diffusion</title> |
|
<style> |
|
body { margin: 0; } |
|
#bg { width: 100%; height: 100%; } |
|
</style> |
|
</head> |
|
<body> |
|
<script> |
|
var W = 256, H = 256, N = W*H; // Tile dimensions |
|
|
|
var doc = document.documentElement, |
|
canvas = document.createElement("canvas"), |
|
cx = canvas.getContext("2d"), |
|
tile = document.createElement("canvas"), |
|
tcx = tile.getContext("2d"); |
|
|
|
function resizeCanvas() { |
|
canvas.width = doc.clientWidth; |
|
canvas.height = doc.clientHeight; |
|
} |
|
|
|
function init() { |
|
tile.width = W; |
|
tile.height = H; |
|
|
|
window.addEventListener("resize", function() { |
|
resizeCanvas(); |
|
drawTiles(); |
|
}, false); |
|
resizeCanvas(); |
|
document.body.appendChild(canvas); |
|
|
|
initReactionDiffusion(); |
|
startReactionDiffusion(); |
|
} |
|
|
|
function drawTiles() { |
|
renderTile(); |
|
cx.fillStyle = cx.createPattern(tile, "repeat"); |
|
cx.fillRect(0, 0, canvas.width, canvas.height); |
|
} |
|
|
|
|
|
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; |
|
|
|
// Five-point stencil |
|
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> |