use kernelvex::odom::pose::Pose;
use kernelvex::util::si::QAngle;
use std::f64::consts::PI;
use kernelvex::Vector2;
#[test]
fn test_pose_new() {
let pose = Pose::new(Vector2::<f64>::new(1.5, 2.0), QAngle::from_degrees(45.0));
let pos =pose.position();
assert_eq!(pos.x, 1.5);
assert_eq!(pos.y, 2.0);
assert!((pose.heading().as_degrees() - 45.0).abs() < f64::EPSILON);
}
#[test]
fn test_pose_identity() {
let identity = Pose::identity();
let pos =identity.position();
assert_eq!(pos.x, 0.0);
assert_eq!(pos.y, 0.0);
assert_eq!(identity.heading().as_radians(), 0.0);
}
#[test]
fn test_pose_default() {
let default: Pose = Default::default();
let pos =default.position();
assert_eq!(pos.x, 0.0);
assert_eq!(pos.y, 0.0);
assert_eq!(default.heading().as_radians(), 0.0);
}
#[test]
fn test_pose_position() {
let pose = Pose::new(Vector2::<f64>::new(3.0, 4.0), QAngle::from_degrees(90.0));
let pos =pose.position();
assert_eq!(pos.x, 3.0);
assert_eq!(pos.y, 4.0);
}
#[test]
fn test_pose_heading() {
let pose = Pose::new(Default::default(), QAngle::from_degrees(45.0));
assert!((pose.heading().as_degrees() - 45.0).abs() < f64::EPSILON);
}
#[test]
fn test_pose_distance_horizontal() {
let p1 = Pose::new(Default::default(), QAngle::from_degrees(0.0));
let p2 = Pose::new(Vector2::<f64>::new(3.0, 0.0), QAngle::from_degrees(0.0));
let dist = p1.distance(p2);
assert_eq!(dist.as_meters(), 3.0);
}
#[test]
fn test_pose_distance_vertical() {
let p1 = Pose::new(Default::default(), QAngle::from_degrees(0.0));
let p2 = Pose::new(Vector2::<f64>::new(0., 4.), QAngle::from_degrees(0.0));
let dist = p1.distance(p2);
assert_eq!(dist.as_meters(), 4.0);
}
#[test]
fn test_pose_distance_diagonal() {
let p1 = Pose::new(Default::default(), QAngle::from_degrees(0.0));
let p2 = Pose::new(Vector2::<f64>::new(3.0, 4.0), QAngle::from_degrees(0.0));
let dist = p1.distance(p2);
assert_eq!(dist.as_meters(), 5.0);
}
#[test]
fn test_pose_distance_symmetric() {
let p1 = Pose::new(Vector2::<f64>::new(1.0, 2.0), QAngle::from_degrees(0.0));
let p2 = Pose::new(Vector2::<f64>::new(3.0, 4.0), QAngle::from_degrees(0.0));
let dist1 = p1.distance(p2);
let dist2 = p2.distance(p1);
assert_eq!(dist1.as_meters(), dist2.as_meters());
}
#[test]
fn test_pose_distance_zero() {
let p1 = Pose::new(Vector2::<f64>::new(1.0, 2.0), QAngle::from_degrees(0.0));
let p2 = Pose::new(Vector2::<f64>::new(1.0, 2.0), QAngle::from_degrees(0.0));
let dist = p1.distance(p2);
assert_eq!(dist.as_meters(), 0.0);
}
#[test]
fn test_pose_angle_horizontal() {
let p1 = Pose::new(Default::default(), QAngle::from_degrees(0.0));
let p2 = Pose::new(Vector2::<f64>::new(1.0, 0.0), QAngle::from_degrees(0.0));
let angle = p1.angle(p2);
assert_eq!(angle.as_radians(), 0.0);
}
#[test]
fn test_pose_angle_vertical() {
let p1 = Pose::new(Default::default(), QAngle::from_degrees(0.0));
let p2 = Pose::new(Vector2::<f64>::new(0.0, 1.0), QAngle::from_degrees(0.0));
let angle = p1.angle(p2);
assert!((angle.as_radians() - PI / 2.0).abs() < f64::EPSILON);
}
#[test]
fn test_pose_angle_diagonal() {
let p1 = Pose::new(Default::default(), QAngle::from_degrees(0.0));
let p2 = Pose::new(Vector2::<f64>::new(1.0, 1.0), QAngle::from_degrees(0.0));
let angle = p1.angle(p2);
assert!((angle.as_degrees() - 45.0).abs() < f64::EPSILON);
}
#[test]
fn test_pose_angle_negative_y() {
let p1 = Pose::new(Vector2::<f64>::new(1.0, 1.0), QAngle::from_degrees(0.0));
let p2 = Pose::new(Vector2::<f64>::new(1.0, 0.0), QAngle::from_degrees(0.0));
let angle = p1.angle(p2);
assert_eq!(angle.as_degrees(), -90.);
}
#[test]
fn test_pose_rotate() {
let pose = Pose::new(Vector2::<f64>::new(1.0, 2.0), QAngle::from_degrees(0.0));
let rotated = pose.rotate(QAngle::from_degrees(90.0));
let pos =rotated.position();
assert_eq!(pos.x, 1.0);
assert_eq!(pos.y, 2.0);
assert!((rotated.heading().as_degrees() - 90.0).abs() < f64::EPSILON);
}
#[test]
fn test_pose_rotate_multiple() {
let pose = Pose::new(Default::default(), QAngle::from_degrees(30.0));
let rotated = pose.rotate(QAngle::from_degrees(60.0));
assert!((rotated.heading().as_degrees() - 90.0).abs() < f64::EPSILON);
}
#[test]
fn test_pose_rotate_negative() {
let pose = Pose::new(Vector2::<f64>::new(1.0, 1.0), QAngle::from_degrees(90.0));
let rotated = pose.rotate(QAngle::from_degrees(-45.0));
assert!((rotated.heading().as_degrees() - 45.0).abs() < f64::EPSILON);
}
#[test]
fn test_pose_add() {
let p1 = Pose::new(Vector2::<f64>::new(1., 2.), QAngle::from_degrees(45.0));
let p2 = Pose::new(Vector2::<f64>::new(3., 4.), QAngle::from_degrees(90.0));
let sum = p1 + p2;
let pos =sum.position();
assert_eq!(pos.x, 4.0);
assert_eq!(pos.y, 6.0);
assert!((sum.heading().as_degrees() - 45.0).abs() < f64::EPSILON);
}
#[test]
fn test_pose_add_identity() {
let p1 = Pose::new(Vector2::<f64>::new(1.0, 2.0), QAngle::from_degrees(30.0));
let identity = Pose::identity();
let sum = p1 + identity;
let pos =sum.position();
assert_eq!(pos.x, 1.0);
assert_eq!(pos.y, 2.0);
}
#[test]
fn test_pose_sub() {
let p1 = Pose::new(Vector2::<f64>::new(5.0, 7.0), QAngle::from_degrees(45.0));
let p2 = Pose::new(Vector2::<f64>::new(2.0, 3.0), QAngle::from_degrees(90.0));
let diff = p1 - p2;
let pos =diff.position();
assert_eq!(pos.x, 3.0);
assert_eq!(pos.y, 4.0);
assert!((diff.heading().as_degrees() - 45.0).abs() < f64::EPSILON);
}
#[test]
fn test_pose_sub_identity() {
let p1 = Pose::new(Vector2::<f64>::new(3.0, 4.0), QAngle::from_degrees(60.0));
let identity = Pose::identity();
let diff = p1 - identity;
let pos =diff.position();
assert_eq!(pos.x, 3.0);
assert_eq!(pos.y, 4.0);
assert!((diff.heading().as_degrees() - 60.0).abs() < 1e-10);
}
#[test]
fn test_pose_mul_identity() {
let p1 = Pose::new(Vector2::<f64>::new(1.0, 2.0), QAngle::from_degrees(45.0));
let identity = Pose::identity();
let result = p1 * identity;
let pos = result.position();
assert!((pos.x - 1.0).abs() < f64::EPSILON);
assert!((pos.y - 2.0).abs() < f64::EPSILON);
assert!((result.heading().as_degrees() - 45.0).abs() < f64::EPSILON);
}
#[test]
fn test_pose_mul_translation_only() {
let p1 = Pose::identity();
let p2 = Pose::new(Vector2::<f64>::new(1.0, 2.0), QAngle::from_degrees(0.0));
let result = p1 * p2;
let pos =result.position();
assert!((pos.x - 1.0).abs() < f64::EPSILON);
assert!((pos.y - 2.0).abs() < f64::EPSILON);
}
#[test]
fn test_pose_mul_rotation_only() {
let p1 = Pose::new(Default::default(), QAngle::from_degrees(90.0));
let p2 = Pose::new(Vector2::<f64>::new(1.0, 0.0), QAngle::from_degrees(0.0));
let result = p1 * p2;
let pos =result.position();
assert!((pos.x - 0.0).abs() < f64::EPSILON);
assert!((pos.y - 1.0).abs() < f64::EPSILON);
assert!((result.heading().as_degrees() - 90.0).abs() < f64::EPSILON);
}
#[test]
fn test_pose_mul_combined() {
let p1 = Pose::new(Vector2::<f64>::new(1.0, 0.0), QAngle::from_degrees(90.0));
let p2 = Pose::new(Vector2::<f64>::new(0.0, 1.0), QAngle::from_degrees(90.0));
let result = p1 * p2;
assert!((result.heading().as_degrees() - 180.0).abs() < f64::EPSILON);
}
#[test]
fn test_pose_mul_scalar() {
let pose = Pose::new(Vector2::<f64>::new(2.0, 3.0), QAngle::from_degrees(45.0));
let scaled = pose * 2.0;
let pos =scaled.position();
assert_eq!(pos.x, 4.0);
assert_eq!(pos.y, 6.0);
assert!((scaled.heading().as_degrees() - 45.0).abs() < f64::EPSILON);
}
#[test]
fn test_pose_mul_scalar_zero() {
let pose = Pose::new(Vector2::<f64>::new(1.0, 2.0), QAngle::from_degrees(30.0));
let scaled = pose * 0.0;
let pos =scaled.position();
assert_eq!(pos.x, 0.0);
assert_eq!(pos.y, 0.0);
assert!((scaled.heading().as_degrees() - 30.0).abs() < 1e-10);
}
#[test]
fn test_pose_mul_scalar_negative() {
let pose = Pose::new(Vector2::<f64>::new(1.0, 2.0), QAngle::from_degrees(45.0));
let scaled = pose * -1.0;
let pos =scaled.position();
assert_eq!(pos.x, -1.0);
assert_eq!(pos.y, -2.0);
assert!((scaled.heading().as_degrees() - 45.0).abs() < f64::EPSILON);
}
#[test]
fn test_pose_div_scalar() {
let pose = Pose::new(Vector2::<f64>::new(4.0, 6.0), QAngle::from_degrees(60.0));
let scaled = pose / 2.0;
let pos =scaled.position();
assert_eq!(pos.x, 2.0);
assert_eq!(pos.y, 3.0);
}
#[test]
fn test_pose_div_scalar_fractional() {
let pose = Pose::new(Vector2::<f64>::new(1.0, 2.0), QAngle::from_degrees(45.0));
let scaled = pose / 4.0;
let pos =scaled.position();
assert_eq!(pos.x, 0.25);
assert_eq!(pos.y, 0.5);
}
#[test]
fn test_pose_move_local() {
let robot = Pose::new(Default::default(), QAngle::from_degrees(0.0));
let local = Pose::new(Vector2::<f64>::new(1.0, 0.0), QAngle::from_degrees(0.0));
let global = robot.move_local(local);
let pos =global.position();
assert!((pos.x - 1.0).abs() < f64::EPSILON);
assert!((pos.y - 0.0).abs() < f64::EPSILON);
}
#[test]
fn test_pose_move_local_rotated() {
let robot = Pose::new(Default::default(), QAngle::from_degrees(90.0));
let local = Pose::new(Vector2::<f64>::new(1.0, 0.0), QAngle::from_degrees(0.0));
let global = robot.move_local(local);
let pos =global.position();
assert!((pos.x - 0.0).abs() < f64::EPSILON);
assert!((pos.y - 1.0).abs() < f64::EPSILON);
}
#[test]
fn test_pose_move_local_translated() {
let robot = Pose::new(Vector2::<f64>::new(2.0, 3.0), QAngle::from_degrees(0.0));
let local = Pose::new(Vector2::<f64>::new(1.0, 0.0), QAngle::from_degrees(0.0));
let global = robot.move_local(local);
let pos =global.position();
assert!((pos.x - 3.0).abs() < f64::EPSILON);
assert!((pos.y - 3.0).abs() < f64::EPSILON);
}
#[test]
fn test_pose_move_global() {
let p1 = Pose::new(Vector2::<f64>::new(1.0, 2.0), QAngle::from_degrees(45.0));
let p2 = Pose::new(Vector2::<f64>::new(3.0, 4.0), QAngle::from_degrees(90.0));
let result = p1.move_global(p2);
let pos =result.position();
assert_eq!(pos.x, 1.0);
assert_eq!(pos.y, 5.0);
assert!(result.heading().as_radians() - 135.0 < f64::EPSILON);
}
#[test]
fn test_pose_move_global_zero() {
let p1 = Pose::new(Vector2::<f64>::new(1.0, 2.0), QAngle::from_degrees(30.0));
let zero = Pose::identity();
let result = p1.move_global(zero);
let pos =result.position();
assert_eq!(pos.x, 1.0);
assert_eq!(pos.y, 2.0);
let z = result.heading().as_degrees();
assert!((z - 30.0).abs() < 1e-10);
}
#[test]
fn test_pose_transformation_matrix_zero_rotation() {
let pose = Pose::new(Vector2::<f64>::new(1.0, 2.0), QAngle::from_degrees(0.0));
let pos =pose.position();
assert_eq!(pos.x, 1.0);
assert_eq!(pos.y, 2.0);
assert!((pose.heading().cos() - 1.0).abs() < f64::EPSILON);
assert!((pose.heading().sin() - 0.0).abs() < f64::EPSILON);
}
#[test]
fn test_pose_transformation_matrix_ninety_rotation() {
let pose = Pose::new(Default::default(), QAngle::from_degrees(90.0));
assert!((pose.heading().cos() - 0.0).abs() < f64::EPSILON);
assert!((pose.heading().sin() - 1.0).abs() < f64::EPSILON);
}
#[test]
fn test_pose_chained_transformations() {
let start = Pose::identity();
let forward = Pose::new(Vector2::<f64>::new(1.0, 0.0), QAngle::from_degrees(0.0));
let rotate = Pose::new(Default::default(), QAngle::from_degrees(90.0));
let forward2 = Pose::new(Vector2::<f64>::new(1.0, 0.0), QAngle::from_degrees(0.0));
let result = start * forward * rotate * forward2;
let _position = result.position();
assert!((result.heading().as_degrees() - 90.0).abs() < f64::EPSILON);
}
#[test]
fn test_pose_roundtrip() {
let original = Pose::new(Vector2::<f64>::new(1.5, 2.5), QAngle::from_degrees(45.0));
let pos = original.position();
let heading = original.heading();
let recreated = Pose::new(pos, heading);
let (x2, y2) = (recreated.position().x, recreated.position().y);
assert_eq!(pos.x, x2);
assert_eq!(pos.y, y2);
assert!((heading.as_degrees() - recreated.heading().as_degrees()).abs() < f64::EPSILON);
}
#[test]
fn test_pose_clone() {
let p1 = Pose::new(Vector2::<f64>::new(1.0, 2.0), QAngle::from_degrees(45.0));
let p2 = p1;
let pos1 = p1.position();
let pos2 = p2.position();
assert_eq!(pos1.x, pos2.x);
assert_eq!(pos2.y, pos1.y);
assert!((p1.heading().as_degrees() - p2.heading().as_degrees()).abs() < f64::EPSILON);
}