Skip to content

Commit ee6c909

Browse files
authored
Merge pull request #44 from Andlon/various-changes
Various changes
2 parents 4b10e3a + 349ab78 commit ee6c909

10 files changed

Lines changed: 429 additions & 106 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ proptest-support = [ "proptest", "fenris-geometry/proptest-support", "nalgebra/p
2323
[dependencies]
2424
nalgebra = { version = "0.31", features = [ "serde-serialize" ] }
2525
nalgebra-sparse = { version = "0.7", features = ["compare"] }
26-
davenport = "0.1.0"
26+
davenport = "0.1.1"
2727
vtkio = "0.6"
2828
num = "0.4"
2929
numeric_literals = "0.2.0"

fenris-geometry/src/lib.rs

Lines changed: 105 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use std::fmt::Debug;
1111
mod polygon;
1212
mod polytope;
1313
mod primitives;
14+
use crate::util::index_set_nth_power_iter;
1415
pub use polygon::*;
1516
pub use polytope::*;
1617
pub use primitives::*;
@@ -86,25 +87,25 @@ where
8687

8788
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8889
#[serde(bound(
89-
serialize = "OVector<T, D>: Serialize",
90-
deserialize = "OVector<T, D>: Deserialize<'de>"
90+
serialize = "OPoint<T, D>: Serialize",
91+
deserialize = "OPoint<T, D>: Deserialize<'de>"
9192
))]
9293
pub struct AxisAlignedBoundingBox<T, D>
9394
where
9495
T: Scalar,
9596
D: DimName,
9697
DefaultAllocator: Allocator<T, D>,
9798
{
98-
min: OVector<T, D>,
99-
max: OVector<T, D>,
99+
min: OPoint<T, D>,
100+
max: OPoint<T, D>,
100101
}
101102

102103
impl<T, D> Copy for AxisAlignedBoundingBox<T, D>
103104
where
104105
T: Scalar,
105106
D: DimName,
106107
DefaultAllocator: Allocator<T, D>,
107-
OVector<T, D>: Copy,
108+
OPoint<T, D>: Copy,
108109
{
109110
}
110111

@@ -117,18 +118,18 @@ where
117118
D: DimName,
118119
DefaultAllocator: Allocator<T, D>,
119120
{
120-
pub fn new(min: OVector<T, D>, max: OVector<T, D>) -> Self {
121+
pub fn new(min: OPoint<T, D>, max: OPoint<T, D>) -> Self {
121122
for i in 0..D::dim() {
122123
assert!(min[i] <= max[i]);
123124
}
124125
Self { min, max }
125126
}
126127

127-
pub fn min(&self) -> &OVector<T, D> {
128+
pub fn min(&self) -> &OPoint<T, D> {
128129
&self.min
129130
}
130131

131-
pub fn max(&self) -> &OVector<T, D> {
132+
pub fn max(&self) -> &OPoint<T, D> {
132133
&self.max
133134
}
134135
}
@@ -140,7 +141,7 @@ where
140141
DefaultAllocator: Allocator<T, D>,
141142
{
142143
fn from(point: OPoint<T, D>) -> Self {
143-
AxisAlignedBoundingBox::new(point.coords.clone(), point.coords)
144+
AxisAlignedBoundingBox::new(point.clone(), point)
144145
}
145146
}
146147

@@ -152,13 +153,21 @@ where
152153
{
153154
/// Computes the minimal bounding box which encloses both `this` and `other`.
154155
pub fn enclose(&self, other: &AxisAlignedBoundingBox<T, D>) -> Self {
155-
let min = self.min.iter().zip(&other.min).map(|(a, b)| T::min(*a, *b));
156+
let min = self
157+
.min
158+
.iter()
159+
.zip(&other.min.coords)
160+
.map(|(a, b)| T::min(*a, *b));
156161
let min = OVector::<T, D>::from_iterator(min);
157162

158-
let max = self.max.iter().zip(&other.max).map(|(a, b)| T::max(*a, *b));
163+
let max = self
164+
.max
165+
.iter()
166+
.zip(&other.max.coords)
167+
.map(|(a, b)| T::max(*a, *b));
159168
let max = OVector::<T, D>::from_iterator(max);
160169

161-
AxisAlignedBoundingBox::new(min, max)
170+
AxisAlignedBoundingBox::new(min.into(), max.into())
162171
}
163172

164173
pub fn from_points<'a>(points: impl IntoIterator<Item = &'a OPoint<T, D>>) -> Option<Self> {
@@ -179,37 +188,37 @@ where
179188
}
180189

181190
pub fn center(&self) -> OPoint<T, D> {
182-
OPoint::from((self.max() + self.min()) / T::from_f64(2.0).unwrap())
191+
OPoint::from((&self.max().coords + &self.min().coords) / T::from_f64(2.0).unwrap())
183192
}
184193

185194
/// Uniformly scales each axis by the given scale amount, with respect to the center of
186195
/// the box.
187196
///
188197
/// ```rust
189198
/// # use fenris_geometry::AxisAlignedBoundingBox;
190-
/// use nalgebra::vector;
199+
/// use nalgebra::{point, vector};
191200
/// use matrixcompare::assert_matrix_eq;
192201
///
193-
/// let aabb = AxisAlignedBoundingBox::new(vector![0.0, 0.0], vector![1.0, 1.0]);
202+
/// let aabb = AxisAlignedBoundingBox::new(point![0.0, 0.0], point![1.0, 1.0]);
194203
/// let scaled = aabb.uniformly_scale(0.5);
195204
///
196-
/// assert_matrix_eq!(scaled.min(), vector![0.25, 0.25], comp = float);
197-
/// assert_matrix_eq!(scaled.max(), vector![0.75, 0.75], comp = float);
205+
/// assert_matrix_eq!(scaled.min().coords, vector![0.25, 0.25], comp = float);
206+
/// assert_matrix_eq!(scaled.max().coords, vector![0.75, 0.75], comp = float);
198207
/// ```
199208
#[replace_float_literals(T::from_f64(literal).unwrap())]
200209
pub fn uniformly_scale(&self, scale: T) -> Self {
201210
assert!(scale >= T::zero());
202211
let s = scale;
203212
let (a, b) = (&self.min, &self.max);
204-
let ref c = self.center().coords;
213+
let ref c = self.center();
205214
Self {
206215
min: c + (a - c) * s,
207216
max: c + (b - c) * s,
208217
}
209218
}
210219

211220
pub fn contains_point(&self, point: &OPoint<T, D>) -> bool {
212-
(0..D::dim()).all(|dim| point[dim] > self.min[dim] && point[dim] < self.max[dim])
221+
(0..D::dim()).all(|dim| point[dim] >= self.min[dim] && point[dim] <= self.max[dim])
213222
}
214223

215224
pub fn intersects(&self, other: &Self) -> bool {
@@ -227,18 +236,91 @@ where
227236
///
228237
/// ```rust
229238
/// # use fenris_geometry::AxisAlignedBoundingBox;
230-
/// # use nalgebra::vector;
231-
/// let aabb = AxisAlignedBoundingBox::new(vector![0.0, 0.0], vector![1.0, 1.0]);
239+
/// # use nalgebra::point;
240+
/// let aabb = AxisAlignedBoundingBox::new(point![0.0, 0.0], point![1.0, 1.0]);
232241
/// let grown = aabb.grow_uniformly(1.0);
233-
/// assert_eq!(grown.min(), &vector![-1.0, -1.0]);
234-
/// assert_eq!(grown.max(), &vector![2.0, 2.0]);
242+
/// assert_eq!(grown.min(), &point![-1.0, -1.0]);
243+
/// assert_eq!(grown.max(), &point![2.0, 2.0]);
235244
/// ```
236245
///
237246
pub fn grow_uniformly(&self, distance: T) -> Self {
238247
let min = self.min().map(|b_i| b_i - distance);
239248
let max = self.max().map(|b_i| b_i + distance);
240249
Self::new(min, max)
241250
}
251+
252+
/// Creates an iterator over the corners of the bounding box.
253+
pub fn corners_iter<'a>(&'a self) -> impl 'a + Iterator<Item = OPoint<T, D>>
254+
where
255+
DefaultAllocator: Allocator<usize, D>,
256+
{
257+
// We can enumerate the corners by looking at {0, 1}^D, i.e. the D-th power of the
258+
// set {0, 1}, and associating 0 and 1 with min and max coordinates for the i-th axis.
259+
index_set_nth_power_iter::<D>(2).map(move |multi_idx| {
260+
OVector::<T, D>::from_fn(|idx, _| match multi_idx[idx] {
261+
0 => self.min[idx].clone(),
262+
1 => self.max[idx].clone(),
263+
_ => unreachable!(),
264+
})
265+
.into()
266+
})
267+
}
268+
269+
/// Compute the point in the bounding box furthest away from the given point.
270+
///
271+
/// # Panics
272+
///
273+
/// Panics if two distances cannot be ordered. This typically only happens if
274+
/// one of the numbers is not a number (NaN) or the comparison is not sensible, such as
275+
/// comparing two infinities. Since given finite coordinates no distance should be infinite,
276+
/// this method will realistically only panic in cases where one of the points
277+
/// --- either of the bounding box or the query point --- has components that are not
278+
/// finite numbers.
279+
#[replace_float_literals(T::from_f64(literal).unwrap())]
280+
pub fn furthest_point_to(&self, point: &OPoint<T, D>) -> OPoint<T, D>
281+
where
282+
DefaultAllocator: Allocator<usize, D>,
283+
{
284+
// It turns out that we can choose, along each dimension, the point in the interval
285+
// [a_i, b_i] furthest away from p_i.
286+
point
287+
.coords
288+
.zip_zip_map(&self.min.coords, &self.max.coords, |p_i, a_i, b_i| {
289+
let mid = (a_i + b_i) / 2.0;
290+
if p_i < mid {
291+
b_i
292+
} else {
293+
a_i
294+
}
295+
})
296+
.into()
297+
}
298+
299+
/// The squared distance to the point in the bounding box furthest away from the given point.
300+
///
301+
/// # Panics
302+
///
303+
/// Panic behavior is identical to [`furthest_point_to`](Self::furthest_point_to).
304+
pub fn max_dist2_to(&self, point: &OPoint<T, D>) -> T
305+
where
306+
// TODO: Use DimAllocator and SmallDim
307+
DefaultAllocator: Allocator<usize, D>,
308+
{
309+
(self.furthest_point_to(point) - point).norm_squared()
310+
}
311+
312+
/// The distance to the point in the bounding box furthest away from the given point.
313+
///
314+
/// # Panics
315+
///
316+
/// Panic behavior is identical to [`max_dist2_to`](Self::max_dist2_to).
317+
pub fn max_dist_to(&self, point: &OPoint<T, D>) -> T
318+
where
319+
// TODO: Use DimAllocator and SmallDim
320+
DefaultAllocator: Allocator<usize, D>,
321+
{
322+
self.max_dist2_to(point).sqrt()
323+
}
242324
}
243325

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

fenris-geometry/src/proptest.rs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,38 @@
1-
// use crate::procedural::create_rectangular_uniform_quad_mesh_2d;
21
use crate::Orientation::Counterclockwise;
3-
use crate::{HalfPlane, LineSegment2d, Orientation, Quad2d, Triangle, Triangle2d, Triangle3d};
2+
use crate::{
3+
AxisAlignedBoundingBox, AxisAlignedBoundingBox2d, AxisAlignedBoundingBox3d, HalfPlane, LineSegment2d, Orientation,
4+
Quad2d, Triangle, Triangle2d, Triangle3d,
5+
};
6+
use nalgebra::allocator::Allocator;
47
use nalgebra::proptest::vector;
5-
use nalgebra::{DimName, Point2, Point3, Unit, Vector2, Vector3, U2, U3};
8+
use nalgebra::{DefaultAllocator, DimName, Point2, Point3, RealField, Unit, Vector2, Vector3, U2, U3};
69
use proptest::prelude::*;
710

11+
pub fn aabb<D>() -> impl Strategy<Value = AxisAlignedBoundingBox<f64, D>>
12+
where
13+
D: DimName,
14+
DefaultAllocator: Allocator<f64, D>,
15+
{
16+
let value_strategy = -10.0..10.0;
17+
(
18+
vector(value_strategy.clone(), D::name()),
19+
vector(value_strategy, D::name()),
20+
)
21+
.prop_map(|(a, b)| {
22+
let min = a.zip_map(&b, |a_i, b_i| RealField::min(a_i, b_i));
23+
let max = a.zip_map(&b, |a_i, b_i| RealField::max(a_i, b_i));
24+
AxisAlignedBoundingBox::new(min.into(), max.into())
25+
})
26+
}
27+
28+
pub fn aabb2() -> impl Strategy<Value = AxisAlignedBoundingBox2d<f64>> {
29+
aabb()
30+
}
31+
32+
pub fn aabb3() -> impl Strategy<Value = AxisAlignedBoundingBox3d<f64>> {
33+
aabb()
34+
}
35+
836
pub fn vector2() -> impl Strategy<Value = Vector2<f64>> {
937
vector(-10.0..10.0, U2::name())
1038
}

fenris-geometry/src/sdf.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use fenris_traits::Real;
2-
use nalgebra::{Point2, Scalar, Vector2, U2};
2+
use nalgebra::{OPoint, Point2, Scalar, Vector2, U2};
33

44
use crate::{AxisAlignedBoundingBox2d, BoundedGeometry};
55
use numeric_literals::replace_float_literals;
@@ -68,8 +68,8 @@ where
6868
fn bounding_box(&self) -> AxisAlignedBoundingBox2d<T> {
6969
let eps = self.radius * T::from_f64(0.01).unwrap();
7070
AxisAlignedBoundingBox2d::new(
71-
self.center - Vector2::repeat(T::one()) * (self.radius + eps),
72-
self.center + Vector2::repeat(T::one()) * (self.radius + eps),
71+
OPoint::from(self.center - Vector2::repeat(T::one()) * (self.radius + eps)),
72+
OPoint::from(self.center + Vector2::repeat(T::one()) * (self.radius + eps)),
7373
)
7474
}
7575
}

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::allocator::Allocator;
3+
use nalgebra::{DefaultAllocator, DimName, OVector, UnitVector3, Vector3};
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+
}

0 commit comments

Comments
 (0)