Skip to content

Commit c3dadf8

Browse files
committed
Implement AxisAlignedBoundingBox::corners_iter
1 parent 7d3ed91 commit c3dadf8

3 files changed

Lines changed: 123 additions & 2 deletions

File tree

fenris-geometry/src/lib.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ mod primitives;
1414
pub use polygon::*;
1515
pub use polytope::*;
1616
pub use primitives::*;
17+
use crate::util::{index_set_nth_power_iter};
1718

1819
pub mod polymesh;
1920
pub mod sdf;
@@ -239,6 +240,23 @@ where
239240
let max = self.max().map(|b_i| b_i + distance);
240241
Self::new(min, max)
241242
}
243+
244+
/// Creates an iterator over the corners of the bounding box.
245+
pub fn corners_iter<'a>(&'a self) -> impl 'a + Iterator<Item=OPoint<T, D>>
246+
where
247+
DefaultAllocator: Allocator<usize, D>
248+
{
249+
// We can enumerate the corners by looking at {0, 1}^D, i.e. the D-th power of the
250+
// set {0, 1}, and associating 0 and 1 with min and max coordinates for the i-th axis.
251+
index_set_nth_power_iter::<D>(2)
252+
.map(move |multi_idx| {
253+
OVector::<T, D>::from_fn(|idx, _| match multi_idx[idx] {
254+
0 => self.min[idx].clone(),
255+
1 => self.max[idx].clone(),
256+
_ => unreachable!()
257+
}).into()
258+
})
259+
}
242260
}
243261

244262
fn intervals_intersect<T: Real>([l1, u1]: [T; 2], [l2, u2]: [T; 2]) -> bool {

fenris-geometry/src/util.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use fenris_traits::Real;
2-
use nalgebra::{UnitVector3, Vector3};
2+
use nalgebra::{DefaultAllocator, DimName, OVector, UnitVector3, Vector3};
3+
use nalgebra::allocator::Allocator;
34

45
pub fn compute_orthonormal_vectors_3d<T: Real>(vector: &UnitVector3<T>) -> [UnitVector3<T>; 2] {
56
// Ported from
@@ -82,3 +83,32 @@ macro_rules! assert_line_segments_approx_equal {
8283
$crate::assert_line_segments_approx_equal_base!(msg_handler, $segment1, $segment2, abstol = $tol);
8384
}};
8485
}
86+
87+
/// Returns an iterator that iterates over the elements of $\{ 0, \dots, n - 1 \}^N$, where $n$ is
88+
/// the number of indices in the index set $\{ 0, \dots, n - 1 \}$.
89+
///
90+
/// TODO: Currently only implicitly tested through [`AxisAlignedBoundingBox::corners_iter`].
91+
pub(crate) fn index_set_nth_power_iter<D: DimName>(n: usize) -> impl Iterator<Item = OVector<usize, D>>
92+
where
93+
DefaultAllocator: Allocator<usize, D>
94+
{
95+
let mut multi_idx = OVector::from_element(0);
96+
let mut i = 0;
97+
std::iter::from_fn(move || {
98+
if i < n.pow(D::dim() as u32) {
99+
let item = Some(multi_idx.clone());
100+
for j in 0..D::dim() {
101+
if multi_idx[j] + 1 < n {
102+
multi_idx[j] += 1;
103+
break;
104+
} else {
105+
multi_idx[j] = 0;
106+
}
107+
}
108+
i += 1;
109+
item
110+
} else {
111+
None
112+
}
113+
})
114+
}

fenris-geometry/tests/unit_tests/aabb.rs

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use fenris_geometry::AxisAlignedBoundingBox;
2-
use nalgebra::{point, U2};
2+
use nalgebra::{DefaultAllocator, DimName, OPoint, point, U2};
3+
use nalgebra::allocator::Allocator;
4+
use fenris::allocators::DimAllocator;
35

46
#[test]
57
fn aabb_intersects_2d() {
@@ -39,4 +41,75 @@ fn aabb_intersects_2d() {
3941
assert_intersection!(Aabb::new(point![1.5, 1.5], point![4.5, 3.5]));
4042
assert_intersection!(Aabb::new(point![0.0, 0.0], point![2.0, 2.0]));
4143
assert_intersection!(Aabb::new(point![0.0, 0.0], point![5.0, 4.0]));
44+
}
45+
46+
/// Helper function for collecting corners from corner iterator.
47+
fn corners<D: DimName>(aabb:& AxisAlignedBoundingBox<f64, D>) -> Vec<OPoint<f64, D>>
48+
where
49+
DefaultAllocator: DimAllocator<f64, D>
50+
{
51+
aabb.corners_iter().collect()
52+
}
53+
54+
fn compare_unordered_points<D: DimName>(points1: &[OPoint<f64, D>], points2: &[OPoint<f64, D>]) -> bool
55+
where
56+
DefaultAllocator: Allocator<f64, D>
57+
{
58+
assert_eq!(points1.len(), points2.len());
59+
// This is O(n^2) but is acceptable for use in tests here
60+
points1.iter()
61+
.all(|p1| points2.contains(p1))
62+
}
63+
64+
macro_rules! assert_unordered_eq {
65+
($p1:expr, $p2:expr) => {
66+
{
67+
let eq = compare_unordered_points((&$p1).as_ref(), (&$p2).as_ref());
68+
if !eq {
69+
dbg!(&$p1, &$p2);
70+
assert!(eq, "Point lists do not contain the same points");
71+
}
72+
}
73+
}
74+
}
75+
76+
77+
#[test]
78+
fn test_aabb_corners_iter() {
79+
80+
// 1D
81+
{
82+
let aabb = AxisAlignedBoundingBox::new(point![3.0], point![4.0]);
83+
assert_eq!(corners(&aabb), vec![ point![3.0 ], point![4.0] ]);
84+
}
85+
86+
// 2D
87+
{
88+
let aabb = AxisAlignedBoundingBox::new(point![3.0, 4.0], point![5.0, 6.0]);
89+
let expected = [
90+
[3.0, 4.0],
91+
[3.0, 6.0],
92+
[5.0, 4.0],
93+
[5.0, 6.0]
94+
].map(OPoint::from);
95+
assert_unordered_eq!(corners(&aabb), &expected);
96+
}
97+
98+
// 3D
99+
{
100+
let aabb = AxisAlignedBoundingBox::new(point![1.0, 2.0, 3.0], point![4.0, 5.0, 6.0]);
101+
let expected = [
102+
[1.0, 2.0, 3.0],
103+
[1.0, 2.0, 6.0],
104+
[1.0, 5.0, 3.0],
105+
[1.0, 5.0, 6.0],
106+
[4.0, 2.0, 3.0],
107+
[4.0, 2.0, 6.0],
108+
[4.0, 5.0, 3.0],
109+
[4.0, 5.0, 6.0]
110+
].map(OPoint::from);
111+
assert_unordered_eq!(corners(&aabb), expected);
112+
}
113+
114+
42115
}

0 commit comments

Comments
 (0)