use rustsim_geometry::vec3::Vec3;
use rustsim_vehicle::{Vehicle, VehicleClass};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ObstacleKind {
Pedestrian,
Vehicle,
Transit,
Static,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ObstacleSnapshot {
pub id: u64,
pub kind: ObstacleKind,
pub center: Vec3,
pub radius: f64,
pub velocity: Vec3,
}
pub trait Obstacle {
fn snapshot(&self) -> ObstacleSnapshot;
}
impl Obstacle for ObstacleSnapshot {
#[inline]
fn snapshot(&self) -> ObstacleSnapshot {
*self
}
}
pub struct PedestrianObstacle<'a> {
pub id: u64,
pub pedestrian: &'a rustsim_crowd::Pedestrian,
pub z: f64,
}
impl<'a> PedestrianObstacle<'a> {
pub fn ground(id: u64, pedestrian: &'a rustsim_crowd::Pedestrian) -> Self {
Self {
id,
pedestrian,
z: 0.0,
}
}
pub fn at_z(id: u64, pedestrian: &'a rustsim_crowd::Pedestrian, z: f64) -> Self {
Self { id, pedestrian, z }
}
}
impl Obstacle for PedestrianObstacle<'_> {
fn snapshot(&self) -> ObstacleSnapshot {
ObstacleSnapshot {
id: self.id,
kind: ObstacleKind::Pedestrian,
center: [self.pedestrian.pos[0], self.pedestrian.pos[1], self.z],
radius: self.pedestrian.radius,
velocity: [self.pedestrian.vel[0], self.pedestrian.vel[1], 0.0],
}
}
}
pub struct VehicleObstacle<'a> {
pub id: u64,
pub vehicle: &'a Vehicle,
pub z: f64,
}
impl<'a> VehicleObstacle<'a> {
pub fn ground(id: u64, vehicle: &'a Vehicle) -> Self {
Self {
id,
vehicle,
z: 0.0,
}
}
pub fn at_z(id: u64, vehicle: &'a Vehicle, z: f64) -> Self {
Self { id, vehicle, z }
}
}
impl Obstacle for VehicleObstacle<'_> {
fn snapshot(&self) -> ObstacleSnapshot {
let (sin_heading, cos_heading) = self.vehicle.heading.sin_cos();
let center_x = self.vehicle.pos[0] + cos_heading * self.vehicle.length * 0.5;
let center_y = self.vehicle.pos[1] + sin_heading * self.vehicle.length * 0.5;
let radius = 0.5 * self.vehicle.length.hypot(self.vehicle.width);
let kind = match self.vehicle.class {
VehicleClass::Bus | VehicleClass::Rail => ObstacleKind::Transit,
_ => ObstacleKind::Vehicle,
};
ObstacleSnapshot {
id: self.id,
kind,
center: [center_x, center_y, self.z],
radius,
velocity: [
cos_heading * self.vehicle.speed,
sin_heading * self.vehicle.speed,
0.0,
],
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn snapshot_is_self_describing() {
let s = ObstacleSnapshot {
id: 1,
kind: ObstacleKind::Vehicle,
center: [1.0, 2.0, 0.0],
radius: 1.2,
velocity: [3.0, 0.0, 0.0],
};
assert_eq!(s.snapshot(), s);
}
#[test]
fn adapters_project_domain_models_to_obstacles() {
let ped = rustsim_crowd::Pedestrian::new([1.0, 2.0], [0.5, 0.0], 0.25, 1.3, [5.0, 2.0]);
let snap = PedestrianObstacle::ground(7, &ped).snapshot();
assert_eq!(snap.kind, ObstacleKind::Pedestrian);
assert_eq!(snap.center, [1.0, 2.0, 0.0]);
assert_eq!(snap.radius, 0.25);
let mut vehicle = Vehicle::default_bus();
vehicle.pos = [10.0, 0.0];
vehicle.speed = 3.0;
let snap = VehicleObstacle::ground(8, &vehicle).snapshot();
assert_eq!(snap.kind, ObstacleKind::Transit);
assert!(snap.center[0] > vehicle.pos[0]);
assert_eq!(snap.velocity, [3.0, 0.0, 0.0]);
}
}