use rustsim::rustsim_crowd::prelude::*;
use rustsim::rustsim_crowd::social_force;
use rustsim::rustsim_crowd::threed::{step_layered, ConnectorKind, FloorTransition, LayeredSpace};
const NUM_COMMUTERS: usize = 20;
const NUM_LOITERERS: usize = 5;
const DT: f64 = 0.1;
const NUM_TICKS: usize = 500; const REPORT_EVERY: usize = 50;
fn build_space() -> LayeredSpace {
let mut space = LayeredSpace::new();
space.set_floor(0, 0.0); space.set_floor(1, 4.0); space.connectors.push(FloorTransition {
id: 1,
kind: ConnectorKind::Stair,
from_floor: 0,
from_pos: [10.0, 0.0],
to_floor: 1,
to_pos: [10.0, 0.0],
boarding_radius: 1.0,
travel_time: 10.0,
});
space
}
fn commuter(i: usize) -> Pedestrian3D {
let base = Pedestrian::new(
[0.0, (i as f64 - NUM_COMMUTERS as f64 / 2.0) * 0.6],
[0.0, 0.0],
0.25,
1.34,
[10.0, 0.0],
);
Pedestrian3D::heading_to_floor(base, 0, 1)
}
fn loiterer(i: usize) -> Pedestrian3D {
let base = Pedestrian::new(
[20.0 + i as f64 * 0.4, 0.0],
[0.0, 0.0],
0.25,
1.34,
[10.0, 0.0],
);
Pedestrian3D::grounded(base, 0)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum State {
GroundedFloor0,
GroundedFloor1,
Riding,
}
fn state_of(p: &Pedestrian3D) -> State {
if p.transition.is_some() {
State::Riding
} else if p.floor == 1 {
State::GroundedFloor1
} else {
State::GroundedFloor0
}
}
fn histogram(peds: &[Pedestrian3D]) -> (usize, usize, usize) {
let mut g0 = 0;
let mut g1 = 0;
let mut riding = 0;
for p in peds {
match state_of(p) {
State::GroundedFloor0 => g0 += 1,
State::GroundedFloor1 => g1 += 1,
State::Riding => riding += 1,
}
}
(g0, g1, riding)
}
fn main() {
let params = social_force::Params::default();
params
.validate(DT)
.expect("SFM defaults should validate at DT = 0.1 s");
let space = build_space();
let mut peds: Vec<Pedestrian3D> = (0..NUM_COMMUTERS)
.map(commuter)
.chain((0..NUM_LOITERERS).map(loiterer))
.collect();
println!("# rustsim-crowd two-floor station example");
println!("# commuters = {NUM_COMMUTERS}, loiterers = {NUM_LOITERERS}");
println!("# stair: floor 0 <-> floor 1, boarding_radius = 1.0 m, travel = 10 s");
println!("tick,t_s,grounded_f0,grounded_f1,riding");
for tick in 0..NUM_TICKS {
#[allow(deprecated)]
step_layered(&mut peds, &space, social_force::step, ¶ms, DT);
if tick % REPORT_EVERY == 0 {
let (g0, g1, r) = histogram(&peds);
println!("{tick},{:.1},{g0},{g1},{r}", tick as f64 * DT);
}
}
let (g0, g1, r) = histogram(&peds);
println!("# final: grounded_f0={g0}, grounded_f1={g1}, riding={r}");
assert_eq!(
r,
0,
"all connector transits should complete within {} s",
NUM_TICKS as f64 * DT
);
assert_eq!(
g1, NUM_COMMUTERS,
"every commuter (with target_floor = Some(1)) should end up on floor 1"
);
assert_eq!(
g0, NUM_LOITERERS,
"every loiterer (with target_floor = None) should stay grounded on floor 0 \
— if this fails, the boarding gate from blocker #8 has regressed and \
loiterers are re-boarding the stair on spatial overlap alone"
);
println!(
"# OK: {NUM_COMMUTERS}/{NUM_COMMUTERS} commuters reached floor 1, \
{NUM_LOITERERS}/{NUM_LOITERERS} loiterers stayed on floor 0"
);
}