#![cfg(feature = "geometry")]
use gemath::*;
const EPS: f32 = if cfg!(feature = "libm") { 1e-5 } else { 1e-6 };
fn approx_eq(a: f32, b: f32) -> bool {
(a - b).abs() <= EPS
}
#[test]
fn obb2_contains_and_closest_point_axis_aligned() {
let obb: Obb2<(), ()> = Obb2::from_center_half_extents(Vec2::new(0.0, 0.0), Vec2::new(1.0, 2.0));
assert!(obb.contains_point(Vec2::new(0.0, 0.0)));
assert!(obb.contains_point(Vec2::new(1.0, 2.0))); assert!(!obb.contains_point(Vec2::new(1.0001, 0.0)));
let p = obb.closest_point(Vec2::new(10.0, -10.0));
assert!(approx_eq(p.x, 1.0));
assert!(approx_eq(p.y, -2.0));
}
#[test]
fn obb2_rotated_contains_touching_and_closest_point() {
let center = Vec2::new(0.0, 0.0);
let half = Vec2::new(2.0, 1.0);
let angle = Radians(core::f32::consts::FRAC_PI_4);
let obb: Obb2<(), ()> = Obb2::from_center_half_extents_rotation_radians(center, half, angle);
let axis_x = Vec2::new(1.0, 0.0).rotate(angle);
let on_face = center + axis_x * half.x;
assert!(obb.contains_point(on_face));
let outside = center + axis_x * (half.x + 0.25);
assert!(!obb.contains_point(outside));
let cp = obb.closest_point(outside);
assert!((cp - on_face).length() <= EPS * 10.0);
}
#[test]
fn obb2_try_from_axes_rejects_non_orthonormal() {
let center: Vec2<(), ()> = Vec2::new(0.0, 0.0);
let half: Vec2<(), ()> = Vec2::new(1.0, 1.0);
let axis_x: Vec2<(), ()> = Vec2::new(2.0, 0.0); let axis_y: Vec2<(), ()> = Vec2::new(0.0, 1.0);
assert!(Obb2::try_from_axes(center, half, axis_x, axis_y).is_none());
}
#[test]
fn obb3_contains_and_closest_point_axis_aligned() {
let obb: Obb3<(), ()> =
Obb3::from_center_half_extents(Vec3::new(0.0, 0.0, 0.0), Vec3::new(1.0, 2.0, 3.0));
assert!(obb.contains_point(Vec3::new(1.0, 2.0, 3.0))); assert!(!obb.contains_point(Vec3::new(1.0, 2.0, 3.0001)));
let p = obb.closest_point(Vec3::new(-10.0, 10.0, 10.0));
assert!(approx_eq(p.x, -1.0));
assert!(approx_eq(p.y, 2.0));
assert!(approx_eq(p.z, 3.0));
}
#[test]
fn obb3_from_axis_angle_matches_vec3_rotation() {
let center: Vec3<(), ()> = Vec3::new(0.0, 0.0, 0.0);
let half: Vec3<(), ()> = Vec3::new(1.0, 1.0, 1.0);
let axis: Vec3<(), ()> = Vec3::new(0.0, 1.0, 0.0);
let angle = Radians(core::f32::consts::FRAC_PI_2);
let obb = Obb3::from_axis_angle_radians(center, half, axis, angle).unwrap();
let expected = Vec3::new(1.0, 0.0, 0.0).rotate_axis(axis.normalize(), angle);
assert!((obb.axis_x - expected).length() <= EPS * 10.0);
}
#[test]
fn obb3_from_axis_angle_deg_works() {
let center: Vec3<(), ()> = Vec3::new(0.0, 0.0, 0.0);
let half: Vec3<(), ()> = Vec3::new(1.0, 1.0, 1.0);
let axis: Vec3<(), ()> = Vec3::new(0.0, 1.0, 0.0);
let obb = Obb3::from_axis_angle_deg(center, half, axis, Degrees(90.0)).unwrap();
assert!(obb.contains_point(Vec3::new(0.0, 0.0, 0.0)));
}
#[cfg(feature = "aabb2")]
#[test]
fn obb2_to_aabb_contains_all_corners() {
use gemath::Aabb2;
let obb: Obb2<(), ()> =
Obb2::from_center_half_extents_rotation_radians(Vec2::new(1.0, -2.0), Vec2::new(2.0, 1.0), Radians(0.7));
let aabb: Aabb2<(), ()> = obb.to_aabb();
let min = aabb.min();
let max = aabb.max();
for c in obb.corners() {
assert!(c.x + EPS >= min.x && c.x - EPS <= max.x);
assert!(c.y + EPS >= min.y && c.y - EPS <= max.y);
}
}
#[cfg(feature = "aabb3")]
#[test]
fn obb3_to_aabb_contains_all_corners() {
use gemath::Aabb3;
let obb: Obb3<(), ()> = Obb3::from_axis_angle_radians(
Vec3::new(1.0, 2.0, 3.0),
Vec3::new(1.0, 2.0, 1.0),
Vec3::new(1.0, 1.0, 0.0),
Radians(0.8),
)
.unwrap();
let aabb: Aabb3<(), ()> = obb.to_aabb();
let min = aabb.min();
let max = aabb.max();
for c in obb.corners() {
assert!(c.x + EPS >= min.x && c.x - EPS <= max.x);
assert!(c.y + EPS >= min.y && c.y - EPS <= max.y);
assert!(c.z + EPS >= min.z && c.z - EPS <= max.z);
}
}