bounding_space/
lib.rs

1pub extern crate nalgebra;
2
3use nalgebra::{SVector, RealField, Point};
4
5pub type BoundingSpace1<T> = BoundingSpaceN<T, 1>;
6pub type BoundingSpace2<T> = BoundingSpaceN<T, 2>;
7pub type BoundingSpace3<T> = BoundingSpaceN<T, 3>;
8
9pub type BoundingRange<T> = BoundingSpace1<T>;
10pub type BoundingSquare<T> = BoundingSpace2<T>;
11pub type BoundingBox<T> = BoundingSpace3<T>;
12
13#[derive(Debug, Clone, Copy)]
14pub struct BoundingSpaceN<T: RealField, const D: usize> {
15    pub lower: Point<T, D>,
16    pub upper: Point<T, D>,
17}
18
19impl<T: RealField, const D: usize> BoundingSpaceN<T, D> {
20    pub fn new(lower: Point<T, D>, upper: Point<T, D>) -> Self {
21        Self { lower, upper }
22    }
23
24    pub fn from_point(point: Point<T, D>) -> Self {
25        Self::new(point.to_owned(), point)
26    }
27
28    pub fn from_value(value: T) -> Self {
29        let coords =  SVector::<T, D>::repeat(value);
30        Self {
31            lower: Point { coords: coords.to_owned() },
32            upper: Point { coords }
33        }
34    }
35
36    pub fn from_values(lower: T, upper: T) -> Self {
37        Self {
38            lower: Point { coords: SVector::repeat(lower) },
39            upper: Point { coords: SVector::repeat(upper) },
40        }
41    }
42
43    pub fn diagonal(&self) -> SVector<T, D> {
44        &self.upper - &self.lower
45    }
46
47    pub fn contains(&self, point: &Point<T, D>) -> bool {
48        for (l, p) in self.lower.coords.iter().zip(&point.coords) {
49            if l > p {
50                return false;
51            }
52        }
53
54        for (u, p) in self.upper.coords.iter().zip(&point.coords) {
55            if u < p {
56                return false;
57            }
58        }
59
60        true
61    }
62
63    pub fn expand_lower(&mut self, point: &Point<T, D>) {
64        self.lower.coords.zip_apply(&point.coords, |l, p| {
65            *l = p.min(l.to_owned());
66        });
67    }
68
69    pub fn expand_upper(&mut self, point: &Point<T, D>) {
70        self.upper.coords.zip_apply(&point.coords, |u, p| {
71            *u = p.max(u.to_owned());
72        });
73    }
74
75    pub fn expand(&mut self, point: &Point<T, D>) {
76        self.expand_lower(point);
77        self.expand_upper(point);
78    }
79}
80
81impl<T: RealField, const D: usize> Default for BoundingSpaceN<T, D>
82{
83    fn default() -> Self {
84        Self {
85            lower: Point::origin(),
86            upper: Point::origin(),
87        }
88    }
89}
90
91#[cfg(test)]
92mod tests {
93    use approx::assert_relative_eq;
94    use nalgebra::Point1;
95
96    use super::*;
97
98    #[test]
99    fn default_initialized_with_zeros() {
100        let bs = BoundingSpaceN::<f64, 1>::default();
101
102        for v in bs.lower.iter().chain(bs.upper.iter()) {
103            assert_relative_eq!(*v, 0.0);
104        }
105    }
106
107    #[test]
108    fn first_expand_nan() {
109        let mut bound = BoundingSpaceN::<f64, 1>::from_value(f64::NAN);
110        let point = Point1::new(0.0);
111
112        bound.expand(&point);
113
114        assert_relative_eq!(bound.lower.x, point.x);
115        assert_relative_eq!(bound.upper.x, point.x);
116    }
117
118    #[test]
119    fn expand_1d() {
120        let value = 0.0_f64;
121        let mut bound = BoundingSpaceN::<f64, 1>::from_value(value);
122
123        let p1 = Point1::new(1.0);
124        bound.expand(&p1);
125
126        assert_relative_eq!(bound.lower.x, value);
127        assert_relative_eq!(bound.upper.x, p1.x);
128
129        let p2 = Point1::new(-1.0);
130        bound.expand(&p2);
131
132        assert_relative_eq!(bound.lower.x, p2.x);
133        assert_relative_eq!(bound.upper.x, p1.x);
134    }
135}