static_aabb2d_index/
core.rs

1use std::cmp::Ordering;
2
3use num_traits::{Bounded, Num, NumCast};
4
5/// Trait used by the [StaticAABB2DIndex](crate::StaticAABB2DIndex) that is required to be
6/// implemented for type T. It is blanket implemented for all supported primitive numeric types.
7pub trait IndexableNum: Copy + Num + PartialOrd + Default + Bounded + NumCast {
8    /// Simple default min implementation for [PartialOrd] types.
9    #[inline]
10    fn min(self, other: Self) -> Self {
11        if self < other {
12            return self;
13        }
14
15        other
16    }
17
18    /// Simple default max implementation for [PartialOrd] types.
19    #[inline]
20    fn max(self, other: Self) -> Self {
21        if self > other {
22            return self;
23        }
24
25        other
26    }
27
28    /// Total comparison between numbers. For types which implement [Ord] this should just use the
29    /// [Ord::cmp] method. For [f32] and [f64] types the `f32::total_cmp` and `f64::total_cmp`
30    /// methods are used.
31    fn total_cmp(&self, other: &Self) -> Ordering;
32}
33
34macro_rules! impl_indexable_num_for_ord_type {
35    ($t:ty) => {
36        impl IndexableNum for $t {
37            #[inline]
38            fn min(self, other: Self) -> Self {
39                std::cmp::min(self, other)
40            }
41            #[inline]
42            fn max(self, other: Self) -> Self {
43                std::cmp::max(self, other)
44            }
45            #[inline]
46            fn total_cmp(&self, other: &Self) -> Ordering {
47                self.cmp(other)
48            }
49        }
50    };
51}
52
53// impl for all supported built in types
54impl_indexable_num_for_ord_type!(i8);
55impl_indexable_num_for_ord_type!(u8);
56impl_indexable_num_for_ord_type!(i16);
57impl_indexable_num_for_ord_type!(u16);
58impl_indexable_num_for_ord_type!(i32);
59impl_indexable_num_for_ord_type!(u32);
60impl_indexable_num_for_ord_type!(i64);
61impl_indexable_num_for_ord_type!(u64);
62impl_indexable_num_for_ord_type!(i128);
63impl_indexable_num_for_ord_type!(u128);
64impl IndexableNum for f32 {
65    #[inline]
66    fn min(self, other: Self) -> Self {
67        self.min(other)
68    }
69    #[inline]
70    fn max(self, other: Self) -> Self {
71        self.max(other)
72    }
73    #[inline]
74    fn total_cmp(&self, other: &Self) -> Ordering {
75        self.total_cmp(other)
76    }
77}
78impl IndexableNum for f64 {
79    #[inline]
80    fn min(self, other: Self) -> Self {
81        self.min(other)
82    }
83    #[inline]
84    fn max(self, other: Self) -> Self {
85        self.max(other)
86    }
87    #[inline]
88    fn total_cmp(&self, other: &Self) -> Ordering {
89        self.total_cmp(other)
90    }
91}
92
93/// Simple 2D axis aligned bounding box which holds the extents of a 2D box.
94#[allow(clippy::upper_case_acronyms)]
95#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
96pub struct AABB<T = f64> {
97    /// Min x extent of the axis aligned bounding box.
98    pub min_x: T,
99    /// Min y extent of the axis aligned bounding box.
100    pub min_y: T,
101    /// Max x extent of the axis aligned bounding box.
102    pub max_x: T,
103    /// Max y extent of the axis aligned bounding box.
104    pub max_y: T,
105}
106
107impl<T> Default for AABB<T>
108where
109    T: IndexableNum,
110{
111    #[inline]
112    fn default() -> Self {
113        AABB {
114            min_x: T::zero(),
115            min_y: T::zero(),
116            max_x: T::zero(),
117            max_y: T::zero(),
118        }
119    }
120}
121
122impl<T> AABB<T>
123where
124    T: IndexableNum,
125{
126    #[inline]
127    pub fn new(min_x: T, min_y: T, max_x: T, max_y: T) -> AABB<T> {
128        AABB {
129            min_x,
130            min_y,
131            max_x,
132            max_y,
133        }
134    }
135
136    /// Tests if this AABB overlaps another AABB (inclusive).
137    ///
138    /// # Examples
139    /// ```
140    /// use static_aabb2d_index::AABB;
141    /// let box_a = AABB::new(0, 0, 2, 2);
142    /// let box_b = AABB::new(1, 1, 3, 3);
143    /// assert!(box_a.overlaps_aabb(&box_b));
144    /// assert!(box_b.overlaps_aabb(&box_a));
145    ///
146    /// let box_c = AABB::new(-1, -1, 0, 0);
147    /// assert!(!box_c.overlaps_aabb(&box_b));
148    /// // note: overlap check is inclusive of edges/corners touching
149    /// assert!(box_c.overlaps_aabb(&box_a));
150    /// ```
151    #[inline]
152    pub fn overlaps_aabb(&self, other: &AABB<T>) -> bool {
153        self.overlaps(other.min_x, other.min_y, other.max_x, other.max_y)
154    }
155
156    /// Tests if this AABB overlaps another AABB.
157    /// Same as [AABB::overlaps_aabb] but accepts AABB extent parameters directly.
158    #[inline]
159    pub fn overlaps(&self, min_x: T, min_y: T, max_x: T, max_y: T) -> bool {
160        if self.max_x < min_x || self.max_y < min_y || self.min_x > max_x || self.min_y > max_y {
161            return false;
162        }
163
164        true
165    }
166
167    /// Tests if this AABB fully contains another AABB (inclusive).
168    ///
169    /// # Examples
170    /// ```
171    /// use static_aabb2d_index::AABB;
172    /// let box_a = AABB::new(0, 0, 3, 3);
173    /// let box_b = AABB::new(1, 1, 2, 2);
174    /// assert!(box_a.contains_aabb(&box_b));
175    /// assert!(!box_b.contains_aabb(&box_a));
176    /// ```
177    #[inline]
178    pub fn contains_aabb(&self, other: &AABB<T>) -> bool {
179        self.contains(other.min_x, other.min_y, other.max_x, other.max_y)
180    }
181
182    /// Tests if this AABB fully contains another AABB.
183    /// Same as [AABB::contains] but accepts AABB extent parameters directly.
184    #[inline]
185    pub fn contains(&self, min_x: T, min_y: T, max_x: T, max_y: T) -> bool {
186        self.min_x <= min_x && self.min_y <= min_y && self.max_x >= max_x && self.max_y >= max_y
187    }
188}
189
190/// Basic control flow enum that can be used when visiting query results.
191#[derive(Debug, Default)]
192pub enum Control<B> {
193    /// Indicates to the query function to continue visiting results.
194    #[default]
195    Continue,
196    /// Indicates to the query function to stop visiting results and return a value.
197    Break(B),
198}
199
200/// Trait for control flow inside query functions.
201pub trait ControlFlow {
202    /// Constructs state indicating to continue.
203    fn continuing() -> Self;
204    /// Should return true if control flow should break.
205    fn should_break(&self) -> bool;
206}
207
208impl<B> ControlFlow for Control<B> {
209    #[inline]
210    fn continuing() -> Self {
211        Control::Continue
212    }
213
214    #[inline]
215    fn should_break(&self) -> bool {
216        matches!(*self, Control::Break(_))
217    }
218}
219
220impl ControlFlow for () {
221    #[inline]
222    fn continuing() -> Self {}
223
224    #[inline]
225    fn should_break(&self) -> bool {
226        false
227    }
228}
229
230impl<C, E> ControlFlow for Result<C, E>
231where
232    C: ControlFlow,
233{
234    fn continuing() -> Self {
235        Ok(C::continuing())
236    }
237
238    fn should_break(&self) -> bool {
239        self.is_err()
240    }
241}
242
243/// Visitor trait used to visit the results of a StaticAABB2DIndex query.
244///
245/// This trait is blanket implemented for FnMut(usize) -> impl ControlFlow.
246pub trait QueryVisitor<T, C>
247where
248    T: IndexableNum,
249    C: ControlFlow,
250{
251    /// Visit the index position of AABB returned by query.
252    fn visit(&mut self, index_pos: usize) -> C;
253}
254
255impl<T, C, F> QueryVisitor<T, C> for F
256where
257    T: IndexableNum,
258    C: ControlFlow,
259    F: FnMut(usize) -> C,
260{
261    #[inline]
262    fn visit(&mut self, index_pos: usize) -> C {
263        self(index_pos)
264    }
265}
266
267/// Visitor trait used to visit the results of a StaticAABB2DIndex nearest neighbors query.
268pub trait NeighborVisitor<T, C>
269where
270    T: IndexableNum,
271    C: ControlFlow,
272{
273    /// Visits the result containing the index position of the AABB neighbor and its euclidean
274    /// distance squared to the nearest neighbor input.
275    fn visit(&mut self, index_pos: usize, dist_squared: T) -> C;
276}
277
278impl<T, C, F> NeighborVisitor<T, C> for F
279where
280    T: IndexableNum,
281    C: ControlFlow,
282    F: FnMut(usize, T) -> C,
283{
284    #[inline]
285    fn visit(&mut self, index_pos: usize, dist_squared: T) -> C {
286        self(index_pos, dist_squared)
287    }
288}
289
290#[macro_export]
291macro_rules! try_control {
292    ($e:expr) => {
293        match $e {
294            x => {
295                if x.should_break() {
296                    return x;
297                }
298            }
299        }
300    };
301}