1use self::acceleration::AccelerationModel;
2use self::dynamics::calc_direction;
3use crate::group::{LinkProjection, Obstacle};
4use crate::math::{project_local, rot90, CubicFn, Point2d, Vector2d};
5use crate::util::Interval;
6use crate::{Link, LinkId, LinkSet, VehicleId};
7use cgmath::prelude::*;
8use serde::{Deserialize, Serialize};
9use std::cell::Cell;
10
11mod acceleration;
12mod dynamics;
13
14const LATERAL_CLEARANCE: f64 = 0.5;
16
17#[derive(Clone, Serialize, Deserialize, Debug)]
19pub struct Vehicle {
20 pub(crate) id: VehicleId,
22 half_wid: f64,
24 half_len: f64,
26 wheel_base: f64,
28 acc: AccelerationModel,
30 pos: f64,
32 vel: f64,
34 stop_cnt: usize,
36 route: Vec<LinkId>,
38 entered: usize,
40 queued: Option<usize>,
43 #[serde(skip)]
46 will_queue_or_enter: Cell<u8>,
47 can_exit: bool,
49 lane_change: Option<LaneChange>,
51 world_pos: Point2d,
53 world_dir: Vector2d,
55 rear_lats: Interval<f64>,
57 rear_coords: [Point2d; 2],
59}
60
61#[derive(Clone, Copy)]
63pub struct VehicleAttributes {
64 pub width: f64,
66 pub length: f64,
68 pub wheel_base: f64,
70 pub max_acc: f64,
72 pub comf_dec: f64,
74}
75
76#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
78pub struct LaneChange {
79 pub end_pos: f64,
81 pub offset: CubicFn,
83}
84
85#[derive(Clone, Copy, Debug, PartialEq, Eq)]
87pub enum RouteState {
88 NotEntered,
89 QueuedAt(usize),
90 Entered,
91}
92
93pub enum ObstaclePassResult {
94 Pass,
95 Follow { pos: f64, vel: f64 },
96}
97
98impl Vehicle {
99 pub(crate) fn new(id: VehicleId, attributes: &VehicleAttributes) -> Self {
101 Self {
102 id,
103 half_wid: 0.5 * attributes.width,
104 half_len: 0.5 * attributes.length,
105 wheel_base: attributes.wheel_base,
106 acc: AccelerationModel::new(&acceleration::ModelParams {
107 max_acceleration: attributes.max_acc,
108 comf_deceleration: attributes.comf_dec,
109 }),
110 pos: 0.0,
111 vel: 0.0,
112 stop_cnt: 0,
113 route: vec![],
114 entered: 0,
115 queued: None,
116 will_queue_or_enter: Cell::new(0),
117 can_exit: true,
118 lane_change: None,
119 world_pos: Point2d::new(0.0, 0.0),
120 world_dir: Vector2d::new(0.0, 0.0),
121 rear_lats: Default::default(),
122 rear_coords: [Point2d::new(0.0, 0.0); 2],
123 }
124 }
125
126 pub fn id(&self) -> VehicleId {
128 self.id
129 }
130
131 pub fn width(&self) -> f64 {
133 2.0 * self.half_wid
134 }
135
136 pub fn length(&self) -> f64 {
138 2.0 * self.half_len
139 }
140
141 pub fn link_id(&self) -> Option<LinkId> {
143 self.route.get(0).copied()
144 }
145
146 pub fn pos_mid(&self) -> f64 {
148 self.pos
149 }
150
151 pub fn pos_rear(&self) -> f64 {
153 self.pos - self.half_len
154 }
155
156 pub fn pos_front(&self) -> f64 {
158 self.pos + self.half_len
159 }
160
161 pub fn position(&self) -> Point2d {
163 self.world_pos
164 }
165
166 pub fn direction(&self) -> Vector2d {
168 self.world_dir
169 }
170
171 pub fn rear_coords(&self) -> [Point2d; 2] {
173 self.rear_coords
174 }
175
176 pub fn vel(&self) -> f64 {
178 self.vel
179 }
180
181 pub fn has_stopped(&self) -> bool {
183 self.vel < 0.1
184 }
185
186 pub fn min_reach_time(&self, pos: f64, max_vel: f64) -> f64 {
189 let dist = f64::max(pos - self.pos, 0.0);
190 self.acc.min_reach_time(self.vel, dist, max_vel)
191 }
192
193 pub(crate) fn get_route(&self, idx: usize) -> Option<(LinkId, RouteState)> {
195 self.route.get(idx).map(|link_id| {
196 use std::cmp::Ordering::*;
197 let state = match (self.entered.cmp(&idx), self.queued) {
198 (Greater, _) => RouteState::Entered,
199 (Equal, Some(f)) => RouteState::QueuedAt(f),
200 _ => RouteState::NotEntered,
201 };
202 (*link_id, state)
203 })
204 }
205
206 pub(crate) fn can_stop(&self, pos: f64) -> bool {
208 let net_dist = pos - self.pos_front();
209 net_dist >= self.stopping_distance()
210 }
211
212 pub(crate) fn stopping_distance(&self) -> f64 {
214 self.acc.stopping_distance(self.vel)
215 }
216
217 pub(crate) fn queue_link(&self) {
219 self.will_queue_or_enter.set(1);
220 }
221
222 pub(crate) fn enter_link(&self) {
224 self.will_queue_or_enter.set(2);
225 }
226
227 pub(crate) fn stop_at_line(&self, pos: f64) {
229 let net_dist = pos - self.pos_front();
230 self.acc.stop_at_line(net_dist, self.vel);
231 }
232
233 pub(crate) fn follow_vehicle(&self, pos: f64, vel: f64) {
235 let net_dist = pos - self.pos_front();
236 self.acc.follow_vehicle(net_dist, self.vel, vel);
237 }
238
239 pub(crate) fn follow_obstacle(&self, coords: [Point2d; 2], vel: f64) {
241 let mid_dist = f64::min(
242 self.direction().dot(coords[0] - self.position()),
243 self.direction().dot(coords[1] - self.position()),
244 );
245 let net_dist = mid_dist - self.half_len;
246 self.acc.follow_vehicle(net_dist, self.vel, vel);
247 }
248
249 pub(crate) fn apply_current_speed_limit(&self, speed_limit: f64) {
251 self.acc.apply_current_speed_limit(self.vel, speed_limit);
252 }
253
254 pub(crate) fn apply_speed_limit(&self, speed_limit: f64, pos: f64) {
256 self.acc
257 .apply_speed_limit(self.vel, speed_limit, pos - self.pos);
258 }
259
260 pub(crate) fn emergency_stop(&self) {
262 self.acc.emergency_stop();
263 }
264
265 pub(crate) fn project(&self, projection: &LinkProjection) -> Obstacle {
267 projection.project(
268 self.rear_coords,
269 self.pos - self.half_len,
270 self.rear_lats,
271 self.vel,
272 )
273 }
274
275 pub(crate) fn offset_at(&self, pos: f64) -> f64 {
278 self.lane_change
279 .filter(|lc| lc.end_pos > pos)
280 .map(|lc| lc.offset.y(pos))
281 .unwrap_or(0.0)
282 }
283
284 pub(crate) fn reset(&self) {
286 self.acc.reset()
287 }
288
289 pub(crate) fn integrate(&mut self, dt: f64, seq: &mut usize) {
294 match self.will_queue_or_enter.get() {
296 0 => {}
297 1 => {
298 *seq += 1;
299 self.queued.get_or_insert(*seq);
300 }
301 2 => {
302 self.entered += 1;
303 self.queued = None;
304 }
305 _ => unreachable!(),
306 }
307 self.will_queue_or_enter.set(0);
308
309 let vel = f64::max(self.vel + dt * self.acc.acc(), 0.0);
311 let pos = self.pos + 0.5 * (self.vel + vel) * dt;
312 self.vel = vel;
313 self.pos = pos;
314
315 self.lane_change = self.lane_change.filter(|lc| lc.end_pos > pos);
317
318 self.update_stop_count();
320 }
321
322 fn update_stop_count(&mut self) {
324 if self.vel < 0.1 {
325 self.stop_cnt += 1;
326 } else {
327 self.stop_cnt = 0;
328 }
329 }
330
331 pub(crate) fn advance(&mut self, links: &LinkSet) -> bool {
339 if let Some(link_id) = self.route.get(0) {
340 let length = links[*link_id].length();
341 if length < self.pos {
342 self.route.remove(0);
343 self.entered = usize::max(self.entered - 1, 1);
344 self.pos -= length;
345 if let Some(lc) = self.lane_change.as_mut() {
346 lc.offset = lc.offset.translate_x(length);
347 lc.end_pos -= length;
348 }
349 return true;
350 }
351 }
352 false
353 }
354
355 pub(crate) fn set_location(&mut self, link: LinkId, pos: f64, lane_change: Option<LaneChange>) {
358 self.route = vec![link];
359 self.entered = 1;
360 self.pos = pos;
361 self.lane_change = lane_change;
362 self.can_exit = false;
363 }
364
365 pub(crate) fn set_route(&mut self, route: &[LinkId], can_exit: bool) {
367 self.route.truncate(1);
368 self.route.extend(route);
369 self.entered = 1;
370 self.can_exit = can_exit;
371 }
372
373 pub(crate) fn update_coords(&mut self, links: &LinkSet) {
375 let link = &links[self.route[0]];
376 let curve = link.curve();
377
378 let sample = curve.sample_centre(self.pos);
380 let (pos, offset) = match self.lane_change {
381 Some(lc) => {
382 let offset = lc.offset.y(self.pos);
383 let pos = sample.lat_offset(offset);
384 (pos, offset)
385 }
386 None => (sample.pos, 0.0),
387 };
388
389 if self.world_dir == Vector2d::new(0.0, 0.0) {
391 self.world_pos = pos - sample.tan;
392 self.world_dir = sample.tan;
393 }
394
395 let perp = rot90(sample.tan);
397
398 let dir = calc_direction(self.world_pos, self.world_dir, pos, self.wheel_base);
400 self.world_pos = pos;
401 self.world_dir = dir;
402
403 let expand = -dir.dot(perp) * self.half_len;
405 self.rear_lats = Interval {
406 min: f64::min(expand + offset, 0.0) - self.half_wid,
407 max: f64::max(expand + offset, 0.0) + self.half_wid,
408 };
409
410 let rear_mid = sample.long_offset(-self.half_len);
412 self.rear_coords = self.rear_lats.as_array().map(|lat| rear_mid + lat * perp);
413 }
415
416 pub(crate) fn activate_links(&self, links: &mut LinkSet) {
418 for link_id in self.route.iter().take(self.entered) {
419 links[*link_id].activate();
420 }
421 }
422
423 pub(crate) fn can_pass(&self, mut obstacle: Obstacle, link: &Link) -> ObstaclePassResult {
425 let is_clear = |obstacle: Obstacle| {
426 let offset = self.offset_at(obstacle.pos - self.half_len);
427 let distance = obstacle.lat.distance(offset);
428 distance > self.half_wid + LATERAL_CLEARANCE
429 };
430
431 if is_clear(obstacle) {
432 return ObstaclePassResult::Pass;
436 }
437
438 loop {
439 let sample = link.curve().sample_centre(obstacle.pos);
440 let ps = obstacle
441 .rear_coords
442 .map(|p| project_local(p, sample.pos, rot90(sample.tan), sample.tan));
443 let delta_pos = f64::min(ps[0].y, ps[1].y);
444 obstacle.pos += delta_pos;
445 if delta_pos < 0.1 {
446 obstacle.lat = Interval {
447 min: ps[0].x,
448 max: ps[1].x,
449 };
450 break;
451 }
452 }
453
454 if is_clear(obstacle) {
455 ObstaclePassResult::Pass
459 } else {
460 ObstaclePassResult::Follow {
464 pos: obstacle.pos,
465 vel: obstacle.vel,
466 }
467 }
468 }
469}