Skip to content

Instantly share code, notes, and snippets.

@newby-jay
Forked from mbostock/.block
Last active August 29, 2015 14:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save newby-jay/b84c6aa65c08e315524e to your computer and use it in GitHub Desktop.
Save newby-jay/b84c6aa65c08e315524e to your computer and use it in GitHub Desktop.
Chemical reaction

All molecules move randomly according to Brownian motion. The 'A' molecules (light blue) create new 'A' molecules at a fixed rate per unit time. When an 'A' molecule encounters a 'B' molecule (magenta), the 'A' molecule is converted to a 'B' molecule. The 'B' molecules decay at a fixed rate per unit time. I used Quadtree to detect when two molecules, one of each type, move within range to react. It's generally fast, but does slow down when there are many reactions occuring very close together.

<style>
html, body { margin: 0; padding: 0; background: #222;}
</style>
<canvas id="canvas"></canvas>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="LV.js"></script>
(function () {
var canvas = document.getElementById("canvas"),
width = canvas.width = 960,
height = canvas.height = 500,
g = canvas.getContext("2d");
var reactionRadius2 = 2,
alpha = 0.007, // fraction of A molecules made per step
gamma = alpha/1.5, // fraction of B molecules that decay per step
randIC = function (i) {
return [width*Math.random(), height*Math.random()];
},
A = d3.range(5e3).map(randIC),
B = d3.range(1e3).map(randIC);
d3.timer(step);
function step() {
A = A.concat(
d3.range((A.length > 3e3) ? 0 : 1 + Math.round(alpha*A.length))
.map(randBirth)); // add new A molecules
B.splice(0, 1 + Math.round(gamma*B.length)); // remove decayed B molecules
if (B.length != 0) A.forEach(react()); // reaction
g.fillStyle = "rgba(0, 0, 0, .05)"; // fade old
g.fillRect(0, 0, width, height);
g.fillStyle = "#00FFFF";
A.forEach(moveDrawPoint);
g.fillStyle = "#FA58AC";
B.forEach(moveDrawPoint);
}
function randBirth(i) {
var n = Math.round((A.length - 1)*Math.random());
return [A[n][0], A[n][1]];
}
function react() {
var qt = d3.geom.quadtree()(B);
return function (r, i) {
if (Math.random()<0.3) return 0;
var rb = qt.find(r),
dx = r[0] - rb[0],
dy = r[1] - rb[1];
if (dx*dx + dy*dy < reactionRadius2) {
B.push(r);
A.splice(i, 1);
}
}
}
var rng = d3.random.normal(0, 1);
function moveDrawPoint(r) {
r[0] += rng();
r[1] += rng();
if (r[0] < 0) r[0] += width;
if (r[0] > width) r[0] -= width;
if (r[1] < 0) r[1] += height;
if (r[1] > height) r[1] -= height;
g.fillRect(r[0], r[1], 0.5, 0.5);
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment