1use rustsim_geometry::vec3::Vec3;
12use rustsim_vehicle::{Vehicle, VehicleClass};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub enum ObstacleKind {
17 Pedestrian,
19 Vehicle,
21 Transit,
23 Static,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq)]
30pub struct ObstacleSnapshot {
31 pub id: u64,
33 pub kind: ObstacleKind,
35 pub center: Vec3,
37 pub radius: f64,
39 pub velocity: Vec3,
41}
42
43pub trait Obstacle {
45 fn snapshot(&self) -> ObstacleSnapshot;
47}
48
49impl Obstacle for ObstacleSnapshot {
50 #[inline]
51 fn snapshot(&self) -> ObstacleSnapshot {
52 *self
53 }
54}
55
56pub struct PedestrianObstacle<'a> {
58 pub id: u64,
60 pub pedestrian: &'a rustsim_crowd::Pedestrian,
62 pub z: f64,
64}
65
66impl<'a> PedestrianObstacle<'a> {
67 pub fn ground(id: u64, pedestrian: &'a rustsim_crowd::Pedestrian) -> Self {
69 Self {
70 id,
71 pedestrian,
72 z: 0.0,
73 }
74 }
75
76 pub fn at_z(id: u64, pedestrian: &'a rustsim_crowd::Pedestrian, z: f64) -> Self {
78 Self { id, pedestrian, z }
79 }
80}
81
82impl Obstacle for PedestrianObstacle<'_> {
83 fn snapshot(&self) -> ObstacleSnapshot {
84 ObstacleSnapshot {
85 id: self.id,
86 kind: ObstacleKind::Pedestrian,
87 center: [self.pedestrian.pos[0], self.pedestrian.pos[1], self.z],
88 radius: self.pedestrian.radius,
89 velocity: [self.pedestrian.vel[0], self.pedestrian.vel[1], 0.0],
90 }
91 }
92}
93
94pub struct VehicleObstacle<'a> {
96 pub id: u64,
98 pub vehicle: &'a Vehicle,
100 pub z: f64,
102}
103
104impl<'a> VehicleObstacle<'a> {
105 pub fn ground(id: u64, vehicle: &'a Vehicle) -> Self {
107 Self {
108 id,
109 vehicle,
110 z: 0.0,
111 }
112 }
113
114 pub fn at_z(id: u64, vehicle: &'a Vehicle, z: f64) -> Self {
116 Self { id, vehicle, z }
117 }
118}
119
120impl Obstacle for VehicleObstacle<'_> {
121 fn snapshot(&self) -> ObstacleSnapshot {
122 let (sin_heading, cos_heading) = self.vehicle.heading.sin_cos();
123 let center_x = self.vehicle.pos[0] + cos_heading * self.vehicle.length * 0.5;
124 let center_y = self.vehicle.pos[1] + sin_heading * self.vehicle.length * 0.5;
125 let radius = 0.5 * self.vehicle.length.hypot(self.vehicle.width);
126 let kind = match self.vehicle.class {
127 VehicleClass::Bus | VehicleClass::Rail => ObstacleKind::Transit,
128 _ => ObstacleKind::Vehicle,
129 };
130 ObstacleSnapshot {
131 id: self.id,
132 kind,
133 center: [center_x, center_y, self.z],
134 radius,
135 velocity: [
136 cos_heading * self.vehicle.speed,
137 sin_heading * self.vehicle.speed,
138 0.0,
139 ],
140 }
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn snapshot_is_self_describing() {
150 let s = ObstacleSnapshot {
151 id: 1,
152 kind: ObstacleKind::Vehicle,
153 center: [1.0, 2.0, 0.0],
154 radius: 1.2,
155 velocity: [3.0, 0.0, 0.0],
156 };
157 assert_eq!(s.snapshot(), s);
158 }
159
160 #[test]
161 fn adapters_project_domain_models_to_obstacles() {
162 let ped = rustsim_crowd::Pedestrian::new([1.0, 2.0], [0.5, 0.0], 0.25, 1.3, [5.0, 2.0]);
163 let snap = PedestrianObstacle::ground(7, &ped).snapshot();
164 assert_eq!(snap.kind, ObstacleKind::Pedestrian);
165 assert_eq!(snap.center, [1.0, 2.0, 0.0]);
166 assert_eq!(snap.radius, 0.25);
167
168 let mut vehicle = Vehicle::default_bus();
169 vehicle.pos = [10.0, 0.0];
170 vehicle.speed = 3.0;
171 let snap = VehicleObstacle::ground(8, &vehicle).snapshot();
172 assert_eq!(snap.kind, ObstacleKind::Transit);
173 assert!(snap.center[0] > vehicle.pos[0]);
174 assert_eq!(snap.velocity, [3.0, 0.0, 0.0]);
175 }
176}