1use crate::q::RobotQ;
2use crate::{DekeError, DekeResult};
3
4#[derive(Debug, Clone)]
6pub struct RobotPath {
7 waypoints: Vec<RobotQ>,
8}
9
10impl RobotPath {
11 pub fn new(waypoints: Vec<RobotQ>) -> DekeResult<Self> {
12 if let Some(first) = waypoints.first() {
13 let n = first.len();
14 for q in waypoints.iter().skip(1) {
15 if q.len() != n {
16 return Err(DekeError::ShapeMismatch {
17 expected: n,
18 found: q.len(),
19 });
20 }
21 }
22 }
23 Ok(Self { waypoints })
24 }
25
26 pub fn empty() -> Self {
27 Self {
28 waypoints: Vec::new(),
29 }
30 }
31
32 pub fn with_capacity(cap: usize) -> Self {
33 Self {
34 waypoints: Vec::with_capacity(cap),
35 }
36 }
37
38 pub fn len(&self) -> usize {
39 self.waypoints.len()
40 }
41
42 pub fn is_empty(&self) -> bool {
43 self.waypoints.is_empty()
44 }
45
46 pub fn ndof(&self) -> usize {
48 self.waypoints.first().map_or(0, |q| q.len())
49 }
50
51 pub fn get(&self, index: usize) -> Option<&RobotQ> {
52 self.waypoints.get(index)
53 }
54
55 pub fn first(&self) -> Option<&RobotQ> {
56 self.waypoints.first()
57 }
58
59 pub fn last(&self) -> Option<&RobotQ> {
60 self.waypoints.last()
61 }
62
63 pub fn iter(&self) -> std::slice::Iter<'_, RobotQ> {
64 self.waypoints.iter()
65 }
66
67 pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, RobotQ> {
68 self.waypoints.iter_mut()
69 }
70
71 pub fn segments(&self) -> impl Iterator<Item = (&RobotQ, &RobotQ)> {
73 self.waypoints.windows(2).map(|w| (&w[0], &w[1]))
74 }
75
76 pub fn push(&mut self, q: RobotQ) -> DekeResult<()> {
77 if let Some(first) = self.waypoints.first() {
78 if q.len() != first.len() {
79 return Err(DekeError::ShapeMismatch {
80 expected: first.len(),
81 found: q.len(),
82 });
83 }
84 }
85 self.waypoints.push(q);
86 Ok(())
87 }
88
89 pub fn pop(&mut self) -> Option<RobotQ> {
90 self.waypoints.pop()
91 }
92
93 pub fn clear(&mut self) {
94 self.waypoints.clear();
95 }
96
97 pub fn truncate(&mut self, len: usize) {
98 self.waypoints.truncate(len);
99 }
100
101 pub fn reverse(&mut self) {
102 self.waypoints.reverse();
103 }
104
105 pub fn reversed(&self) -> Self {
106 let mut wps = self.waypoints.clone();
107 wps.reverse();
108 Self { waypoints: wps }
109 }
110
111 pub fn arc_length(&self) -> f32 {
113 self.segments().map(|(a, b)| q_distance(a, b)).sum()
114 }
115
116 pub fn segment_lengths(&self) -> Vec<f32> {
118 self.segments().map(|(a, b)| q_distance(a, b)).collect()
119 }
120
121 pub fn cumulative_lengths(&self) -> Vec<f32> {
123 let mut cum = Vec::with_capacity(self.len());
124 let mut total = 0.0;
125 cum.push(0.0);
126 for (a, b) in self.segments() {
127 total += q_distance(a, b);
128 cum.push(total);
129 }
130 cum
131 }
132
133 pub fn max_segment_length(&self) -> f32 {
135 self.segments()
136 .map(|(a, b)| q_distance(a, b))
137 .fold(0.0, f32::max)
138 }
139
140 pub fn max_joint_step(&self) -> f32 {
142 self.segments()
143 .map(|(a, b)| q_linf_distance(a, b))
144 .fold(0.0, f32::max)
145 }
146
147 pub fn sample(&self, t: f32) -> Option<RobotQ> {
150 let n = self.len();
151 if n < 2 {
152 return if n == 1 {
153 Some(self.waypoints[0].clone())
154 } else {
155 None
156 };
157 }
158
159 let t = t.clamp(0.0, 1.0);
160 let total = self.arc_length();
161 if total == 0.0 {
162 return Some(self.waypoints[0].clone());
163 }
164
165 let target = t * total;
166 let mut accumulated = 0.0;
167
168 for (a, b) in self.segments() {
169 let seg_len = q_distance(a, b);
170 if accumulated + seg_len >= target {
171 let local_t = if seg_len > 0.0 {
172 (target - accumulated) / seg_len
173 } else {
174 0.0
175 };
176 return Some(a + &((b - a) * local_t));
177 }
178 accumulated += seg_len;
179 }
180
181 Some(self.waypoints.last().unwrap().clone())
182 }
183
184 pub fn densify(&self, max_dist: f32) -> Self {
186 if self.len() < 2 {
187 return self.clone();
188 }
189
190 let mut out = Vec::new();
191 out.push(self.waypoints[0].clone());
192
193 for (a, b) in self.segments() {
194 let d = q_distance(a, b);
195 let steps = (d / max_dist).ceil() as usize;
196 let steps = steps.max(1);
197 for i in 1..=steps {
198 let t = i as f32 / steps as f32;
199 out.push(a + &((b - a) * t));
200 }
201 }
202
203 Self { waypoints: out }
204 }
205
206 pub fn simplify(&self, tol: f32) -> Self {
209 if self.len() <= 2 {
210 return self.clone();
211 }
212
213 let mut keep = vec![true; self.len()];
214 rdp_mark(&self.waypoints, 0, self.len() - 1, tol, &mut keep);
215
216 let waypoints = self
217 .waypoints
218 .iter()
219 .enumerate()
220 .filter(|(i, _)| keep[*i])
221 .map(|(_, q)| q.clone())
222 .collect();
223
224 Self { waypoints }
225 }
226
227 pub fn map_waypoints(&self, f: impl Fn(&RobotQ) -> RobotQ) -> Self {
229 Self {
230 waypoints: self.waypoints.iter().map(f).collect(),
231 }
232 }
233}
234
235impl std::ops::Index<usize> for RobotPath {
236 type Output = RobotQ;
237 #[inline]
238 fn index(&self, i: usize) -> &RobotQ {
239 &self.waypoints[i]
240 }
241}
242
243impl std::ops::IndexMut<usize> for RobotPath {
244 #[inline]
245 fn index_mut(&mut self, i: usize) -> &mut RobotQ {
246 &mut self.waypoints[i]
247 }
248}
249
250impl FromIterator<RobotQ> for RobotPath {
251 fn from_iter<I: IntoIterator<Item = RobotQ>>(iter: I) -> Self {
252 Self {
253 waypoints: iter.into_iter().collect(),
254 }
255 }
256}
257
258impl IntoIterator for RobotPath {
259 type Item = RobotQ;
260 type IntoIter = std::vec::IntoIter<RobotQ>;
261
262 fn into_iter(self) -> Self::IntoIter {
263 self.waypoints.into_iter()
264 }
265}
266
267impl<'a> IntoIterator for &'a RobotPath {
268 type Item = &'a RobotQ;
269 type IntoIter = std::slice::Iter<'a, RobotQ>;
270
271 fn into_iter(self) -> Self::IntoIter {
272 self.waypoints.iter()
273 }
274}
275
276fn q_distance(a: &RobotQ, b: &RobotQ) -> f32 {
277 let diff = b - a;
278 diff.mapv(|x| x * x).sum().sqrt()
279}
280
281fn q_linf_distance(a: &RobotQ, b: &RobotQ) -> f32 {
282 let diff = b - a;
283 diff.mapv(f32::abs).into_iter().fold(0.0, f32::max)
284}
285
286fn rdp_mark(pts: &[RobotQ], start: usize, end: usize, tol: f32, keep: &mut [bool]) {
287 if end <= start + 1 {
288 return;
289 }
290
291 let seg = &pts[end] - &pts[start];
292 let seg_len_sq: f32 = seg.mapv(|x| x * x).sum();
293
294 let mut max_dist = 0.0f32;
295 let mut max_idx = start;
296
297 for i in (start + 1)..end {
298 let v = &pts[i] - &pts[start];
299 let dist = if seg_len_sq < 1e-30 {
300 v.mapv(|x| x * x).sum().sqrt()
301 } else {
302 let t = (v.dot(&seg) / seg_len_sq).clamp(0.0, 1.0);
303 let proj = &v - &(&seg * t);
304 proj.mapv(|x| x * x).sum().sqrt()
305 };
306 if dist > max_dist {
307 max_dist = dist;
308 max_idx = i;
309 }
310 }
311
312 if max_dist > tol {
313 keep[max_idx] = true;
314 rdp_mark(pts, start, max_idx, tol, keep);
315 rdp_mark(pts, max_idx, end, tol, keep);
316 } else {
317 for k in (start + 1)..end {
318 keep[k] = false;
319 }
320 }
321}