{
let { people, radius, infectedTime, simulationTime, speed,
stillProb, barrierHeight, fps } = params;
speed *= 60 / fps;
infectedTime = Math.round(infectedTime * fps / 60);
simulationTime = Math.round(simulationTime * fps / 60);
const sim = new AA.Simulation({
width: Math.floor(width / 10) * 10,
height: 500,
gridStep: 10,
afterTick: () => sim.pause(sim.tickIndex === simulationTime - 1)
}).vis({baseColor: 0xf2f2f2});
invalidation.then(() => sim.end());
if (barrierHeight) {
const bx = Math.ceil(sim.xMaxIndex / 2);
[
[bx, bx, 0, barrierHeight / 10 - 1],
[bx, bx, 50 - barrierHeight / 10, 49]
].forEach(indexLimits => new AA.Zone({
indexLimits,
zIndex: Infinity
}).vis({tint: 0x6666666})
.addTo(sim));
}
sim.populate({
n: people,
padding: radius,
radius,
random: true,
overlap: true,
exclude: sim.zones,
setup: (ac, i) => {
ac.still = AA.random.uniform_01() < stillProb;
ac.maxSpeed = speed;
ac.vel = AA.Vector.randomAngle(speed);
ac.state = i === 0
? {health: 'infected', recoverAt: infectedTime}
: {health: 'susceptible'};
ac.updateState = function() {
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'
]});
}
});
sim.interaction.set('people-bounce', {
group1: sim.actors,
behavior: 'bounce',
proximity: 'overlap',
log: true,
speed
});
sim.interaction.set('barrier-bounce', {
group1: sim.zones,
group2: sim.actors,
behavior: 'bounce',
proximity: 'overlap'
});
sim.interaction.set('boundary-bounce', {
group1: sim,
group2: sim.actors,
behavior: 'bounce'
});
return AV.visObs(sim, {
maxFPS: fps,
stats: true,
backParticles: true
});
}