Skip to content

Commit

Permalink
Add test cases.
Browse files Browse the repository at this point in the history
  • Loading branch information
finnbear committed Jan 7, 2025
1 parent 0387e00 commit 38e4143
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 9 deletions.
20 changes: 20 additions & 0 deletions src/ball.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,26 @@ pub struct Ball<T: BHValue, const D: usize> {
pub radius: T,
}

impl<T: BHValue, const D: usize> Ball<T, D> {
/// Constructs a new ball.
///
/// # Panics
/// Panics, in debug mode, if the radius is negative.
pub fn new(center: Point<T, D>, radius: T) -> Self {
debug_assert!(radius >= T::from_f32(0.0).unwrap());
Self { center, radius }
}

/// Test if the ball contains a point.
pub fn contains(&self, point: &Point<T, D>) -> bool {
let mut distance_squared = T::zero();
for i in 0..D {
distance_squared += (point[i] - self.center[i]).powi(2);
}
distance_squared.sqrt()
}
}

impl<T: BHValue, const D: usize> IntersectsAabb<T, D> for Ball<T, D> {
fn intersects_aabb(&self, aabb: &Aabb<T, D>) -> bool {
// https://gamemath.com/book/geomtests.html A.14
Expand Down
79 changes: 70 additions & 9 deletions src/testbase.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Common utilities shared by unit tests.
use crate::aabb::Bounded;
use crate::aabb::{Aabb, Bounded, IntersectsAabb};
use crate::ball::Sphere;
use crate::bounding_hierarchy::{BHShape, BoundingHierarchy};

use num::{FromPrimitive, Integer};
Expand Down Expand Up @@ -129,18 +130,20 @@ pub fn build_empty_bh<BH: BoundingHierarchy<f32, 3>>() -> (Vec<UnitBox>, BH) {
/// Given a ray, a bounding hierarchy, the complete list of shapes in the scene and a list of
/// expected hits, verifies, whether the ray hits only the expected shapes.
fn traverse_and_verify<BH: BoundingHierarchy<f32, 3>>(
ray_origin: TPoint3,
ray_direction: TVector3,
query: &impl IntersectsAabb<f32, 3>,
all_shapes: &[UnitBox],
bh: &BH,
expected_shapes: &HashSet<i32>,
) {
let ray = TRay3::new(ray_origin, ray_direction);
let hit_shapes = bh.traverse(&ray, all_shapes);
let hit_shapes = bh.traverse(query, all_shapes);

assert_eq!(expected_shapes.len(), hit_shapes.len());
for shape in hit_shapes {
assert!(expected_shapes.contains(&shape.id));
assert!(
expected_shapes.contains(&shape.id),
"unexpected shape {}",
shape.id
);
}
}

Expand Down Expand Up @@ -169,7 +172,12 @@ fn traverse_some_built_bh<BH: BoundingHierarchy<f32, 3>>(all_shapes: &[UnitBox],
for id in -10..11 {
expected_shapes.insert(id);
}
traverse_and_verify(origin, direction, all_shapes, &bh, &expected_shapes);
traverse_and_verify(
&TRay3::new(origin, direction),
all_shapes,
&bh,
&expected_shapes,
);
}

{
Expand All @@ -180,7 +188,12 @@ fn traverse_some_built_bh<BH: BoundingHierarchy<f32, 3>>(all_shapes: &[UnitBox],
// It should hit only one box.
let mut expected_shapes = HashSet::new();
expected_shapes.insert(0);
traverse_and_verify(origin, direction, all_shapes, &bh, &expected_shapes);
traverse_and_verify(
&TRay3::new(origin, direction),
all_shapes,
&bh,
&expected_shapes,
);
}

{
Expand All @@ -193,7 +206,55 @@ fn traverse_some_built_bh<BH: BoundingHierarchy<f32, 3>>(all_shapes: &[UnitBox],
expected_shapes.insert(4);
expected_shapes.insert(5);
expected_shapes.insert(6);
traverse_and_verify(origin, direction, all_shapes, &bh, &expected_shapes);
traverse_and_verify(
&TRay3::new(origin, direction),
all_shapes,
&bh,
&expected_shapes,
);
}

{
// Define a point at the origin.
let point = TPoint3::new(0.0, 0.0, 0.0);

// It should be contained by the middle box.
let mut expected_shapes = HashSet::new();
expected_shapes.insert(0);
traverse_and_verify(&point, all_shapes, &bh, &expected_shapes);
}

{
// Define a point far away.
let point = TPoint3::new(0.0, 1000.0, 0.0);

// It shouldn't be contained by any boxes.
let expected_shapes = HashSet::new();
traverse_and_verify(&point, all_shapes, &bh, &expected_shapes);
}

{
// Define an AABB intersecting with some boxes.
let aabb = Aabb::with_bounds(TPoint3::new(5.1, -1.0, -1.0), TPoint3::new(9.9, 1.0, 1.0));

// It shouldn't be contained by any boxes.
let mut expected_shapes = HashSet::new();
for x in 5..=10 {
expected_shapes.insert(x);
}
traverse_and_verify(&aabb, all_shapes, &bh, &expected_shapes);
}

{
// Define a sphere intersecting with some boxes.
let aabb = Sphere::new(TPoint3::new(5.0, -1.0, -1.0), 1.4);

// It shouldn't be contained by any boxes.
let mut expected_shapes = HashSet::new();
for x in 4..=6 {
expected_shapes.insert(x);
}
traverse_and_verify(&aabb, all_shapes, &bh, &expected_shapes);
}
}

Expand Down

0 comments on commit 38e4143

Please sign in to comment.