1use crate::error::{SpatialError, SpatialResult};
7use crate::transform::{Rotation, Slerp};
8use scirs2_core::ndarray::{array, Array1};
9
10#[allow(dead_code)]
12fn euler_array(x: f64, y: f64, z: f64) -> Array1<f64> {
13 array![x, y, z]
14}
15
16#[allow(dead_code)]
18fn rotation_from_euler(x: f64, y: f64, z: f64, convention: &str) -> SpatialResult<Rotation> {
19 let angles = euler_array(x, y, z);
20 let angles_view = angles.view();
21 Rotation::from_euler(&angles_view, convention)
22}
23
24#[derive(Clone, Debug)]
58pub struct RotationSpline {
59 rotations: Vec<Rotation>,
61 times: Vec<f64>,
63 velocities: Option<Vec<Array1<f64>>>,
65 interpolation_type: String,
67}
68
69impl RotationSpline {
70 pub fn new(rotations: &[Rotation], times: &[f64]) -> SpatialResult<Self> {
97 if rotations.is_empty() {
98 return Err(SpatialError::ValueError("Rotations cannot be empty".into()));
99 }
100
101 if times.is_empty() {
102 return Err(SpatialError::ValueError("Times cannot be empty".into()));
103 }
104
105 if rotations.len() != times.len() {
106 return Err(SpatialError::ValueError(format!(
107 "Number of _rotations ({}) must match number of times ({})",
108 rotations.len(),
109 times.len()
110 )));
111 }
112
113 for i in 1..times.len() {
115 if times[i] <= times[i - 1] {
116 return Err(SpatialError::ValueError(format!(
117 "Times must be strictly increasing, but times[{}] = {} <= times[{}] = {}",
118 i,
119 times[i],
120 i - 1,
121 times[i - 1]
122 )));
123 }
124 }
125
126 let rotations = rotations.to_vec();
128 let times = times.to_vec();
129
130 Ok(RotationSpline {
131 rotations,
132 times,
133 velocities: None,
134 interpolation_type: "slerp".to_string(),
135 })
136 }
137
138 pub fn set_interpolation_type(&mut self, _interptype: &str) -> SpatialResult<()> {
167 match _interptype.to_lowercase().as_str() {
168 "slerp" => {
169 self.interpolation_type = "slerp".to_string();
170 self.velocities = None;
171 Ok(())
172 }
173 "cubic" => {
174 self.interpolation_type = "cubic".to_string();
175 self.compute_velocities();
177 Ok(())
178 }
179 _ => Err(SpatialError::ValueError(format!(
180 "Invalid interpolation _type: {_interptype}. Must be 'slerp' or 'cubic'"
181 ))),
182 }
183 }
184
185 fn compute_velocities(&mut self) {
187 if self.velocities.is_some() {
188 return; }
190
191 let n = self.times.len();
192 if n <= 2 {
193 let mut vels = Vec::with_capacity(n);
195 for _ in 0..n {
196 vels.push(Array1::zeros(3));
197 }
198 self.velocities = Some(vels);
199 return;
200 }
201
202 let mut rotvecs = Vec::with_capacity(n);
204 for rot in &self.rotations {
205 rotvecs.push(rot.as_rotvec());
206 }
207
208 let mut vels = Vec::with_capacity(n);
210
211 for i in 0..n {
214 let vel = if i == 0 {
215 let dt = self.times[1] - self.times[0];
217 (&rotvecs[1] - &rotvecs[0]) / dt
218 } else if i == n - 1 {
219 let dt = self.times[n - 1] - self.times[n - 2];
221 (&rotvecs[n - 1] - &rotvecs[n - 2]) / dt
222 } else {
223 let dt_prev = self.times[i] - self.times[i - 1];
225 let dt_next = self.times[i + 1] - self.times[i];
226
227 let vel_prev = (&rotvecs[i] - &rotvecs[i - 1]) / dt_prev;
229 let vel_next = (&rotvecs[i + 1] - &rotvecs[i]) / dt_next;
230
231 let weight_prev = dt_next / (dt_prev + dt_next);
233 let weight_next = dt_prev / (dt_prev + dt_next);
234 &vel_prev * weight_prev + &vel_next * weight_next
235 };
236
237 vels.push(vel);
238 }
239
240 self.velocities = Some(vels);
241 }
242
243 #[allow(dead_code)]
245 fn compute_natural_spline_second_derivatives(&self, values: &[f64]) -> Vec<f64> {
246 let n = values.len();
247 if n <= 2 {
248 return vec![0.0; n];
249 }
250
251 let mut a = vec![0.0; n - 2]; let mut b = vec![0.0; n - 2]; let mut c = vec![0.0; n - 2]; let mut d = vec![0.0; n - 2]; for i in 0..n - 2 {
264 let h_i = self.times[i + 1] - self.times[i];
265 let h_ip1 = self.times[i + 2] - self.times[i + 1];
266
267 a[i] = h_i;
268 b[i] = 2.0 * (h_i + h_ip1);
269 c[i] = h_ip1;
270
271 let fd_i = (values[i + 1] - values[i]) / h_i;
272 let fd_ip1 = (values[i + 2] - values[i + 1]) / h_ip1;
273 d[i] = 6.0 * (fd_ip1 - fd_i);
274 }
275
276 let mut x = vec![0.0; n - 2];
278 self.solve_tridiagonal(&a, &b, &c, &d, &mut x);
279
280 let mut second_derivs = vec![0.0; n];
282 second_derivs[1..((n - 2) + 1)].copy_from_slice(&x[..(n - 2)]);
283
284 second_derivs
285 }
286
287 #[allow(dead_code)]
289 fn solve_tridiagonal(
290 &self,
291 a: &[f64], b: &[f64], c: &[f64], d: &[f64], x: &mut [f64], ) {
297 let n = x.len();
298 if n == 0 {
299 return;
300 }
301
302 let mut c_prime = vec![0.0; n];
304 let mut d_prime = vec![0.0; n];
305
306 c_prime[0] = c[0] / b[0];
307 d_prime[0] = d[0] / b[0];
308
309 for i in 1..n {
310 let m = b[i] - a[i - 1] * c_prime[i - 1];
311 c_prime[i] = if i < n - 1 { c[i] / m } else { 0.0 };
312 d_prime[i] = (d[i] - a[i - 1] * d_prime[i - 1]) / m;
313 }
314
315 x[n - 1] = d_prime[n - 1];
317 for i in (0..n - 1).rev() {
318 x[i] = d_prime[i] - c_prime[i] * x[i + 1];
319 }
320 }
321
322 pub fn interpolate(&self, t: f64) -> Rotation {
351 let n = self.times.len();
352
353 if t <= self.times[0] {
355 return self.rotations[0].clone();
356 }
357 if t >= self.times[n - 1] {
358 return self.rotations[n - 1].clone();
359 }
360
361 let mut idx = 0;
363 for i in 0..n - 1 {
364 if t >= self.times[i] && t < self.times[i + 1] {
365 idx = i;
366 break;
367 }
368 }
369
370 match self.interpolation_type.as_str() {
372 "slerp" => self.interpolate_slerp(t, idx),
373 "cubic" => self.interpolate_cubic(t, idx),
374 _ => self.interpolate_slerp(t, idx), }
376 }
377
378 fn interpolate_slerp(&self, t: f64, idx: usize) -> Rotation {
380 let t0 = self.times[idx];
381 let t1 = self.times[idx + 1];
382 let normalized_t = (t - t0) / (t1 - t0);
383
384 let slerp =
386 Slerp::new(self.rotations[idx].clone(), self.rotations[idx + 1].clone()).unwrap();
387
388 slerp.interpolate(normalized_t)
389 }
390
391 fn interpolate_cubic(&self, t: f64, idx: usize) -> Rotation {
393 if self.velocities.is_none() {
395 let mut mutable_self = self.clone();
396 mutable_self.compute_velocities();
397 return mutable_self.interpolate_cubic(t, idx);
398 }
399
400 let t0 = self.times[idx];
401 let t1 = self.times[idx + 1];
402 let dt = t1 - t0;
403 let normalized_t = (t - t0) / dt;
404
405 let rot0 = &self.rotations[idx];
406 let rot1 = &self.rotations[idx + 1];
407
408 let rotvec0 = rot0.as_rotvec();
410 let rotvec1 = rot1.as_rotvec();
411
412 let velocities = self.velocities.as_ref().unwrap();
414 let vel0 = &velocities[idx];
415 let vel1 = &velocities[idx + 1];
416
417 let t2 = normalized_t * normalized_t;
421 let t3 = t2 * normalized_t;
422
423 let h00 = 2.0 * t3 - 3.0 * t2 + 1.0;
425 let h10 = t3 - 2.0 * t2 + normalized_t;
426 let h01 = -2.0 * t3 + 3.0 * t2;
427 let h11 = t3 - t2;
428
429 let mut result = rotvec0 * h00;
431 result = &result + &(vel0 * dt * h10);
432 result = &result + &(rotvec1 * h01);
433 result = &result + &(vel1 * dt * h11);
434
435 Rotation::from_rotvec(&result.view()).unwrap()
437 }
438
439 pub fn times(&self) -> &Vec<f64> {
462 &self.times
463 }
464
465 pub fn rotations(&self) -> &Vec<Rotation> {
488 &self.rotations
489 }
490
491 pub fn sample(&self, n: usize) -> (Vec<f64>, Vec<Rotation>) {
521 if n <= 1 {
522 return (vec![self.times[0]], vec![self.rotations[0].clone()]);
523 }
524
525 let t_min = self.times[0];
526 let t_max = self.times[self.times.len() - 1];
527
528 let mut sampled_times = Vec::with_capacity(n);
529 let mut sampled_rotations = Vec::with_capacity(n);
530
531 for i in 0..n {
532 let t = t_min + (t_max - t_min) * (i as f64 / (n - 1) as f64);
533 sampled_times.push(t);
534 sampled_rotations.push(self.interpolate(t));
535 }
536
537 (sampled_times, sampled_rotations)
538 }
539
540 pub fn from_key_rotations(_key_rots: &[Rotation], keytimes: &[f64]) -> SpatialResult<Self> {
570 Self::new(_key_rots, keytimes)
571 }
572
573 pub fn interpolation_type(&self) -> &'_ str {
595 &self.interpolation_type
596 }
597
598 pub fn angular_velocity(&self, t: f64) -> SpatialResult<Array1<f64>> {
627 let n = self.times.len();
628
629 if t <= self.times[0] || t >= self.times[n - 1] {
631 return Ok(Array1::zeros(3));
632 }
633
634 let mut idx = 0;
636 for i in 0..n - 1 {
637 if t >= self.times[i] && t < self.times[i + 1] {
638 idx = i;
639 break;
640 }
641 }
642
643 match self.interpolation_type.as_str() {
645 "slerp" => self.angular_velocity_slerp(t, idx),
646 "cubic" => Ok(self.angular_velocity_cubic(t, idx)),
647 _ => self.angular_velocity_slerp(t, idx), }
649 }
650
651 fn angular_velocity_slerp(&self, t: f64, idx: usize) -> SpatialResult<Array1<f64>> {
653 let t0 = self.times[idx];
654 let t1 = self.times[idx + 1];
655 let dt = t1 - t0;
656 let normalized_t = (t - t0) / dt;
657
658 let r0 = &self.rotations[idx];
660 let r1 = &self.rotations[idx + 1];
661
662 let delta_rot = r0.inv().compose(r1);
664
665 let rotvec = delta_rot.as_rotvec();
667 let angle = (rotvec.dot(&rotvec)).sqrt();
668 let axis = if angle > 1e-10 {
669 &rotvec / angle
670 } else {
671 Array1::zeros(3)
672 };
673
674 let slerp = Slerp::new(r0.clone(), r1.clone()).unwrap();
681 let rot_t = slerp.interpolate(normalized_t);
682
683 let angular_rate = angle / dt;
685 let omega_global = axis * angular_rate;
686
687 rot_t.inv().apply(&omega_global.view())
690 }
691
692 fn angular_velocity_cubic(&self, t: f64, idx: usize) -> Array1<f64> {
694 if self.velocities.is_none() {
696 let mut mutable_self = self.clone();
697 mutable_self.compute_velocities();
698 return mutable_self.angular_velocity_cubic(t, idx);
699 }
700
701 let t0 = self.times[idx];
702 let t1 = self.times[idx + 1];
703 let dt = t1 - t0;
704 let normalized_t = (t - t0) / dt;
705
706 let rot0 = &self.rotations[idx];
707 let rot1 = &self.rotations[idx + 1];
708
709 let rotvec0 = rot0.as_rotvec();
711 let rotvec1 = rot1.as_rotvec();
712
713 let velocities = self.velocities.as_ref().unwrap();
715 let vel0 = &velocities[idx];
716 let vel1 = &velocities[idx + 1];
717
718 let dh00_dt = (6.0 * normalized_t.powi(2) - 6.0 * normalized_t) / dt;
720 let dh10_dt = (3.0 * normalized_t.powi(2) - 4.0 * normalized_t + 1.0) / dt;
721 let dh01_dt = (-6.0 * normalized_t.powi(2) + 6.0 * normalized_t) / dt;
722 let dh11_dt = (3.0 * normalized_t.powi(2) - 2.0 * normalized_t) / dt;
723
724 let mut d_rotvec_dt = &rotvec0 * dh00_dt;
726 d_rotvec_dt = &d_rotvec_dt + &(vel0 * dt * dh10_dt);
727 d_rotvec_dt = &d_rotvec_dt + &(&rotvec1 * dh01_dt);
728 d_rotvec_dt = &d_rotvec_dt + &(vel1 * dt * dh11_dt);
729
730 d_rotvec_dt
733 }
734
735 pub fn angular_acceleration(&self, t: f64) -> Array1<f64> {
767 if self.interpolation_type != "cubic" {
769 return Array1::zeros(3); }
771
772 let n = self.times.len();
773
774 if t <= self.times[0] || t >= self.times[n - 1] {
776 return Array1::zeros(3);
777 }
778
779 let mut idx = 0;
781 for i in 0..n - 1 {
782 if t >= self.times[i] && t < self.times[i + 1] {
783 idx = i;
784 break;
785 }
786 }
787
788 self.angular_acceleration_cubic(t, idx)
790 }
791
792 fn angular_acceleration_cubic(&self, t: f64, idx: usize) -> Array1<f64> {
794 if self.velocities.is_none() {
796 let mut mutable_self = self.clone();
797 mutable_self.compute_velocities();
798 return mutable_self.angular_acceleration_cubic(t, idx);
799 }
800
801 let t0 = self.times[idx];
802 let t1 = self.times[idx + 1];
803 let dt = t1 - t0;
804 let normalized_t = (t - t0) / dt;
805
806 let rot0 = &self.rotations[idx];
807 let rot1 = &self.rotations[idx + 1];
808
809 let rotvec0 = rot0.as_rotvec();
811 let rotvec1 = rot1.as_rotvec();
812
813 let velocities = self.velocities.as_ref().unwrap();
815 let vel0 = &velocities[idx];
816 let vel1 = &velocities[idx + 1];
817
818 let d2h00_dt2 = (12.0 * normalized_t - 6.0) / (dt * dt);
820 let d2h10_dt2 = (6.0 * normalized_t - 4.0) / (dt * dt);
821 let d2h01_dt2 = (-12.0 * normalized_t + 6.0) / (dt * dt);
822 let d2h11_dt2 = (6.0 * normalized_t - 2.0) / (dt * dt);
823
824 let mut d2_rotvec_dt2 = &rotvec0 * d2h00_dt2;
826 d2_rotvec_dt2 = &d2_rotvec_dt2 + &(vel0 * dt * d2h10_dt2);
827 d2_rotvec_dt2 = &d2_rotvec_dt2 + &(&rotvec1 * d2h01_dt2);
828 d2_rotvec_dt2 = &d2_rotvec_dt2 + &(vel1 * dt * d2h11_dt2);
829
830 d2_rotvec_dt2
832 }
833}
834
835#[cfg(test)]
836mod tests {
837 use super::*;
838 use approx::assert_relative_eq;
839 use std::f64::consts::PI;
840
841 #[test]
842 fn test_rotation_spline_creation() {
843 let rotations = vec![
844 Rotation::identity(),
845 rotation_from_euler(0.0, 0.0, PI / 2.0, "xyz").unwrap(),
846 Rotation::from_euler(&array![0.0, 0.0, PI].view(), "xyz").unwrap(),
847 ];
848 let times = vec![0.0, 1.0, 2.0];
849
850 let spline = RotationSpline::new(&rotations, ×).unwrap();
851
852 assert_eq!(spline.rotations().len(), 3);
853 assert_eq!(spline.times().len(), 3);
854 assert_eq!(spline.interpolation_type(), "slerp");
855 }
856
857 #[test]
858 fn test_rotation_spline_interpolation_endpoints() {
859 let rotations = vec![
860 Rotation::identity(),
861 rotation_from_euler(0.0, 0.0, PI / 2.0, "xyz").unwrap(),
862 Rotation::from_euler(&array![0.0, 0.0, PI].view(), "xyz").unwrap(),
863 ];
864 let times = vec![0.0, 1.0, 2.0];
865
866 let spline = RotationSpline::new(&rotations, ×).unwrap();
867
868 let interp_start = spline.interpolate(0.0);
870 let interp_end = spline.interpolate(2.0);
871
872 assert_eq!(interp_start.as_quat(), rotations[0].as_quat());
874 assert_eq!(interp_end.as_quat(), rotations[2].as_quat());
875
876 let before_start = spline.interpolate(-1.0);
878 let after_end = spline.interpolate(3.0);
879
880 assert_eq!(before_start.as_quat(), rotations[0].as_quat());
881 assert_eq!(after_end.as_quat(), rotations[2].as_quat());
882 }
883
884 #[test]
885 fn test_rotation_spline_interpolation_midpoints() {
886 let rotations = vec![
887 Rotation::identity(),
888 rotation_from_euler(0.0, 0.0, PI / 2.0, "xyz").unwrap(),
889 Rotation::from_euler(&array![0.0, 0.0, PI].view(), "xyz").unwrap(),
890 ];
891 let times = vec![0.0, 1.0, 2.0];
892
893 let spline = RotationSpline::new(&rotations, ×).unwrap();
894
895 let interp_mid1 = spline.interpolate(0.5);
897 let interp_mid2 = spline.interpolate(1.5);
898
899 let test_point = array![1.0, 0.0, 0.0];
901
902 let rotated_mid1 = interp_mid1.apply(&test_point.view()).unwrap();
904 let rotated_mid2 = interp_mid2.apply(&test_point.view()).unwrap();
905
906 assert_relative_eq!(rotated_mid1[0], 2.0_f64.sqrt() / 2.0, epsilon = 1e-3);
908 assert_relative_eq!(rotated_mid1[1], 2.0_f64.sqrt() / 2.0, epsilon = 1e-3);
909 assert_relative_eq!(rotated_mid1[2], 0.0, epsilon = 1e-3);
910
911 assert_relative_eq!(rotated_mid2[0], -2.0_f64.sqrt() / 2.0, epsilon = 1e-3);
913 assert_relative_eq!(rotated_mid2[1], 2.0_f64.sqrt() / 2.0, epsilon = 1e-3);
914 assert_relative_eq!(rotated_mid2[2], 0.0, epsilon = 1e-3);
915 }
916
917 #[test]
918 fn test_rotation_spline_sampling() {
919 let rotations = vec![
920 Rotation::identity(),
921 Rotation::from_euler(&array![0.0, 0.0, PI].view(), "xyz").unwrap(),
922 ];
923 let times = vec![0.0, 1.0];
924
925 let spline = RotationSpline::new(&rotations, ×).unwrap();
926
927 let (sample_times, sample_rotations) = spline.sample(5);
929
930 assert_eq!(sample_times.len(), 5);
931 assert_eq!(sample_rotations.len(), 5);
932
933 assert_relative_eq!(sample_times[0], 0.0, epsilon = 1e-10);
935 assert_relative_eq!(sample_times[1], 0.25, epsilon = 1e-10);
936 assert_relative_eq!(sample_times[2], 0.5, epsilon = 1e-10);
937 assert_relative_eq!(sample_times[3], 0.75, epsilon = 1e-10);
938 assert_relative_eq!(sample_times[4], 1.0, epsilon = 1e-10);
939
940 let point = array![1.0, 0.0, 0.0];
942
943 let rot0 = &sample_rotations[0];
945 let rotated0 = rot0.apply(&point.view()).unwrap();
946 assert_relative_eq!(rotated0[0], 1.0, epsilon = 1e-10);
947 assert_relative_eq!(rotated0[1], 0.0, epsilon = 1e-10);
948
949 let rot2 = &sample_rotations[2];
951 let rotated2 = rot2.apply(&point.view()).unwrap();
952 assert_relative_eq!(rotated2[0], 0.0, epsilon = 1e-3);
953 assert_relative_eq!(rotated2[1], 1.0, epsilon = 1e-3);
954 assert_relative_eq!(rotated2[2], 0.0, epsilon = 1e-3);
955
956 let rot4 = &sample_rotations[4];
958 let rotated4 = rot4.apply(&point.view()).unwrap();
959 assert_relative_eq!(rotated4[0], -1.0, epsilon = 1e-10);
960 assert_relative_eq!(rotated4[1], 0.0, epsilon = 1e-10);
961 assert_relative_eq!(rotated4[2], 0.0, epsilon = 1e-10);
962 }
963
964 #[test]
965 fn test_rotation_spline_errors() {
966 let result = RotationSpline::new(&[], &[0.0]);
968 assert!(result.is_err());
969
970 let rotations = vec![Rotation::identity()];
972 let result = RotationSpline::new(&rotations, &[]);
973 assert!(result.is_err());
974
975 let rotations = vec![Rotation::identity(), Rotation::identity()];
977 let times = vec![0.0];
978 let result = RotationSpline::new(&rotations, ×);
979 assert!(result.is_err());
980
981 let rotations = vec![Rotation::identity(), Rotation::identity()];
983 let times = vec![1.0, 0.0];
984 let result = RotationSpline::new(&rotations, ×);
985 assert!(result.is_err());
986
987 let rotations = vec![Rotation::identity(), Rotation::identity()];
989 let times = vec![0.0, 0.0];
990 let result = RotationSpline::new(&rotations, ×);
991 assert!(result.is_err());
992
993 let rotations = vec![Rotation::identity(), Rotation::identity()];
995 let times = vec![0.0, 1.0];
996 let mut spline = RotationSpline::new(&rotations, ×).unwrap();
997 let result = spline.set_interpolation_type("invalid");
998 assert!(result.is_err());
999 }
1000
1001 #[test]
1002 fn test_interpolation_types() {
1003 let rotations = vec![
1004 Rotation::identity(),
1005 rotation_from_euler(0.0, 0.0, PI / 2.0, "xyz").unwrap(),
1006 Rotation::from_euler(&array![0.0, 0.0, PI].view(), "xyz").unwrap(),
1007 ];
1008 let times = vec![0.0, 1.0, 2.0];
1009
1010 let mut spline = RotationSpline::new(&rotations, ×).unwrap();
1011
1012 assert_eq!(spline.interpolation_type(), "slerp");
1014
1015 spline.set_interpolation_type("cubic").unwrap();
1017 assert_eq!(spline.interpolation_type(), "cubic");
1018
1019 assert!(spline.velocities.is_some());
1021
1022 spline.set_interpolation_type("slerp").unwrap();
1024 assert_eq!(spline.interpolation_type(), "slerp");
1025
1026 assert!(spline.velocities.is_none());
1028 }
1029
1030 #[test]
1031 fn test_angular_velocity() {
1032 let rotations = vec![
1033 Rotation::identity(),
1034 Rotation::from_euler(&array![0.0, 0.0, PI].view(), "xyz").unwrap(),
1035 ];
1036 let times = vec![0.0, 1.0];
1037
1038 let spline = RotationSpline::new(&rotations, ×).unwrap();
1039
1040 let velocity = spline.angular_velocity(0.5).unwrap();
1042
1043 assert_relative_eq!(velocity[0], 0.0, epsilon = 1e-3);
1046 assert_relative_eq!(velocity[1], 0.0, epsilon = 1e-3);
1047 assert_relative_eq!(velocity[2], PI, epsilon = 1e-3);
1048
1049 let velocity_25 = spline.angular_velocity(0.25).unwrap();
1051 let velocity_75 = spline.angular_velocity(0.75).unwrap();
1052
1053 assert_relative_eq!(velocity_25[0], velocity[0], epsilon = 1e-10);
1054 assert_relative_eq!(velocity_25[1], velocity[1], epsilon = 1e-10);
1055 assert_relative_eq!(velocity_25[2], velocity[2], epsilon = 1e-10);
1056
1057 assert_relative_eq!(velocity_75[0], velocity[0], epsilon = 1e-10);
1058 assert_relative_eq!(velocity_75[1], velocity[1], epsilon = 1e-10);
1059 assert_relative_eq!(velocity_75[2], velocity[2], epsilon = 1e-10);
1060 }
1061
1062 #[test]
1063 fn test_cubic_interpolation() {
1064 let rotations = vec![
1065 Rotation::identity(),
1066 rotation_from_euler(0.0, 0.0, PI / 2.0, "xyz").unwrap(),
1067 Rotation::from_euler(&array![0.0, 0.0, PI].view(), "xyz").unwrap(),
1068 ];
1069 let times = vec![0.0, 1.0, 2.0];
1070
1071 let mut spline = RotationSpline::new(&rotations, ×).unwrap();
1072
1073 spline.set_interpolation_type("cubic").unwrap();
1075
1076 let rot_0 = spline.interpolate(0.0);
1078 let rot_1 = spline.interpolate(1.0);
1079 let rot_2 = spline.interpolate(2.0);
1080
1081 let test_point = array![1.0, 0.0, 0.0];
1082
1083 let rotated_0 = rot_0.apply(&test_point.view()).unwrap();
1085 let expected_0 = rotations[0].apply(&test_point.view()).unwrap();
1086 assert_relative_eq!(rotated_0[0], expected_0[0], epsilon = 1e-10);
1087 assert_relative_eq!(rotated_0[1], expected_0[1], epsilon = 1e-10);
1088 assert_relative_eq!(rotated_0[2], expected_0[2], epsilon = 1e-10);
1089
1090 let rotated_1 = rot_1.apply(&test_point.view()).unwrap();
1091 let expected_1 = rotations[1].apply(&test_point.view()).unwrap();
1092 assert_relative_eq!(rotated_1[0], expected_1[0], epsilon = 1e-10);
1093 assert_relative_eq!(rotated_1[1], expected_1[1], epsilon = 1e-10);
1094 assert_relative_eq!(rotated_1[2], expected_1[2], epsilon = 1e-10);
1095
1096 let rotated_2 = rot_2.apply(&test_point.view()).unwrap();
1097 let expected_2 = rotations[2].apply(&test_point.view()).unwrap();
1098 assert_relative_eq!(rotated_2[0], expected_2[0], epsilon = 1e-10);
1099 assert_relative_eq!(rotated_2[1], expected_2[1], epsilon = 1e-10);
1100 assert_relative_eq!(rotated_2[2], expected_2[2], epsilon = 1e-10);
1101
1102 let rot_05 = spline.interpolate(0.5);
1105 let rot_15 = spline.interpolate(1.5);
1106
1107 let rotated_05 = rot_05.apply(&test_point.view()).unwrap();
1109 let rotated_15 = rot_15.apply(&test_point.view()).unwrap();
1110
1111 let norm_05 = (rotated_05.dot(&rotated_05)).sqrt();
1113 let norm_15 = (rotated_15.dot(&rotated_15)).sqrt();
1114 assert_relative_eq!(norm_05, 1.0, epsilon = 1e-10);
1115 assert_relative_eq!(norm_15, 1.0, epsilon = 1e-10);
1116 }
1117
1118 #[test]
1119 fn test_angular_acceleration() {
1120 let rotations = vec![
1121 Rotation::identity(),
1122 rotation_from_euler(0.0, 0.0, PI / 2.0, "xyz").unwrap(),
1123 Rotation::from_euler(&array![0.0, 0.0, PI].view(), "xyz").unwrap(),
1124 ];
1125 let times = vec![0.0, 1.0, 2.0];
1126
1127 let mut spline = RotationSpline::new(&rotations, ×).unwrap();
1128
1129 let accel_slerp = spline.angular_acceleration(0.5);
1131 assert_relative_eq!(accel_slerp[0], 0.0, epsilon = 1e-10);
1132 assert_relative_eq!(accel_slerp[1], 0.0, epsilon = 1e-10);
1133 assert_relative_eq!(accel_slerp[2], 0.0, epsilon = 1e-10);
1134
1135 spline.set_interpolation_type("cubic").unwrap();
1137
1138 let _accel_cubic = spline.angular_acceleration(0.5);
1140
1141 let complex_rotations = vec![
1144 Rotation::identity(),
1145 {
1146 let angles = array![PI / 2.0, 0.0, 0.0];
1147 Rotation::from_euler(&angles.view(), "xyz").unwrap()
1148 },
1149 {
1150 let angles = array![PI / 2.0, PI / 2.0, 0.0];
1151 Rotation::from_euler(&angles.view(), "xyz").unwrap()
1152 },
1153 ];
1154 let complex_times = vec![0.0, 1.0, 2.0];
1155
1156 let mut complex_spline = RotationSpline::new(&complex_rotations, &complex_times).unwrap();
1157 complex_spline.set_interpolation_type("cubic").unwrap();
1158
1159 let complex_accel = complex_spline.angular_acceleration(0.5);
1160
1161 let magnitude = (complex_accel.dot(&complex_accel)).sqrt();
1163 assert!(magnitude > 1e-6); }
1165}