use super::*;
use crate::common::sealed::ScalarType;
use std::f32;
use vector_traits::glam::{Vec3, Vec3A};
fn make_triangle<S: ScalarType>(v0: S::Vec3, v1: S::Vec3, v2: S::Vec3) -> Triangle<S> {
let edge0 = v1 - v0;
let edge1 = v2 - v0;
let normal = edge0.cross(edge1).normalize();
Triangle { v0, v1, v2, normal }
}
#[test]
fn test_empty_bvh() {
let bvh = StaticBVH::<f32>::new(&[], &[]);
let result = bvh.closest_point(Vec3::ZERO, Vec3A::ZERO, -1.0);
assert!(result.is_none());
}
#[test]
fn test_single_triangle() {
let tri = make_triangle(
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
);
let bvh = StaticBVH::<f32>::new_from_triangles(vec![tri.clone()]);
let query = Vec3::new(0.25, 0.25, 1.0);
let result = bvh.closest_point(query, Vec3A::ZERO, -1.0).unwrap().0;
assert!(
(result.z).abs() < 0.001,
"result: {:?} distance a:{}, distance b:{}, distance c:{}",
result,
query.distance(tri.v0),
query.distance(tri.v1),
query.distance(tri.v2)
); assert!(
(result.x - 0.25).abs() < 0.001,
"result: {:?} distance a:{}, distance b:{}, distance c:{}",
result,
query.distance(tri.v0),
query.distance(tri.v1),
query.distance(tri.v2)
); assert!(
(result.y - 0.25).abs() < 0.001,
"result: {:?} distance a:{}, distance b:{}, distance c:{}",
result,
query.distance(tri.v0),
query.distance(tri.v1),
query.distance(tri.v2)
);
}
#[test]
fn test_project_to_triangle_center() {
let tri = make_triangle(
Vec3::new(-1.0, -1.0, 0.0),
Vec3::new(1.0, -1.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
);
let bvh = StaticBVH::<f32>::new_from_triangles(vec![tri]);
let query = Vec3::new(0.0, 0.0, 5.0);
let result = bvh.closest_point(query, Vec3A::ZERO, -1.0).unwrap().0;
assert!((result.z).abs() < 0.001);
}
#[test]
fn test_project_to_triangle_vertex() {
let tri = make_triangle(
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
);
let bvh = StaticBVH::<f32>::new_from_triangles(vec![tri]);
let query = Vec3::new(-1.0, -1.0, 0.0);
let result = bvh.closest_point(query, Vec3A::ZERO, -1.0).unwrap().0;
assert!(result.distance(Vec3::ZERO) < 0.001);
}
#[test]
fn test_project_to_triangle_edge() {
let tri = make_triangle(
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
);
let bvh = StaticBVH::<f32>::new_from_triangles(vec![tri]);
let query = Vec3::new(0.5, -1.0, 0.0);
let result = bvh.closest_point(query, Vec3A::ZERO, -1.0).unwrap().0;
assert!((result.y).abs() < 0.001);
assert!((result.z).abs() < 0.001);
assert!(result.x >= 0.0 && result.x <= 1.0);
}
#[test]
fn test_multiple_triangles_closest() {
let tri1 = make_triangle(
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
);
let tri2 = make_triangle(
Vec3::new(0.0, 0.0, 5.0),
Vec3::new(1.0, 0.0, 5.0),
Vec3::new(0.0, 1.0, 5.0),
);
let bvh = StaticBVH::<f32>::new_from_triangles(vec![tri1, tri2]);
let query = Vec3::new(0.25, 0.25, 0.5);
let result = bvh.closest_point(query, Vec3A::ZERO, -1.0).unwrap().0;
assert!((result.z).abs() < 0.001);
let query2 = Vec3::new(0.25, 0.25, 4.5);
let result2 = bvh.closest_point(query2, Vec3A::ZERO, -1.0).unwrap().0;
assert!((result2.z - 5.0).abs() < 0.001);
}
#[test]
fn test_separated_triangles() {
let tri1 = make_triangle(
Vec3::new(-10.0, 0.0, 0.0),
Vec3::new(-9.0, 0.0, 0.0),
Vec3::new(-10.0, 1.0, 0.0),
);
let tri2 = make_triangle(
Vec3::new(10.0, 0.0, 0.0),
Vec3::new(11.0, 0.0, 0.0),
Vec3::new(10.0, 1.0, 0.0),
);
let bvh = StaticBVH::<f32>::new_from_triangles(vec![tri1, tri2]);
let query = Vec3::new(-10.0, 0.0, 0.0);
let result = bvh.closest_point(query, Vec3A::ZERO, -1.0).unwrap().0;
let dist_to_tri1 = result.distance(Vec3::new(-10.0, 0.0, 0.0));
assert!(dist_to_tri1 < 0.001);
}
#[test]
fn test_many_triangles() {
let mut triangles = Vec::new();
for i in 0..10 {
for j in 0..10 {
let x = i as f32;
let y = j as f32;
triangles.push(make_triangle(
Vec3::new(x, y, 0.0),
Vec3::new(x + 1.0, y, 0.0),
Vec3::new(x, y + 1.0, 0.0),
));
triangles.push(make_triangle(
Vec3::new(x + 1.0, y, 0.0),
Vec3::new(x + 1.0, y + 1.0, 0.0),
Vec3::new(x, y + 1.0, 0.0),
));
}
}
let bvh = StaticBVH::<f32>::new_from_triangles(triangles);
let query = Vec3::new(5.5, 5.5, 10.0);
let result = bvh.closest_point(query, Vec3A::ZERO, -1.0).unwrap().0;
assert!((result.z).abs() < 0.001);
assert!((result.x - 5.5).abs() < 0.001);
assert!((result.y - 5.5).abs() < 0.001);
}
#[test]
fn test_aabb_union() {
let aabb1 = Aabb::<f32>::new(Vec3::new(0.0, 0.0, 0.0), Vec3::new(1.0, 1.0, 1.0));
let aabb2 = Aabb::<f32>::new(Vec3::new(0.5, 0.5, 0.5), Vec3::new(2.0, 2.0, 2.0));
let union = aabb1.union(&aabb2);
assert_eq!(union.min, Vec3::new(0.0, 0.0, 0.0));
assert_eq!(union.max, Vec3::new(2.0, 2.0, 2.0));
}
#[test]
fn test_aabb_distance_inside() {
let aabb = Aabb::<f32>::new(Vec3::new(0.0, 0.0, 0.0), Vec3::new(1.0, 1.0, 1.0));
let point = Vec3::new(0.5, 0.5, 0.5);
let dist = aabb.distance_sq(point);
assert_eq!(dist, 0.0);
}
#[test]
fn test_aabb_distance_outside() {
let aabb = Aabb::<f32>::new(Vec3::new(0.0, 0.0, 0.0), Vec3::new(1.0, 1.0, 1.0));
let point = Vec3::new(2.0, 0.5, 0.5);
let dist = aabb.distance_sq(point);
assert!((dist - 1.0).abs() < 0.001);
}
#[test]
fn test_projection_preserves_distance_to_plane() {
let tri = make_triangle::<f32>(
Vec3::new(-5.0, -5.0, 0.0),
Vec3::new(5.0, -5.0, 0.0),
Vec3::new(0.0, 5.0, 0.0),
);
let point = Vec3::new(0.0, 0.0, 3.0);
let projected = project_point_to_triangle::<f32>(point, &tri);
assert!((projected.z).abs() < 0.001);
assert!((projected.x).abs() < 0.001);
assert!((projected.y).abs() < 0.001);
}
#[test]
fn test_bvh_matches_brute_force() {
let triangles = vec![
make_triangle(
Vec3::new(0.0, 0.0, 0.0),
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
),
make_triangle(
Vec3::new(2.0, 2.0, 0.0),
Vec3::new(3.0, 2.0, 0.0),
Vec3::new(2.0, 3.0, 0.0),
),
make_triangle(
Vec3::new(-2.0, -2.0, 1.0),
Vec3::new(-1.0, -2.0, 1.0),
Vec3::new(-2.0, -1.0, 1.0),
),
];
let bvh = StaticBVH::<f32>::new_from_triangles(triangles.clone());
let query = Vec3::new(1.0, 1.0, 5.0);
let bvh_result = bvh.closest_point(query, Vec3A::ZERO, -1.0).unwrap().0;
let mut brute_force_result = Vec3::ZERO;
let mut min_dist = f32::MAX;
for tri in &triangles {
let proj = project_point_to_triangle(query, tri);
let dist = query.distance_squared(proj);
if dist < min_dist {
min_dist = dist;
brute_force_result = proj;
}
}
assert!(bvh_result.distance(brute_force_result) < 0.001);
}