1use crate::math::{project_point_onto_curve, rot90};
2use crate::util::Interval;
3use crate::Link;
4use crate::{math::Point2d, LinkId};
5use cgmath::InnerSpace;
6use itertools::iproduct;
7use serde::{Deserialize, Serialize};
8
9#[derive(Clone)]
12pub struct LinkGroup {
13 num_links: usize,
14 projections: Vec<LinkProjection>,
15}
16
17#[derive(Clone)]
18pub(crate) struct LinkProjection {
19 src: LinkId,
20 dst: LinkId,
21 inv_segment_len: f64,
22 samples: Vec<ProjectedSample>,
23}
24
25#[derive(Clone, Copy)]
27pub struct Obstacle {
28 pub pos: f64,
30 pub lat: Interval<f64>,
32 pub rear_coords: [Point2d; 2],
34 pub vel: f64,
36}
37
38#[derive(Clone, Copy, Serialize, Deserialize)]
39struct ProjectedSample {
40 pos: f64,
41 lat: f64,
42}
43
44impl LinkGroup {
45 pub(crate) fn new(links: &[&Link]) -> Self {
46 if links.len() < 2 {
47 panic!("Link group must contain atleast two links");
48 }
49
50 let segment_len = 2.0;
51
52 let projections = iproduct!(links, links)
53 .filter(|(src, dst)| src.id() != dst.id())
54 .map(|(src, dst)| LinkProjection {
55 src: src.id(),
56 dst: dst.id(),
57 inv_segment_len: 1.0 / segment_len,
58 samples: (0..)
59 .map(|i| segment_len * i as f64)
60 .take_while(|pos| *pos < src.curve().length())
61 .map(|pos| src.curve().sample_centre(pos).pos)
62 .scan(Some(0.0), |pos, point| {
63 *pos = project_point_onto_curve(dst.curve(), point, 0.1, *pos);
64 pos.map(|pos| (point, pos))
65 })
66 .map(|(point, pos)| {
67 let s = dst.curve().sample_centre(pos);
68 let lat = (point - s.pos).dot(rot90(s.tan));
69 ProjectedSample { pos, lat }
70 })
71 .collect(),
72 })
73 .collect();
74
75 Self {
76 num_links: links.len(),
77 projections,
78 }
79 }
80
81 pub(crate) fn link_ids(&self) -> impl Iterator<Item = LinkId> + '_ {
82 let n = self.num_links - 1;
83 (0..self.num_links).map(move |i| self.projections[n * i].src)
84 }
85
86 pub(crate) fn projections(&self, src_link: LinkId) -> &[LinkProjection] {
87 let idx = self
88 .link_ids()
89 .position(|id| id == src_link)
90 .expect("Link ID is not in group.");
91 let n = self.num_links - 1;
92 &self.projections[(n * idx)..(n * (idx + 1))]
93 }
94
95 }
119
120impl LinkProjection {
121 pub fn link_id(&self) -> LinkId {
122 self.dst
123 }
124
125 pub fn project(
126 &self,
127 rear_coords: [Point2d; 2],
128 pos: f64,
129 lat: Interval<f64>,
130 vel: f64,
131 ) -> Obstacle {
132 let idx = usize::min(
133 (pos * self.inv_segment_len) as usize,
134 self.samples.len() - 1,
135 );
136 let sample = unsafe {
137 self.samples.get_unchecked(idx)
139 };
140 let behind = f64::min(pos, 0.0);
142 Obstacle {
143 rear_coords,
144 pos: sample.pos + behind,
145 lat: lat + sample.lat,
146 vel,
147 }
148 }
149}