Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save chooblarin/21822f8938925f4524893f66f71686a6 to your computer and use it in GitHub Desktop.
Save chooblarin/21822f8938925f4524893f66f71686a6 to your computer and use it in GitHub Desktop.
1-dimensional reaction simulation with D3.js
/*
This is replication from this article http://www.degeneratestate.org/posts/2017/May/05/turing-patterns/
*/
console.clear();
const { BehaviorSubject, interval } = rxjs;
const { take } = rxjs.operators;
const margin = { top: 20, right: 20, bottom: 40, left: 45 }
const width = 640 - margin.left - margin.right
const height = 400 - margin.top - margin.bottom
const svg = d3.select('body')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`);
const x = d3.scaleLinear().range([0, width]);
const y = d3.scaleLinear().range([0, height]);
const initialCondition = [
{
a: { x: 0.0, y: 0.1 },
b: { x: 0.0, y: 0.7 }
}
];
const xAxis = d3.axisBottom(x).ticks(5);
const yAxis = d3.axisLeft(y).ticks(5);
const dataset = new BehaviorSubject(initialCondition);
x.domain([0.0, 5.0]);
y.domain([1.0, 0.0]);
const valueLine = d3
.line()
.x(d => x(d.x))
.y(d => y(d.y));
svg
.append('g')
.attr('class', 'axis')
.attr('transform', `translate(0, ${height})`)
.call(xAxis);
svg
.append('g')
.attr('class', 'axis')
.call(yAxis);
dataset.subscribe(data => {
svg.selectAll('.line').remove();
svg.append('path')
.attr('class', 'line a')
.attr('d', valueLine(data.map(d => d.a)));
svg.append('path')
.attr('class', 'line b')
.attr('d', valueLine(data.map(d => d.b)));
});
// parameters of the equations
const alpha = 0.3;
const beta = 3.0;
const reactionA = (a, b) => (a - Math.pow(a, 3) - b + alpha);
const reactionB = (a, b) => (beta * (a - b));
const dt = 0.05;
const update = (current) => {
const last = current[current.length - 1];
const a = last.a.y;
const b = last.b.y;
const ra = reactionA(a, b);
const rb = reactionB(a, b);
const nextA = { x: last.a.x + dt, y: a + ra * dt };
const nextB = { x: last.b.x + dt, y: b + rb * dt };
const next = { a: nextA, b: nextB };
return [...current, next];
};
interval(50)
.pipe(
take(100)
)
.subscribe(n => {
const current = dataset.value;
const next = update(current);
dataset.next(next);
});
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.2.0/rxjs.umd.js"></script>
body {
font-family: system-ui, "Helvetica Neue", Helvetica, sans-serif;
font: 10px;
background-color: hsl(195, 45%, 18%);
}
svg {
margin-left: 20px;
background: hsl(195, 45%, 18%);
}
.axis line,
.axis path {
stroke: white;
}
.axis text {
fill: white;
}
.line {
stroke: hsl(72, 75%, 66%);
stroke-width: 2;
fill: none;
}
.a {
stroke: hsl(12, 75%, 66%);
}
.b {
stroke: hsl(132, 75%, 66%);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment