1use std::time::Duration;
2
3use num_traits::Float;
4
5use crate::{DekeError, DekeResult, RobotPath, SRobotPath, SRobotQ};
6
7#[derive(Debug, Clone)]
9pub struct RobotTraj<T: Float = f32> {
10 dt: Duration,
11 path: RobotPath<T>,
12}
13
14impl<T: Float> RobotTraj<T> {
15 pub fn new(dt: Duration, path: RobotPath<T>) -> Self {
16 Self { dt, path }
17 }
18
19 pub fn dt(&self) -> Duration {
20 self.dt
21 }
22
23 pub fn path(&self) -> &RobotPath<T> {
24 &self.path
25 }
26
27 pub fn path_mut(&mut self) -> &mut RobotPath<T> {
28 &mut self.path
29 }
30
31 pub fn into_path(self) -> RobotPath<T> {
32 self.path
33 }
34
35 pub fn len(&self) -> usize {
36 self.path.nrows()
37 }
38
39 pub fn is_empty(&self) -> bool {
40 self.path.nrows() == 0
41 }
42
43 pub fn dof(&self) -> usize {
44 self.path.ncols()
45 }
46
47 pub fn duration(&self) -> Duration {
48 self.dt
49 .saturating_mul(self.len().saturating_sub(1) as u32)
50 }
51
52 pub fn time_at(&self, index: usize) -> Duration {
53 self.dt.saturating_mul(index as u32)
54 }
55}
56
57#[derive(Debug, Clone)]
59pub struct SRobotTraj<const N: usize, T: Float = f32> {
60 dt: Duration,
61 path: SRobotPath<N, T>,
62}
63
64impl<const N: usize, T: Float> SRobotTraj<N, T> {
65 pub fn new(dt: Duration, path: SRobotPath<N, T>) -> Self {
66 Self { dt, path }
67 }
68
69 pub fn try_from_waypoints(dt: Duration, waypoints: Vec<SRobotQ<N, T>>) -> DekeResult<Self> {
70 Ok(Self {
71 dt,
72 path: SRobotPath::try_new(waypoints)?,
73 })
74 }
75
76 pub fn dt(&self) -> Duration {
77 self.dt
78 }
79
80 pub fn set_dt(&mut self, dt: Duration) {
81 self.dt = dt;
82 }
83
84 pub fn path(&self) -> &SRobotPath<N, T> {
85 &self.path
86 }
87
88 pub fn path_mut(&mut self) -> &mut SRobotPath<N, T> {
89 &mut self.path
90 }
91
92 pub fn into_path(self) -> SRobotPath<N, T> {
93 self.path
94 }
95
96 pub fn len(&self) -> usize {
97 self.path.len()
98 }
99
100 pub fn is_empty(&self) -> bool {
101 self.len() == 0
102 }
103
104 pub fn dof(&self) -> usize {
105 N
106 }
107
108 pub fn first(&self) -> &SRobotQ<N, T> {
109 self.path.first()
110 }
111
112 pub fn last(&self) -> &SRobotQ<N, T> {
113 self.path.last()
114 }
115
116 pub fn get(&self, index: usize) -> Option<&SRobotQ<N, T>> {
117 self.path.get(index)
118 }
119
120 pub fn duration(&self) -> Duration {
121 self.dt
122 .saturating_mul(self.len().saturating_sub(1) as u32)
123 }
124
125 pub fn time_at(&self, index: usize) -> Duration {
126 self.dt.saturating_mul(index as u32)
127 }
128
129 pub fn iter(&self) -> std::slice::Iter<'_, SRobotQ<N, T>> {
130 self.path.iter()
131 }
132
133 pub fn iter_timed(&self) -> impl Iterator<Item = (Duration, &SRobotQ<N, T>)> + '_ {
134 let dt = self.dt;
135 self.path
136 .iter()
137 .enumerate()
138 .map(move |(i, q)| (dt.saturating_mul(i as u32), q))
139 }
140
141 pub fn sample_at_time(&self, t: Duration) -> Option<SRobotQ<N, T>> {
144 let n = self.len();
145 if n == 0 {
146 return None;
147 }
148 if n == 1 {
149 return Some(*self.first());
150 }
151
152 let dt_secs = self.dt.as_secs_f64();
153 if dt_secs <= 0.0 {
154 return Some(*self.first());
155 }
156
157 let t_secs = t.as_secs_f64();
158 let max_secs = dt_secs * (n - 1) as f64;
159 let clamped = t_secs.clamp(0.0, max_secs);
160
161 let f = clamped / dt_secs;
162 let i = (f.floor() as usize).min(n - 2);
163 let local = T::from(f - i as f64).unwrap_or_else(T::zero);
164
165 let a = self.path.get(i)?;
166 let b = self.path.get(i + 1)?;
167 Some(a.interpolate(b, local))
168 }
169
170 pub fn velocity_at(&self, i: usize) -> Option<SRobotQ<N, T>> {
173 let n = self.len();
174 if n < 2 || i >= n {
175 return None;
176 }
177 let dt_secs = T::from(self.dt.as_secs_f64()).unwrap_or_else(T::zero);
178 if dt_secs == T::zero() {
179 return Some(SRobotQ::zeros());
180 }
181
182 let (a, b, span) = if i == 0 {
183 (*self.path.get(0)?, *self.path.get(1)?, T::one())
184 } else if i == n - 1 {
185 (*self.path.get(n - 2)?, *self.path.get(n - 1)?, T::one())
186 } else {
187 (
188 *self.path.get(i - 1)?,
189 *self.path.get(i + 1)?,
190 T::from(2.0).unwrap_or_else(T::one),
191 )
192 };
193
194 Some((b - a) * (T::one() / (span * dt_secs)))
195 }
196
197 pub fn acceleration_at(&self, i: usize) -> Option<SRobotQ<N, T>> {
199 let n = self.len();
200 if n < 3 || i >= n {
201 return None;
202 }
203 let dt_secs = T::from(self.dt.as_secs_f64()).unwrap_or_else(T::zero);
204 if dt_secs == T::zero() {
205 return Some(SRobotQ::zeros());
206 }
207
208 let idx = i.clamp(1, n - 2);
209 let a = *self.path.get(idx - 1)?;
210 let b = *self.path.get(idx)?;
211 let c = *self.path.get(idx + 1)?;
212 let inv_dt_sq = T::one() / (dt_secs * dt_secs);
213 Some((a - b * T::from(2.0).unwrap_or_else(T::one) + c) * inv_dt_sq)
214 }
215
216 pub fn max_joint_velocity(&self) -> T {
217 let mut peak = T::zero();
218 for i in 0..self.len() {
219 if let Some(v) = self.velocity_at(i) {
220 peak = peak.max(v.linf_norm());
221 }
222 }
223 peak
224 }
225
226 pub fn max_joint_acceleration(&self) -> T {
227 let mut peak = T::zero();
228 for i in 0..self.len() {
229 if let Some(a) = self.acceleration_at(i) {
230 peak = peak.max(a.linf_norm());
231 }
232 }
233 peak
234 }
235
236 pub fn reverse(&mut self) {
238 self.path.reverse();
239 }
240
241 pub fn reversed(&self) -> Self {
242 Self {
243 dt: self.dt,
244 path: self.path.reversed(),
245 }
246 }
247
248 pub fn rescale_time(&mut self, scale: f64) {
250 let new_secs = self.dt.as_secs_f64() * scale.max(0.0);
251 self.dt = Duration::from_secs_f64(new_secs);
252 }
253
254 pub fn to_robot_traj(&self) -> RobotTraj<T> {
255 RobotTraj {
256 dt: self.dt,
257 path: self.path.to_robot_path(),
258 }
259 }
260}
261
262impl<const N: usize, T: Float> std::ops::Index<usize> for SRobotTraj<N, T> {
263 type Output = SRobotQ<N, T>;
264 #[inline]
265 fn index(&self, i: usize) -> &SRobotQ<N, T> {
266 &self.path[i]
267 }
268}
269
270impl<'a, const N: usize, T: Float> IntoIterator for &'a SRobotTraj<N, T> {
271 type Item = &'a SRobotQ<N, T>;
272 type IntoIter = std::slice::Iter<'a, SRobotQ<N, T>>;
273
274 fn into_iter(self) -> Self::IntoIter {
275 self.path.iter()
276 }
277}
278
279impl<const N: usize, T: Float> AsRef<SRobotPath<N, T>> for SRobotTraj<N, T> {
280 fn as_ref(&self) -> &SRobotPath<N, T> {
281 &self.path
282 }
283}
284
285impl<const N: usize, T: Float> From<SRobotTraj<N, T>> for SRobotPath<N, T> {
286 fn from(traj: SRobotTraj<N, T>) -> Self {
287 traj.path
288 }
289}
290
291impl<const N: usize, T: Float> From<SRobotTraj<N, T>> for RobotTraj<T> {
292 fn from(traj: SRobotTraj<N, T>) -> Self {
293 traj.to_robot_traj()
294 }
295}
296
297impl<const N: usize, T: Float> TryFrom<RobotTraj<T>> for SRobotTraj<N, T> {
298 type Error = DekeError;
299
300 fn try_from(traj: RobotTraj<T>) -> Result<Self, Self::Error> {
301 Ok(Self {
302 dt: traj.dt,
303 path: SRobotPath::<N, T>::try_from(traj.path)?,
304 })
305 }
306}