1use crate::error::{TransformError, TransformResult};
2use crate::transform::StampedTransform;
3use cu29::clock::CuTime;
4use cu_spatial_payloads::Transform3D;
5use std::fmt::Debug;
6
7pub trait Interpolate: Copy + Debug + Default + 'static {
9 fn to_f64(self) -> f64;
11 fn from_f64(val: f64) -> Self;
13}
14
15impl Interpolate for f32 {
17 fn to_f64(self) -> f64 {
18 self as f64
19 }
20 fn from_f64(val: f64) -> Self {
21 val as f32
22 }
23}
24
25impl Interpolate for f64 {
26 fn to_f64(self) -> f64 {
27 self
28 }
29 fn from_f64(val: f64) -> Self {
30 val
31 }
32}
33
34impl Interpolate for i32 {
35 fn to_f64(self) -> f64 {
36 self as f64
37 }
38 fn from_f64(val: f64) -> Self {
39 val.round() as i32
40 }
41}
42
43impl Interpolate for i64 {
44 fn to_f64(self) -> f64 {
45 self as f64
46 }
47 fn from_f64(val: f64) -> Self {
48 val.round() as i64
49 }
50}
51
52impl Interpolate for u32 {
53 fn to_f64(self) -> f64 {
54 self as f64
55 }
56 fn from_f64(val: f64) -> Self {
57 val.round() as u32
58 }
59}
60
61impl Interpolate for u64 {
62 fn to_f64(self) -> f64 {
63 self as f64
64 }
65 fn from_f64(val: f64) -> Self {
66 val.round() as u64
67 }
68}
69
70pub fn interpolate_transforms<T: Interpolate>(
88 before: &StampedTransform<T>,
89 after: &StampedTransform<T>,
90 time: CuTime,
91) -> TransformResult<Transform3D<T>> {
92 if before.parent_frame != after.parent_frame || before.child_frame != after.child_frame {
93 return Err(TransformError::InterpolationError(
94 "Cannot interpolate between different frame pairs".to_string(),
95 ));
96 }
97
98 if time < before.stamp || time > after.stamp {
99 return Err(TransformError::InterpolationError(
100 "Requested time is outside the range of the transforms".to_string(),
101 ));
102 }
103
104 let before_nanos = before.stamp.as_nanos() as f64;
105 let after_nanos = after.stamp.as_nanos() as f64;
106 let time_nanos = time.as_nanos() as f64;
107
108 let ratio = (time_nanos - before_nanos) / (after_nanos - before_nanos);
109
110 let before_mat = before.transform.to_matrix();
112 let after_mat = after.transform.to_matrix();
113
114 let mut result_mat = [[T::default(); 4]; 4];
116
117 for i in 0..4 {
119 for j in 0..4 {
120 result_mat[i][j] = before_mat[i][j];
121 }
122 }
123
124 for i in 0..3 {
126 let before_val = before_mat[3][i].to_f64();
127 let after_val = after_mat[3][i].to_f64();
128 let interpolated = before_val + (after_val - before_val) * ratio;
129 result_mat[3][i] = T::from_f64(interpolated);
130 }
131
132 Ok(Transform3D::from_matrix(result_mat))
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138 use crate::frame_id;
139 fn assert_approx_eq<T>(actual: T, expected: T, epsilon: T)
141 where
142 T: Copy + std::fmt::Display + std::ops::Sub<Output = T> + PartialOrd,
143 T: num_traits::Signed,
144 {
145 let diff = (actual - expected).abs();
146 assert!(
147 diff <= epsilon,
148 "expected {expected}, got {actual}, difference {diff} exceeds epsilon {epsilon}",
149 );
150 }
151 use cu29::clock::CuDuration;
152
153 #[test]
154 fn test_interpolate_transforms_f32() {
155 let before: StampedTransform<f32> = StampedTransform {
156 transform: Transform3D::from_matrix([
157 [1.0, 0.0, 0.0, 0.0],
158 [0.0, 1.0, 0.0, 0.0],
159 [0.0, 0.0, 1.0, 0.0],
160 [0.0, 0.0, 0.0, 1.0], ]),
162 stamp: CuDuration(1000),
163 parent_frame: frame_id!("world"),
164 child_frame: frame_id!("robot"),
165 };
166
167 let after: StampedTransform<f32> = StampedTransform {
168 transform: Transform3D::from_matrix([
169 [1.0, 0.0, 0.0, 0.0],
170 [0.0, 1.0, 0.0, 0.0],
171 [0.0, 0.0, 1.0, 0.0],
172 [10.0, 0.0, 0.0, 1.0], ]),
174 stamp: CuDuration(3000),
175 parent_frame: frame_id!("world"),
176 child_frame: frame_id!("robot"),
177 };
178
179 let result = interpolate_transforms(&before, &after, CuDuration(2000));
180 assert!(result.is_ok());
181
182 let transform = result.unwrap();
183 assert_approx_eq(transform.to_matrix()[3][0], 5.0, 1e-5);
184
185 let result = interpolate_transforms(&before, &after, CuDuration(1500));
186 assert!(result.is_ok());
187
188 let transform = result.unwrap();
189 assert_approx_eq(transform.to_matrix()[3][0], 2.5, 1e-5);
190
191 let result = interpolate_transforms(&before, &after, CuDuration(2500));
192 assert!(result.is_ok());
193
194 let transform = result.unwrap();
195 assert_approx_eq(transform.to_matrix()[3][0], 7.5, 1e-5);
196 }
197
198 #[test]
199 fn test_interpolate_transforms_f64() {
200 let before: StampedTransform<f64> = StampedTransform {
201 transform: Transform3D::from_matrix([
202 [1.0, 0.0, 0.0, 0.0],
203 [0.0, 1.0, 0.0, 0.0],
204 [0.0, 0.0, 1.0, 0.0],
205 [0.0, 0.0, 0.0, 1.0], ]),
207 stamp: CuDuration(1000),
208 parent_frame: frame_id!("world"),
209 child_frame: frame_id!("robot"),
210 };
211
212 let after: StampedTransform<f64> = StampedTransform {
213 transform: Transform3D::from_matrix([
214 [1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0],
216 [0.0, 0.0, 1.0, 0.0],
217 [10.0, 0.0, 0.0, 1.0],
218 ]),
219 stamp: CuDuration(3000),
220 parent_frame: frame_id!("world"),
221 child_frame: frame_id!("robot"),
222 };
223
224 let result = interpolate_transforms(&before, &after, CuDuration(2000));
226 assert!(result.is_ok());
227 let transform = result.unwrap();
228 assert_approx_eq(transform.to_matrix()[3][0], 5.0, 1e-5);
229 }
230
231 #[test]
233 #[ignore]
234 fn test_interpolate_transforms_integer() {
235 let mut before: StampedTransform<i32> = StampedTransform {
237 transform: Transform3D::default(),
238 stamp: CuDuration(1000),
239 parent_frame: frame_id!("world"),
240 child_frame: frame_id!("robot"),
241 };
242
243 let mut after: StampedTransform<i32> = StampedTransform {
244 transform: Transform3D::default(),
245 stamp: CuDuration(3000),
246 parent_frame: frame_id!("world"),
247 child_frame: frame_id!("robot"),
248 };
249
250 let mut before_mat = before.transform.to_matrix();
251 before_mat[0][3] = 0;
252 before.transform = Transform3D::from_matrix(before_mat);
253
254 let mut after_mat = after.transform.to_matrix();
255 after_mat[0][3] = 10;
256 after.transform = Transform3D::from_matrix(after_mat);
257
258 let result = interpolate_transforms(&before, &after, CuDuration(2000));
260 assert!(result.is_ok());
261 let transform = result.unwrap();
262 assert_eq!(transform.to_matrix()[0][3], 5);
263
264 let result = interpolate_transforms(&before, &after, CuDuration(1500));
266 assert!(result.is_ok());
267 let transform = result.unwrap();
268 assert_eq!(transform.to_matrix()[0][3], 3); }
270
271 #[test]
273 #[ignore]
274 fn test_interpolate_transforms_u64() {
275 let mut before: StampedTransform<u64> = StampedTransform {
277 transform: Transform3D::default(),
278 stamp: CuDuration(1000),
279 parent_frame: frame_id!("world"),
280 child_frame: frame_id!("robot"),
281 };
282
283 let mut after: StampedTransform<u64> = StampedTransform {
284 transform: Transform3D::default(),
285 stamp: CuDuration(3000),
286 parent_frame: frame_id!("world"),
287 child_frame: frame_id!("robot"),
288 };
289
290 let mut before_mat = before.transform.to_matrix();
292 before_mat[0][3] = 1_000_000_000;
293 before.transform = Transform3D::from_matrix(before_mat);
294
295 let mut after_mat = after.transform.to_matrix();
296 after_mat[0][3] = 2_000_000_000;
297 after.transform = Transform3D::from_matrix(after_mat);
298
299 let result = interpolate_transforms(&before, &after, CuDuration(2500));
301 assert!(result.is_ok());
302 let transform = result.unwrap();
303 assert_eq!(transform.to_matrix()[0][3], 1_750_000_000);
304 }
305
306 #[test]
307 fn test_interpolate_transforms_errors() {
308 let before: StampedTransform<f32> = StampedTransform {
309 transform: Transform3D::default(),
310 stamp: CuDuration(1000),
311 parent_frame: frame_id!("world"),
312 child_frame: frame_id!("robot"),
313 };
314
315 let after: StampedTransform<f32> = StampedTransform {
316 transform: Transform3D::default(),
317 stamp: CuDuration(3000),
318 parent_frame: frame_id!("different"),
319 child_frame: frame_id!("robot"),
320 };
321
322 let result = interpolate_transforms(&before, &after, CuDuration(2000));
323 assert!(result.is_err());
324
325 let after: StampedTransform<f32> = StampedTransform {
326 transform: Transform3D::default(),
327 stamp: CuDuration(3000),
328 parent_frame: frame_id!("world"),
329 child_frame: frame_id!("different"),
330 };
331
332 let result = interpolate_transforms(&before, &after, CuDuration(2000));
333 assert!(result.is_err());
334
335 let after: StampedTransform<f32> = StampedTransform {
336 transform: Transform3D::default(),
337 stamp: CuDuration(3000),
338 parent_frame: frame_id!("world"),
339 child_frame: frame_id!("robot"),
340 };
341
342 let result = interpolate_transforms(&before, &after, CuDuration(500));
343 assert!(result.is_err());
344
345 let result = interpolate_transforms(&before, &after, CuDuration(3500));
346 assert!(result.is_err());
347 }
348}