use rand::Rng;
use rdpe::prelude::*;
#[derive(ParticleType, Clone, Copy, PartialEq)]
enum Health {
Healthy, Infected, Recovered, }
#[derive(Particle, Clone)]
struct Person {
position: Vec3,
velocity: Vec3,
#[color]
color: Vec3,
particle_type: u32,
}
fn main() {
let mut rng = rand::thread_rng();
let total = 3000;
let initial_infected = 5;
let particles: Vec<Person> = (0..total)
.map(|i| {
let is_infected = i < initial_infected;
let health = if is_infected {
Health::Infected
} else {
Health::Healthy
};
let pos = Vec3::new(
rng.gen_range(-0.9..0.9),
rng.gen_range(-0.9..0.9),
rng.gen_range(-0.9..0.9),
);
let vel = Vec3::new(
rng.gen_range(-0.3..0.3),
rng.gen_range(-0.3..0.3),
rng.gen_range(-0.3..0.3),
);
let color = if is_infected {
Vec3::new(1.0, 0.1, 0.1) } else {
Vec3::new(0.1, 0.9, 0.2) };
Person {
position: pos,
velocity: vel,
color,
particle_type: health.into(),
}
})
.collect();
Simulation::<Person>::new()
.with_particle_count(total as u32)
.with_bounds(1.0)
.with_spatial_config(0.1, 32)
.with_spawner(move |ctx| particles[ctx.index as usize].clone())
.with_rule(Rule::Convert {
from_type: Health::Healthy.into(),
trigger_type: Health::Infected.into(),
to_type: Health::Infected.into(),
radius: 0.08,
probability: 0.15,
})
.with_rule(Rule::Convert {
from_type: Health::Infected.into(),
trigger_type: Health::Infected.into(),
to_type: Health::Recovered.into(),
radius: 0.01, probability: 0.002,
})
.with_rule(Rule::Separate {
radius: 0.06,
strength: 1.0,
})
.with_rule(Rule::Custom(
r#"
if p.particle_type == 0u {
p.color = vec3<f32>(0.1, 0.9, 0.2); // Healthy: green
} else if p.particle_type == 1u {
p.color = vec3<f32>(1.0, 0.1, 0.1); // Infected: red
} else {
p.color = vec3<f32>(0.2, 0.4, 1.0); // Recovered: blue
}
"#
.to_string(),
))
.with_rule(Rule::Wander {
strength: 0.5,
frequency: 100.0,
})
.with_rule(Rule::SpeedLimit { min: 0.0, max: 1.0 })
.with_rule(Rule::Drag(1.0))
.with_rule(Rule::BounceWalls { restitution: 1.0 })
.run().expect("Simulation failed");
}