traffic_sim/
link.rs

1use crate::conflict::LinkConflict;
2use crate::group::Obstacle;
3use crate::math::{ParametricCurve2d, Point2d};
4use crate::util::rotated_range;
5use crate::vehicle::{ObstaclePassResult, RouteState, Vehicle};
6use crate::{LinkGroup, LinkId, LinkSet, VehicleId, VehicleSet};
7pub use curve::{LinkCurve, LinkSample};
8use serde::{Deserialize, Serialize};
9use smallvec::{smallvec, SmallVec};
10use std::cell::Cell;
11use std::cmp::Ordering;
12use std::ops::ControlFlow;
13use std::rc::Rc;
14
15mod curve;
16
17/// The maximum lookahead for the car following model, in s.
18const MAX_LOOKAHEAD: f64 = 5.0;
19
20/// The buffer between reach times for gap acceptance, in s.
21const GAP_BUFFER: f64 = 0.5;
22
23/// A link represents a single lane of traffic.
24#[derive(Clone, Serialize, Deserialize)]
25pub struct Link {
26    /// The link ID.
27    id: LinkId,
28    /// The link group.
29    #[serde(skip)]
30    group: Option<Rc<LinkGroup>>,
31    /// The geometry of the link.
32    curve: LinkCurve,
33    /// The links that precede this one.
34    links_in: SmallVec<[LinkId; 4]>,
35    /// The links that succeed this one.
36    links_out: SmallVec<[LinkId; 4]>,
37    /// The links that are adjacent to this one.
38    links_adjacent: SmallVec<[LinkId; 2]>,
39    /// The links that conflict with this one.
40    conflicts: Vec<LinkConflict>,
41    /// The index of the last conflict with an insufficient gap; an optimisation.
42    last_conflict: Cell<usize>,
43    /// Speed limit in m/s.
44    speed_limit: f64,
45    /// The vehicles on the link.
46    vehicles: Vec<VehicleId>,
47    /// The traffic control at the start of the link.
48    control: TrafficControl,
49    /// A flag indicating whether any vehicles are on/entering this link.
50    active: bool,
51}
52
53/// The attributes of a link.
54pub struct LinkAttributes<'a> {
55    /// A curve defining the centre line of the link.
56    pub curve: &'a dyn ParametricCurve2d,
57    /// The speed limit in m/s.
58    pub speed_limit: f64,
59}
60
61/// A traffic control.
62#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
63pub enum TrafficControl {
64    /// Traffic can enter freely, e.g. green light or priority road.
65    Open,
66    /// Traffic must yield before entering.
67    Yield {
68        /// The minimum distance from the stop line before a vehicle can enter.
69        distance: f64,
70        /// Whether a vehicle needs to come to a stop before entering.
71        must_stop: bool,
72        /// The relative priority of the movement.
73        priority: i8,
74    },
75    /// Traffic cannot enter, e.g. red light.
76    Closed,
77}
78
79impl Link {
80    /// Creates a new link.
81    pub(crate) fn new(id: LinkId, attribs: &LinkAttributes) -> Self {
82        let curve = LinkCurve::new(&attribs.curve);
83        Self {
84            id,
85            curve,
86            group: None,
87            links_in: smallvec![],
88            links_out: smallvec![],
89            links_adjacent: smallvec![],
90            conflicts: vec![],
91            last_conflict: Cell::new(0),
92            speed_limit: attribs.speed_limit,
93            vehicles: vec![],
94            control: TrafficControl::Open,
95            active: false,
96        }
97    }
98
99    /// Gets the link ID.
100    pub fn id(&self) -> LinkId {
101        self.id
102    }
103
104    /// Gets the length of the link in m.
105    pub fn length(&self) -> f64 {
106        self.curve.length()
107    }
108
109    /// Gets the curve representing the link's centre line.
110    pub fn curve(&self) -> &LinkCurve {
111        &self.curve
112    }
113
114    /// Gets the links the precede this link, which vehicles may enter from.
115    pub fn links_in(&self) -> &[LinkId] {
116        &self.links_in
117    }
118
119    /// Gets the links the succeed this link, which vehicles may exit to.
120    pub fn links_out(&self) -> &[LinkId] {
121        &self.links_out
122    }
123
124    /// Gets the links that a vehicle on this link may change lanes to.
125    pub fn links_adjacent(&self) -> &[LinkId] {
126        &self.links_adjacent
127    }
128
129    /// Adds a successor link.
130    pub(crate) fn add_link_out(&mut self, link_id: LinkId) {
131        self.links_out.push(link_id);
132    }
133
134    /// Adds a predecessor link.
135    pub(crate) fn add_link_in(&mut self, link_id: LinkId) {
136        self.links_in.push(link_id);
137    }
138
139    /// Adds an adjacent link.
140    pub(crate) fn add_lane_change(&mut self, link_id: LinkId) {
141        self.links_adjacent.push(link_id);
142    }
143
144    /// Sets the link group.
145    pub(crate) fn set_group(&mut self, group: Rc<LinkGroup>) {
146        self.group = Some(group);
147    }
148
149    /// Adds a conflicting link.
150    pub(crate) fn add_conflict(&mut self, link_conflict: LinkConflict) {
151        self.conflicts.push(link_conflict);
152    }
153
154    /// Checks that a vehicle could be inserted into the link without collision.
155    #[allow(unused)]
156    pub(crate) fn can_insert_vehicle(&self, vehicles: &VehicleSet, pos: f64, len: f64) -> bool {
157        for id in &self.vehicles {
158            let other = &vehicles[*id];
159            let gap = other.pos_mid() - pos;
160            let needed = 0.5 * (len + other.length());
161            if gap.abs() < needed {
162                return false;
163            }
164            if gap > 0.0 {
165                break;
166            }
167        }
168        true
169    }
170
171    /// Inserts the vehicle with the given ID into the link.
172    pub(crate) fn insert_vehicle(&mut self, vehicles: &VehicleSet, id: VehicleId) {
173        let veh_pos = vehicles[id].pos_mid();
174        let idx = self
175            .vehicles
176            .iter()
177            .map(|id| vehicles[*id].pos_mid())
178            .position(|pos| pos > veh_pos)
179            .unwrap_or(self.vehicles.len());
180        self.vehicles.insert(idx, id);
181    }
182
183    /// Removes the vehicle with the given ID from the link.
184    pub(crate) fn remove_vehicle(&mut self, id: VehicleId) {
185        if let Some(idx) = self.vehicles.iter().rposition(|v| *v == id) {
186            self.vehicles.remove(idx);
187        }
188    }
189
190    /// Sets the traffic control at the start of the link.
191    pub(crate) fn set_control(&mut self, control: TrafficControl) {
192        self.control = control;
193    }
194
195    /// Allows vehicles to enter the link.
196    pub(crate) fn apply_stoplines(&self, links: &LinkSet, vehicles: &VehicleSet) {
197        self.process_vehicles(
198            links,
199            vehicles,
200            &mut |vehicle| match vehicle.get_route(1) {
201                Some((_, RouteState::Entered)) => ControlFlow::Continue(()),
202                Some((link_id, state)) => match links[link_id].control {
203                    TrafficControl::Open => {
204                        // Enter the link if it can, otherwise stop processing
205                        if !vehicle.can_stop(-5.0) {
206                            vehicle.enter_link();
207                            ControlFlow::Continue(())
208                        } else {
209                            ControlFlow::Break(())
210                        }
211                    }
212                    TrafficControl::Yield {
213                        distance,
214                        must_stop,
215                        priority,
216                    } => match state {
217                        RouteState::NotEntered => {
218                            // Enqueue the vehicle if it can, and stop before the line
219                            let close_enough = vehicle.pos_front() > -distance;
220                            let stopped = !must_stop || vehicle.has_stopped();
221                            if close_enough && stopped {
222                                vehicle.queue_link();
223                            }
224                            vehicle.stop_at_line(0.0);
225                            ControlFlow::Break(())
226                        }
227                        RouteState::QueuedAt(frame) => {
228                            // Enter the link if it can, otherwise stop before the line
229                            if links[link_id].can_enter(
230                                links,
231                                vehicles,
232                                &vehicle.offset(self.length()),
233                                frame,
234                                priority,
235                            ) {
236                                vehicle.enter_link();
237                                ControlFlow::Continue(())
238                            } else {
239                                vehicle.stop_at_line(0.0);
240                                ControlFlow::Break(())
241                            }
242                        }
243                        RouteState::Entered => unreachable!(),
244                    },
245                    TrafficControl::Closed => {
246                        // Stop before the line
247                        vehicle.stop_at_line(0.0);
248                        ControlFlow::Break(())
249                    }
250                },
251                None => ControlFlow::Continue(()),
252            },
253            (0, self.id),
254            self.length(),
255            0,
256        );
257    }
258
259    /// Determines whether a vehicle can enter this link or if it must give way.
260    fn can_enter(
261        &self,
262        links: &LinkSet,
263        vehicles: &VehicleSet,
264        vehicle: &RelativeVehicle,
265        queued_at: usize,
266        priority: i8,
267    ) -> bool {
268        for idx in rotated_range(self.conflicts.len(), self.last_conflict.get()) {
269            let conflict = &self.conflicts[idx];
270            let link = &links[conflict.link_id];
271            let queued_at = match link.control {
272                TrafficControl::Open => None,
273                TrafficControl::Yield { priority: p2, .. } => match priority.cmp(&p2) {
274                    Ordering::Less => None,
275                    Ordering::Equal => Some(queued_at),
276                    Ordering::Greater => Some(0),
277                },
278                TrafficControl::Closed => Some(0),
279            };
280            let max_pos = conflict.own_max_pos;
281            let has_gap = link.check_gap(
282                links,
283                vehicles,
284                [conflict.min_pos, conflict.max_pos],
285                vehicle.min_reach_time(max_pos, self.speed_limit) + GAP_BUFFER,
286                queued_at,
287                (0, conflict.link_id),
288                0.0,
289            );
290            if !has_gap {
291                self.last_conflict.set(idx);
292                return false;
293            }
294        }
295        true
296    }
297
298    /// Checks whether there is sufficient gap on this link.
299    #[allow(clippy::too_many_arguments)]
300    fn check_gap(
301        &self,
302        links: &LinkSet,
303        vehicles: &VehicleSet,
304        pos: [f64; 2],
305        time: f64,
306        queued_at: Option<usize>,
307        route: (usize, LinkId),
308        max_vel: f64,
309    ) -> bool {
310        let max_vel = f64::max(max_vel, self.speed_limit);
311
312        let vehs_on_link = self
313            .vehicles
314            .iter()
315            .map(|id| &vehicles[*id])
316            .rev()
317            .skip_while(|veh| veh.pos_rear() > pos[1]);
318
319        for vehicle in vehs_on_link {
320            // Check vehicle's route and priority
321            match (vehicle.get_route(route.0), queued_at) {
322                // On another route
323                (None, _) => continue,
324                (Some((link_id, _)), _) if link_id != route.1 => continue,
325                // Vehicle has lower priority and hasn't entered the intersection yet
326                (Some((_, RouteState::NotEntered)), Some(_)) => return true,
327                (Some((_, RouteState::QueuedAt(a))), Some(b)) if a > b => return true,
328                // Otherwise, check if there is a sufficient gap
329                _ => return vehicle.min_reach_time(pos[0], max_vel) > time,
330            }
331        }
332
333        if pos[0] / max_vel > time {
334            true
335        } else {
336            let route = (route.0 + 1, route.1);
337            self.links_in.iter().all(|link_id| {
338                let link = &links[*link_id];
339                let pos = pos.map(|p| p + link.length());
340                link.check_gap(links, vehicles, pos, time, queued_at, route, max_vel)
341            })
342        }
343    }
344
345    /// Applies the car following model and the link's speed limit to vehicles on this link and preceeding links.
346    pub(crate) fn apply_accelerations(&self, links: &LinkSet, vehicles: &VehicleSet) {
347        // Apply car following and speed limit to vehicles on this link
348        for ids in self.vehicles.windows(2) {
349            let [follower, leader] = [ids[0], ids[1]].map(|id| &vehicles[id]);
350            follower.follow_vehicle(leader.pos_rear(), leader.vel());
351            follower.apply_current_speed_limit(self.speed_limit);
352        }
353        if let Some(id) = self.vehicles.last() {
354            vehicles[*id].apply_current_speed_limit(self.speed_limit);
355        }
356
357        // Apply car following and speed limit to vehicles about to enter this link
358        self.process_vehicles(
359            links,
360            vehicles,
361            &mut |veh| {
362                veh.apply_speed_limit(self.speed_limit, 0.0);
363                if let Some(leader) = self.vehicles.first().map(|id| &vehicles[*id]) {
364                    veh.follow_vehicle(leader.pos_rear(), leader.vel());
365                }
366                ControlFlow::Break(())
367            },
368            (0, self.id),
369            0.0,
370            self.vehicles.len(),
371        );
372
373        // Apply accelerations to vehicles on adjacent links
374        if let Some(group) = self.group.as_deref() {
375            for proj in group.projections(self.id) {
376                let obstacles = self
377                    .vehicles
378                    .iter()
379                    .rev()
380                    .map(|id| vehicles[*id].project(proj));
381                links[proj.link_id()].follow_obstacles(links, vehicles, obstacles);
382            }
383        }
384    }
385
386    /// Applies an acceleration to the vehicles on this and preceeding links
387    /// that need to follow the given obstacles.
388    /// The obstacles must be in reverse order (descreasing `pos`) along the link.
389    fn follow_obstacles(
390        &self,
391        links: &LinkSet,
392        vehicles: &VehicleSet,
393        obstacles: impl Iterator<Item = Obstacle>,
394    ) {
395        use ObstaclePassResult::*;
396
397        let mut skip = 0;
398
399        for obstacle in obstacles {
400            // Skip vehicles ahead of the obstacle
401            skip = self
402                .vehicles
403                .iter()
404                .rev()
405                .skip(skip)
406                .position(|id| vehicles[*id].pos_mid() < obstacle.pos)
407                .map(|cnt| skip + cnt)
408                .unwrap_or(self.vehicles.len());
409
410            // Follow the obstacle
411            self.process_vehicles(
412                links,
413                vehicles,
414                &mut |veh| match veh.can_pass(obstacle, self) {
415                    Pass => ControlFlow::Continue(()),
416                    Follow { pos, vel } => {
417                        veh.follow_vehicle(pos - obstacle.pos, vel);
418                        ControlFlow::Break(())
419                    }
420                },
421                (0, self.id),
422                obstacle.pos,
423                skip,
424            );
425        }
426    }
427
428    /// Resets the link's `active` flag
429    pub(crate) fn deactivate(&mut self) {
430        self.active = false;
431    }
432
433    /// Sets the link's `active` flag
434    pub(crate) fn activate(&mut self) {
435        self.active = true;
436    }
437
438    /// Applies the passed function on each vehicle on the link and preceeding links
439    /// in reverse order, in a depth-first fashion.
440    /// If the inner function returns `false`, processing on that link stops.
441    ///
442    /// # Parameters
443    /// * `links` - The links in the network
444    /// * `vehicles` - The vehicles in the network
445    /// * `func` - The function to apply to each vehicle
446    /// * `route` - Only process vehicles on this route
447    /// * `pos` - The `pos` value that each vehicle's own `pos` will be relative to
448    /// * `skip` - Skips this number of vehicles at the end of the link
449    pub(crate) fn process_vehicles<'a, F: FnMut(RelativeVehicle<'a>) -> ControlFlow<()>>(
450        &self,
451        links: &LinkSet,
452        vehicles: &'a VehicleSet,
453        func: &mut F,
454        route: (usize, LinkId),
455        pos: f64,
456        skip: usize,
457    ) {
458        if !self.active {
459            return;
460        }
461
462        let min_pos = pos - MAX_LOOKAHEAD * self.speed_limit;
463
464        for veh_id in self.vehicles.iter().rev().skip(skip) {
465            let vehicle = &vehicles[*veh_id];
466            if vehicle.pos_front() < min_pos {
467                return;
468            }
469            match vehicle.get_route(route.0) {
470                None => continue,
471                Some((link_id, _)) if link_id != route.1 => continue,
472                Some((_, RouteState::Entered)) => {}
473                Some((_, _)) => {
474                    if pos > self.length() {
475                        return;
476                    }
477                }
478            }
479            let result = (*func)(RelativeVehicle {
480                vehicle,
481                route_idx: route.0,
482                pos,
483            });
484            if result.is_break() {
485                return;
486            }
487        }
488
489        if min_pos >= 0.0 {
490            return;
491        }
492
493        let route = (route.0 + 1, route.1);
494        for link_id in &self.links_in {
495            let link = &links[*link_id];
496            let pos = pos + link.length();
497            let skip = 0;
498            link.process_vehicles(links, vehicles, func, route, pos, skip);
499        }
500    }
501}
502
503/// A reference to a vehicle with reference to another object on the network.
504pub(crate) struct RelativeVehicle<'a> {
505    vehicle: &'a Vehicle,
506    route_idx: usize,
507    pos: f64,
508}
509
510impl<'a> RelativeVehicle<'a> {
511    pub fn offset(&self, offset: f64) -> RelativeVehicle {
512        Self {
513            vehicle: self.vehicle,
514            pos: self.pos + offset,
515            route_idx: self.route_idx + 1,
516        }
517    }
518
519    pub fn pos_front(&self) -> f64 {
520        self.vehicle.pos_front() - self.pos
521    }
522
523    pub fn pos_rear(&self) -> f64 {
524        self.vehicle.pos_rear() - self.pos
525    }
526
527    pub fn can_stop(&self, pos: f64) -> bool {
528        self.vehicle.can_stop(self.pos + pos)
529    }
530
531    pub fn vel(&self) -> f64 {
532        self.vehicle.vel()
533    }
534
535    pub fn has_stopped(&self) -> bool {
536        self.vehicle.has_stopped()
537    }
538
539    pub fn rear_coords(&self) -> [Point2d; 2] {
540        self.vehicle.rear_coords()
541    }
542
543    pub fn min_reach_time(&self, pos: f64, max_vel: f64) -> f64 {
544        self.vehicle.min_reach_time(self.pos + pos, max_vel)
545    }
546
547    pub fn apply_speed_limit(&self, speed_limit: f64, pos: f64) {
548        self.vehicle.apply_speed_limit(speed_limit, self.pos + pos);
549    }
550
551    pub fn follow_vehicle(&self, pos: f64, vel: f64) {
552        self.vehicle.follow_vehicle(self.pos + pos, vel);
553    }
554
555    pub fn stop_at_line(&self, pos: f64) {
556        self.vehicle.stop_at_line(self.pos + pos);
557    }
558
559    pub fn follow_obstacle(&self, coords: [Point2d; 2], vel: f64) {
560        self.vehicle.follow_obstacle(coords, vel);
561    }
562
563    pub fn enter_link(&self) {
564        self.vehicle.enter_link();
565    }
566
567    pub fn queue_link(&self) {
568        self.vehicle.queue_link();
569    }
570
571    pub fn get_route(&self, idx: usize) -> Option<(LinkId, RouteState)> {
572        self.vehicle.get_route(self.route_idx + idx)
573    }
574
575    pub fn link_id(&self, idx: usize) -> Option<LinkId> {
576        self.get_route(idx).map(|(link_id, _)| link_id)
577    }
578
579    /// Determines whether the vehicle can pass the given obstacle.
580    pub fn can_pass(&self, obstacle: Obstacle, link: &Link) -> ObstaclePassResult {
581        self.vehicle.can_pass(obstacle, link)
582    }
583}