Published
Edited
May 22, 2022
2 forks
8 stars
Insert cell
Insert cell
Insert cell
{
// parameters
let { people, radius, infectedTime, simulationTime, speed,
stillProb, barrierHeight, fps } = params;

// make speed and durations relative to 60 fps - note that there is an increased
// risk of 'tunneling' when the adjusted speed is high and/or particles are small
speed *= 60 / fps;
infectedTime = Math.round(infectedTime * fps / 60);
simulationTime = Math.round(simulationTime * fps / 60);

// create simulation
const sim = new AA.Simulation({
width: Math.floor(width / 10) * 10, // ensure a multiple of gridStep
height: 500,
gridStep: 10,
afterTick: () => sim.pause(sim.tickIndex === simulationTime - 1)
}).vis({baseColor: 0xf2f2f2});
invalidation.then(() => sim.end());

// barriers
if (barrierHeight) {
const bx = Math.ceil(sim.xMaxIndex / 2);
[
[bx, bx, 0, barrierHeight / 10 - 1], // top barrier
[bx, bx, 50 - barrierHeight / 10, 49] // bottom barrier
].forEach(indexLimits => new AA.Zone({
indexLimits,
zIndex: Infinity
}).vis({tint: 0x6666666})
.addTo(sim));
}
// people
sim.populate({
n: people,
padding: radius,
radius,
random: true, // random initial positions (rather than grid)
overlap: true, // allow initial overlaps
exclude: sim.zones, // do not start on barriers
setup: (ac, i) => {
ac.still = AA.random.uniform_01() < stillProb;
ac.maxSpeed = speed;
ac.vel = AA.Vector.randomAngle(speed);
ac.state = i === 0 // index case starts off infected, everyone else is susceptible
? {health: 'infected', recoverAt: infectedTime}
: {health: 'susceptible'};
ac.updateState = function() { // update infection status each tick
if (this.state.health === 'susceptible') {
if (this.bounceLog.find(p => p.state.health === 'infected')) {
this.state.health = 'infected';
this.state.recoverAt = sim.tickIndex + infectedTime;
}
}
else if (this.state.recoverAt === sim.tickIndex) {
this.state.health = 'recovered';
}
};
ac.vis({tint: ({state: {health: h}}) => AV.colors[
h === 'susceptible' ? 'blue' : h === 'infected' ? 'red' : 'green'
]});
}
});

// people bounce off each other
sim.interaction.set('people-bounce', {
group1: sim.actors,
behavior: 'bounce',
proximity: 'overlap', // helps reduce tunneling
log: true,
speed // constant speed like Simulitis
});

// people bounce off barriers
sim.interaction.set('barrier-bounce', {
group1: sim.zones,
group2: sim.actors,
behavior: 'bounce',
proximity: 'overlap'
});
// people bounce off boundary
sim.interaction.set('boundary-bounce', {
group1: sim,
group2: sim.actors,
behavior: 'bounce'
});

// vis
return AV.visObs(sim, {
maxFPS: fps,
stats: true,
backParticles: true
});
}
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more