traffic_sim/
simulation.rs

1use crate::conflict::ConflictPoint;
2use crate::debug::take_debug_frame;
3use crate::light::TrafficLight;
4use crate::link::{Link, LinkAttributes, TrafficControl};
5use crate::math::CubicFn;
6use crate::vehicle::{LaneChange, Vehicle, VehicleAttributes};
7use crate::{LinkGroup, LinkId, LinkSet, TrafficLightId, VehicleId, VehicleSet};
8use serde::{Deserialize, Serialize};
9use slotmap::SlotMap;
10use std::rc::Rc;
11
12/// A traffic simulation.
13#[derive(Default, Clone, Serialize, Deserialize)]
14pub struct Simulation {
15    /// The links in the network.
16    links: LinkSet,
17    /// The traffic lights.
18    lights: SlotMap<TrafficLightId, TrafficLight>,
19    /// The vehicles being simulated.
20    vehicles: VehicleSet,
21    /// The conflict points.
22    conflicts: Vec<ConflictPoint>,
23    /// The set of "frozen" vehicles, which will not move.
24    frozen_vehs: Vec<VehicleId>,
25    /// The current frame of simulation.
26    frame: usize,
27    /// The next sequence number.
28    seq: usize,
29    /// Debugging information from the previously simulated frame.
30    debug: serde_json::Value,
31}
32
33impl Simulation {
34    /// Creates a new simulation.
35    pub fn new() -> Self {
36        Default::default()
37    }
38
39    /// Adds a link to the network.
40    pub fn add_link(&mut self, attributes: &LinkAttributes) -> LinkId {
41        self.links.insert_with_key(|id| Link::new(id, attributes))
42    }
43
44    /// Specifies that these vehicles on these links may
45    /// interact with vehicles on other links in the group.
46    pub fn add_link_group(&mut self, link_ids: &[LinkId]) {
47        let links = link_ids
48            .iter()
49            .map(|id| &self.links[*id])
50            .collect::<Vec<_>>();
51        let group = Rc::new(LinkGroup::new(&links));
52        for id in link_ids {
53            self.links[*id].set_group(group.clone());
54        }
55    }
56
57    /// Permits vehicles to lane change from the `from` link to the `to` link.
58    /// Both links must belong to the same [LinkGroup].
59    pub fn add_lane_change(&mut self, from: LinkId, to: LinkId) {
60        self.links[from].add_lane_change(to);
61    }
62
63    /// Specifies that two links may converge or cross.
64    pub fn add_link_convergance(&mut self, a: LinkId, b: LinkId) {
65        if let Some([a, b]) = self.links.get_disjoint_mut([a, b]) {
66            if let Some(conflict_point) = ConflictPoint::new(a, b) {
67                for (link_id, link_conflict) in conflict_point.link_conflicts() {
68                    self.links[link_id].add_conflict(link_conflict);
69                }
70                self.conflicts.push(conflict_point);
71            }
72        }
73    }
74
75    /// Specifies that the end of the `from` link connects to the start of the `to` link.
76    pub fn add_link_connection(&mut self, from: LinkId, to: LinkId) {
77        self.links[from].add_link_out(to);
78        self.links[to].add_link_in(from);
79    }
80
81    /// Adds a traffic light to the simulation.
82    pub fn add_traffic_light(&mut self, light: TrafficLight) -> TrafficLightId {
83        self.lights.insert(light)
84    }
85
86    /// Adds a vehicle to the simulation.
87    pub fn add_vehicle(&mut self, attributes: &VehicleAttributes, link: LinkId) -> VehicleId {
88        let vehicle_id = self.vehicles.insert_with_key(|id| {
89            let mut vehicle = Vehicle::new(id, attributes);
90            vehicle.set_location(link, 0.0, None);
91            vehicle.update_coords(&self.links);
92            vehicle
93        });
94        self.links[link].insert_vehicle(&self.vehicles, vehicle_id);
95        vehicle_id
96    }
97
98    /// Sets the `frozen` attribute of a vehicle. When a vehicle is frozen,
99    /// it will maximally decelerate until its velocity is zero and remain stopped
100    /// until it is no longer frozen.
101    pub fn set_vehicle_frozen(&mut self, vehicle_id: VehicleId, frozen: bool) {
102        let idx = self.frozen_vehs.iter().position(|id| *id == vehicle_id);
103        match (frozen, idx) {
104            (true, None) => {
105                self.frozen_vehs.push(vehicle_id);
106            }
107            (false, Some(idx)) => {
108                self.frozen_vehs.remove(idx);
109            }
110            _ => {}
111        }
112    }
113
114    /// Gets the `frozen` attribute of a vehicle. [Read more](Self::set_vehicle_frozen).
115    pub fn get_vehicle_frozen(&mut self, vehicle_id: VehicleId) -> bool {
116        self.frozen_vehs.iter().any(|id| *id == vehicle_id)
117    }
118
119    /// Advances the simulation by `dt` seconds.
120    ///
121    /// For a realistic simulation, do not use a time step greater than around 0.2.
122    pub fn step(&mut self, dt: f64) {
123        self.update_lights(dt);
124        self.apply_accelerations();
125        self.integrate(dt);
126        self.advance_vehicles();
127        self.update_vehicle_coords();
128        self.take_debug_frame();
129        self.frame += 1;
130    }
131
132    /// Advances the simulation by `dt` seconds, but only integrates vehicles positions,
133    /// and doesn't recalculate more expensive aspects of the simulation.
134    ///
135    /// Use this to achieve better performance while retaining a smooth animation frame rate,
136    /// though beware that simulating too many consecutive frames with this method will result
137    /// in a noticeable degradation in simulation quality and realism.
138    pub fn step_fast(&mut self, dt: f64) {
139        self.update_lights(dt);
140        self.integrate(dt);
141        self.advance_vehicles();
142        self.update_vehicle_coords();
143        self.take_debug_frame();
144        self.frame += 1;
145    }
146
147    /// Gets the current simulation frame index.
148    pub fn frame(&self) -> usize {
149        self.frame
150    }
151
152    /// Returns an iterator over all the links in the simulation.
153    pub fn iter_links(&self) -> impl Iterator<Item = &Link> {
154        self.links.iter().map(|(_, link)| link)
155    }
156
157    /// Returns an iterator over all the vehicles in the simulation.
158    pub fn iter_vehicles(&self) -> impl Iterator<Item = &Vehicle> {
159        self.vehicles.iter().map(|(_, veh)| veh)
160    }
161
162    /// Gets a reference to the vehicle with the given ID.
163    pub fn get_vehicle(&self, vehicle_id: VehicleId) -> &Vehicle {
164        &self.vehicles[vehicle_id]
165    }
166
167    /// Gets a reference to the link with the given ID.
168    pub fn get_link(&self, link_id: LinkId) -> &Link {
169        &self.links[link_id]
170    }
171
172    /// Sets the [TrafficControl] at the start of the given link.
173    pub fn set_link_control(&mut self, link_id: LinkId, control: TrafficControl) {
174        self.links[link_id].set_control(control);
175    }
176
177    /// Gets the debugging information for the previously simulated frame as JSON array.
178    pub fn debug(&mut self) -> serde_json::Value {
179        self.debug.take()
180    }
181
182    /// Takes the debugging information from the global buffer.
183    fn take_debug_frame(&mut self) {
184        self.debug = take_debug_frame();
185    }
186
187    /// Updates the traffic lights.
188    fn update_lights(&mut self, dt: f64) {
189        for (_, light) in &mut self.lights {
190            light.step(dt);
191            for (link_id, control) in light.get_states() {
192                self.links[link_id].set_control(control);
193            }
194        }
195    }
196
197    /// Calculates the accelerations of the vehicles.
198    fn apply_accelerations(&mut self) {
199        for (_, link) in &mut self.links {
200            link.deactivate();
201        }
202        for (_, vehicle) in &mut self.vehicles {
203            vehicle.reset();
204            vehicle.activate_links(&mut self.links);
205        }
206        self.apply_stoplines();
207        self.apply_link_accelerations();
208        self.apply_frozen_vehicles();
209    }
210
211    /// Applies accelerations to stop vehicles before stop lines,
212    /// and allows vehicles to enter links.
213    fn apply_stoplines(&self) {
214        for (_, link) in &self.links {
215            link.apply_stoplines(&self.links, &self.vehicles);
216        }
217    }
218
219    /// Applies the car following model, speed limits, etc. to all vehicles.
220    fn apply_link_accelerations(&mut self) {
221        for (_, link) in &self.links {
222            link.apply_accelerations(&self.links, &self.vehicles);
223        }
224        for conflict in &self.conflicts {
225            conflict.apply_accelerations(&self.links, &self.vehicles);
226        }
227    }
228
229    /// Applies a large negative acceleration to all frozen vehicles.
230    fn apply_frozen_vehicles(&mut self) {
231        self.frozen_vehs.retain(|vehicle_id| {
232            if let Some(vehicle) = self.vehicles.get(*vehicle_id) {
233                vehicle.emergency_stop();
234                true
235            } else {
236                false
237            }
238        })
239    }
240
241    /// Integrates the velocities and positions of all vehicles,
242    /// then resets their accelerations.
243    fn integrate(&mut self, dt: f64) {
244        for (_, vehicle) in &mut self.vehicles {
245            vehicle.integrate(dt, &mut self.seq);
246        }
247    }
248
249    /// Find vehicles that have advanced their link and either move them
250    /// to their new link or remove them from the simulation.
251    fn advance_vehicles(&mut self) {
252        let mut advanced = vec![];
253        let mut exited = vec![];
254
255        for (vehicle_id, vehicle) in &mut self.vehicles {
256            let link_id = vehicle.link_id().unwrap();
257            let did_advance = vehicle.advance(&self.links);
258
259            if did_advance {
260                self.links[link_id].remove_vehicle(vehicle_id);
261                if let Some(link_id) = vehicle.link_id() {
262                    // Vehicle is now on a new link
263                    advanced.push((vehicle_id, link_id));
264                } else {
265                    // Vehicle has exited the simulation
266                    exited.push(vehicle_id);
267                }
268            }
269        }
270
271        for (vehicle_id, link_id) in advanced {
272            self.links[link_id].insert_vehicle(&self.vehicles, vehicle_id);
273        }
274
275        for vehicle_id in exited {
276            self.vehicles.remove(vehicle_id);
277        }
278    }
279
280    /// Updates the world coordinates of all the vehicles.
281    fn update_vehicle_coords(&mut self) {
282        for (_, vehicle) in &mut self.vehicles {
283            vehicle.update_coords(&self.links);
284        }
285    }
286
287    /// Causes the given vehicle to change lanes from its current link to the specified link.
288    /// It will smoothly transition from one to the other over the given `distance` specified in metres.
289    pub fn do_lane_change(&mut self, vehicle_id: VehicleId, link_id: LinkId, distance: f64) {
290        let vehicle = &mut self.vehicles[vehicle_id];
291
292        // Remove the vehicle from its current link
293        if let Some(link_id) = vehicle.link_id() {
294            self.links[link_id].remove_vehicle(vehicle_id);
295        }
296
297        // Project the vehicle's position onto the new link
298        let link = &mut self.links[link_id];
299        let (pos, offset, slope) = link
300            .curve()
301            .inverse_sample(vehicle.position(), vehicle.direction())
302            .unwrap();
303
304        // Set the vehicle's new position
305        let end_pos = pos + distance;
306        let lane_change = LaneChange {
307            end_pos,
308            offset: CubicFn::fit(pos, offset, slope, end_pos, 0.0, 0.0),
309        };
310        vehicle.set_location(link_id, pos, Some(lane_change));
311
312        // Add the vehicle to the new link
313        link.insert_vehicle(&self.vehicles, vehicle_id);
314    }
315
316    /// Sets the route for the given vehicle, which is the list of links it will traverse
317    /// after it has exited its current link.
318    pub fn set_vehicle_route(&mut self, vehicle_id: VehicleId, route: &[LinkId]) {
319        self.vehicles[vehicle_id].set_route(route, true);
320    }
321}