use approx::assert_relative_eq;
use scirs2_core::ndarray::array;
use std::f64::consts::PI;
use scirs2_spatial::transform::{Rotation, RotationSpline, Slerp};
#[test]
#[allow(dead_code)]
fn test_rotation_basic() {
let rot_identity = Rotation::identity();
let point_arr = array![1.0, 2.0, 3.0];
let point = point_arr.view();
let rotated = rot_identity.apply(&point).expect("Operation failed");
assert_relative_eq!(rotated[0], point[0], epsilon = 1e-10);
assert_relative_eq!(rotated[1], point[1], epsilon = 1e-10);
assert_relative_eq!(rotated[2], point[2], epsilon = 1e-10);
let euler_z_arr = array![0.0, 0.0, PI / 2.0];
let euler_z = euler_z_arr.view();
let rot_z = Rotation::from_euler(&euler_z, "xyz").expect("Operation failed");
let p_x_arr = array![1.0, 0.0, 0.0];
let p_x = p_x_arr.view();
let rotated_z = rot_z.apply(&p_x).expect("Operation failed");
assert_relative_eq!(rotated_z[0], 0.0, epsilon = 1e-10);
assert_relative_eq!(rotated_z[1], 1.0, epsilon = 1e-10);
assert_relative_eq!(rotated_z[2], 0.0, epsilon = 1e-10);
let rot_z_inv = rot_z.inv();
let rotated_z_view = rotated_z.view();
let point_back = rot_z_inv.apply(&rotated_z_view).expect("Operation failed");
assert_relative_eq!(point_back[0], 1.0, epsilon = 1e-10);
assert_relative_eq!(point_back[1], 0.0, epsilon = 1e-10);
assert_relative_eq!(point_back[2], 0.0, epsilon = 1e-10);
}
#[test]
#[allow(dead_code)]
fn test_rotation_euler_conventions() {
let test_conventions = ["xyz", "zyx", "xyx", "xzx", "yxy", "yzy", "zxz", "zyz"];
for &convention in &test_conventions {
let angles_arr = match convention {
"xyz" | "zyx" => array![PI / 4.0, 0.0, 0.0], "xyx" | "xzx" => array![0.0, PI / 4.0, 0.0], "yxy" | "yzy" => array![PI / 4.0, 0.0, 0.0], "zxz" | "zyz" => array![0.0, PI / 4.0, 0.0], _ => unreachable!(),
};
let angles = angles_arr.view();
let rotation = Rotation::from_euler(&angles, convention).expect("Operation failed");
let angles_back = rotation.as_euler(convention).expect("Operation failed");
let angles_back_view = angles_back.view();
let point_arr = array![1.0, 1.0, 1.0];
let point = point_arr.view();
let rotated1 = rotation.apply(&point).expect("Operation failed");
let rotation2 =
Rotation::from_euler(&angles_back_view, convention).expect("Operation failed");
let rotated2 = rotation2.apply(&point).expect("Operation failed");
assert_relative_eq!(rotated1[0], rotated2[0], epsilon = 1e-10);
assert_relative_eq!(rotated1[1], rotated2[1], epsilon = 1e-10);
assert_relative_eq!(rotated1[2], rotated2[2], epsilon = 1e-10);
}
}
#[test]
#[allow(dead_code)]
fn test_slerp_basic() {
let rot1 = Rotation::identity();
let euler_pi_arr = array![0.0, 0.0, PI];
let euler_pi = euler_pi_arr.view();
let rot2 = Rotation::from_euler(&euler_pi, "xyz").expect("Operation failed");
let slerp = Slerp::new(rot1, rot2).expect("Operation failed");
let test_point_arr = array![1.0, 0.0, 0.0];
let test_point = test_point_arr.view();
let rot_0 = slerp.interpolate(0.0);
let rot_1 = slerp.interpolate(1.0);
let rotated_0 = rot_0.apply(&test_point).expect("Operation failed");
let rotated_1 = rot_1.apply(&test_point).expect("Operation failed");
assert_relative_eq!(rotated_0[0], 1.0, epsilon = 1e-10);
assert_relative_eq!(rotated_0[1], 0.0, epsilon = 1e-10);
assert_relative_eq!(rotated_0[2], 0.0, epsilon = 1e-10);
assert_relative_eq!(rotated_1[0], -1.0, epsilon = 1e-10);
assert_relative_eq!(rotated_1[1], 0.0, epsilon = 1e-10);
assert_relative_eq!(rotated_1[2], 0.0, epsilon = 1e-10);
let rot_half = slerp.interpolate(0.5);
let rotated_half = rot_half.apply(&test_point).expect("Operation failed");
assert_relative_eq!(rotated_half[0], 0.0, epsilon = 1e-10);
assert_relative_eq!(rotated_half[1], 1.0, epsilon = 1e-10);
assert_relative_eq!(rotated_half[2], 0.0, epsilon = 1e-10);
}
#[test]
#[allow(dead_code)]
fn test_rotation_spline_slerp() {
let rotations = vec![
Rotation::identity(),
{
let euler_90_arr = array![0.0, 0.0, PI / 2.0];
let euler_90 = euler_90_arr.view();
Rotation::from_euler(&euler_90, "xyz").expect("Operation failed")
},
{
let euler_180_arr = array![0.0, 0.0, PI];
let euler_180 = euler_180_arr.view();
Rotation::from_euler(&euler_180, "xyz").expect("Operation failed")
},
];
let times = vec![0.0, 1.0, 2.0];
let spline = RotationSpline::new(&rotations, ×).expect("Operation failed");
assert_eq!(spline.interpolation_type(), "slerp");
let test_point_arr = array![1.0, 0.0, 0.0];
let test_point = test_point_arr.view();
let rot_0 = spline.interpolate(0.0);
let rot_1 = spline.interpolate(1.0);
let rot_2 = spline.interpolate(2.0);
let rotated_0 = rot_0.apply(&test_point).expect("Operation failed");
let rotated_1 = rot_1.apply(&test_point).expect("Operation failed");
let rotated_2 = rot_2.apply(&test_point).expect("Operation failed");
assert_relative_eq!(rotated_0[0], 1.0, epsilon = 1e-10);
assert_relative_eq!(rotated_0[1], 0.0, epsilon = 1e-10);
assert_relative_eq!(rotated_0[2], 0.0, epsilon = 1e-10);
assert_relative_eq!(rotated_1[0], 0.0, epsilon = 1e-10);
assert_relative_eq!(rotated_1[1], 1.0, epsilon = 1e-10);
assert_relative_eq!(rotated_1[2], 0.0, epsilon = 1e-10);
assert_relative_eq!(rotated_2[0], -1.0, epsilon = 1e-10);
assert_relative_eq!(rotated_2[1], 0.0, epsilon = 1e-10);
assert_relative_eq!(rotated_2[2], 0.0, epsilon = 1e-10);
let rot_05 = spline.interpolate(0.5);
let rot_15 = spline.interpolate(1.5);
let rotated_05 = rot_05.apply(&test_point).expect("Operation failed");
let rotated_15 = rot_15.apply(&test_point).expect("Operation failed");
assert_relative_eq!(
rotated_05[0],
std::f64::consts::FRAC_1_SQRT_2,
epsilon = 0.001
);
assert_relative_eq!(
rotated_05[1],
std::f64::consts::FRAC_1_SQRT_2,
epsilon = 0.001
);
assert_relative_eq!(rotated_05[2], 0.0, epsilon = 0.001);
assert_relative_eq!(
rotated_15[0],
-std::f64::consts::FRAC_1_SQRT_2,
epsilon = 0.001
);
assert_relative_eq!(
rotated_15[1],
std::f64::consts::FRAC_1_SQRT_2,
epsilon = 0.001
);
assert_relative_eq!(rotated_15[2], 0.0, epsilon = 0.001);
}
#[test]
#[allow(dead_code)]
fn test_rotation_spline_cubic() {
let rotations = vec![
Rotation::identity(),
{
let euler_90_arr = array![0.0, 0.0, PI / 2.0];
let euler_90 = euler_90_arr.view();
Rotation::from_euler(&euler_90, "xyz").expect("Operation failed")
},
{
let euler_180_arr = array![0.0, 0.0, PI];
let euler_180 = euler_180_arr.view();
Rotation::from_euler(&euler_180, "xyz").expect("Operation failed")
},
];
let times = vec![0.0, 1.0, 2.0];
let mut spline = RotationSpline::new(&rotations, ×).expect("Operation failed");
spline
.set_interpolation_type("cubic")
.expect("Operation failed");
assert_eq!(spline.interpolation_type(), "cubic");
let test_point_arr = array![1.0, 0.0, 0.0];
let test_point = test_point_arr.view();
let rot_0 = spline.interpolate(0.0);
let rot_1 = spline.interpolate(1.0);
let rot_2 = spline.interpolate(2.0);
let rotated_0 = rot_0.apply(&test_point).expect("Operation failed");
let rotated_1 = rot_1.apply(&test_point).expect("Operation failed");
let rotated_2 = rot_2.apply(&test_point).expect("Operation failed");
assert_relative_eq!(rotated_0[0], 1.0, epsilon = 1e-10);
assert_relative_eq!(rotated_0[1], 0.0, epsilon = 1e-10);
assert_relative_eq!(rotated_0[2], 0.0, epsilon = 1e-10);
assert_relative_eq!(rotated_1[0], 0.0, epsilon = 1e-10);
assert_relative_eq!(rotated_1[1], 1.0, epsilon = 1e-10);
assert_relative_eq!(rotated_1[2], 0.0, epsilon = 1e-10);
assert_relative_eq!(rotated_2[0], -1.0, epsilon = 1e-10);
assert_relative_eq!(rotated_2[1], 0.0, epsilon = 1e-10);
assert_relative_eq!(rotated_2[2], 0.0, epsilon = 1e-10);
let rot_05 = spline.interpolate(0.5);
let rot_15 = spline.interpolate(1.5);
let rotated_05 = rot_05.apply(&test_point).expect("Operation failed");
let rotated_15 = rot_15.apply(&test_point).expect("Operation failed");
let norm_05 = (rotated_05[0] * rotated_05[0]
+ rotated_05[1] * rotated_05[1]
+ rotated_05[2] * rotated_05[2])
.sqrt();
let norm_15 = (rotated_15[0] * rotated_15[0]
+ rotated_15[1] * rotated_15[1]
+ rotated_15[2] * rotated_15[2])
.sqrt();
assert_relative_eq!(norm_05, 1.0, epsilon = 1e-10);
assert_relative_eq!(norm_15, 1.0, epsilon = 1e-10);
}
#[test]
#[allow(dead_code)]
fn test_rotation_spline_angular_velocity() {
let rotations = vec![
Rotation::identity(),
{
let euler_180_arr = array![0.0, 0.0, PI];
let euler_180 = euler_180_arr.view();
Rotation::from_euler(&euler_180, "xyz").expect("Operation failed")
}, ];
let times = vec![0.0, 1.0];
let spline = RotationSpline::new(&rotations, ×).expect("Operation failed");
let velocity = spline.angular_velocity(0.5).expect("Operation failed");
assert_relative_eq!(velocity[0], 0.0, epsilon = 1e-3);
assert_relative_eq!(velocity[1], 0.0, epsilon = 1e-3);
assert_relative_eq!(velocity[2], PI, epsilon = 1e-3);
let velocity_start = spline.angular_velocity(0.0).expect("Operation failed");
let velocity_end = spline.angular_velocity(1.0).expect("Operation failed");
assert_relative_eq!(
(velocity_start.dot(&velocity_start)).sqrt(),
0.0,
epsilon = 1e-10
);
assert_relative_eq!(
(velocity_end.dot(&velocity_end)).sqrt(),
0.0,
epsilon = 1e-10
);
let mut cubic_spline = RotationSpline::new(&rotations, ×).expect("Operation failed");
cubic_spline
.set_interpolation_type("cubic")
.expect("Operation failed");
let cubic_velocity = cubic_spline
.angular_velocity(0.5)
.expect("Operation failed");
let cubic_magnitude = (cubic_velocity.dot(&cubic_velocity)).sqrt();
assert!(cubic_magnitude > 0.0); }
#[test]
#[allow(dead_code)]
fn test_rotation_spline_angular_acceleration() {
let rotations = vec![
Rotation::identity(),
{
let euler_90_arr = array![0.0, 0.0, PI / 2.0];
let euler_90 = euler_90_arr.view();
Rotation::from_euler(&euler_90, "xyz").expect("Operation failed")
},
{
let euler_180_arr = array![0.0, 0.0, PI];
let euler_180 = euler_180_arr.view();
Rotation::from_euler(&euler_180, "xyz").expect("Operation failed")
},
];
let times = vec![0.0, 1.0, 2.0];
let spline = RotationSpline::new(&rotations, ×).expect("Operation failed");
let accel = spline.angular_acceleration(0.5);
assert_relative_eq!(accel[0], 0.0, epsilon = 1e-10);
assert_relative_eq!(accel[1], 0.0, epsilon = 1e-10);
assert_relative_eq!(accel[2], 0.0, epsilon = 1e-10);
let mut cubic_spline = RotationSpline::new(&rotations, ×).expect("Operation failed");
cubic_spline
.set_interpolation_type("cubic")
.expect("Operation failed");
let complex_rotations = vec![
Rotation::identity(),
{
let euler_1_arr = array![0.0, PI / 4.0, 0.0];
let euler_1 = euler_1_arr.view();
Rotation::from_euler(&euler_1, "xyz").expect("Operation failed")
},
{
let euler_2_arr = array![PI / 4.0, PI / 4.0, 0.0];
let euler_2 = euler_2_arr.view();
Rotation::from_euler(&euler_2, "xyz").expect("Operation failed")
},
{
let euler_3_arr = array![0.0, 0.0, 0.0];
let euler_3 = euler_3_arr.view();
Rotation::from_euler(&euler_3, "xyz").expect("Operation failed")
},
];
let complex_times = vec![0.0, 1.0, 2.0, 3.0];
let mut complex_spline =
RotationSpline::new(&complex_rotations, &complex_times).expect("Operation failed");
complex_spline
.set_interpolation_type("cubic")
.expect("Operation failed");
let accel_1 = complex_spline.angular_acceleration(0.5);
let accel_2 = complex_spline.angular_acceleration(1.5);
let accel_3 = complex_spline.angular_acceleration(2.5);
let has_accel_1 = accel_1[0].abs() > 1e-6 || accel_1[1].abs() > 1e-6 || accel_1[2].abs() > 1e-6;
let has_accel_2 = accel_2[0].abs() > 1e-6 || accel_2[1].abs() > 1e-6 || accel_2[2].abs() > 1e-6;
let has_accel_3 = accel_3[0].abs() > 1e-6 || accel_3[1].abs() > 1e-6 || accel_3[2].abs() > 1e-6;
assert!(has_accel_1 || has_accel_2 || has_accel_3);
}