Skip to main content

math_utils/geometry/primitive/
integer.rs

1//! Integer Interval and Aabb primitives.
2//!
3//! Note that unlike Aabbs over scalar types, Aabbs over Integers are *closed* (they
4//! include their boundary points). This means that there can be degenerate Aabbs
5//! containing a single point (when `min = max`), or line or plane in higher dimensions.
6// TODO: more types? ortho lines, planes?
7
8#[cfg(feature = "derive_serdes")]
9use serde::{Deserialize, Serialize};
10
11use crate::*;
12use crate::geometry::intersect;
13
14/// 1D interval
15#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
16#[derive(Clone, Copy, Debug, Eq, PartialEq)]
17pub struct Interval <I> {
18  min : I,
19  max : I
20}
21
22/// 2D axis-aligned bounding box
23#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
24#[derive(Clone, Copy, Debug, Eq, PartialEq)]
25pub struct Aabb2 <I> {
26  min : Point2 <I>,
27  max : Point2 <I>
28}
29
30/// 3D axis-aligned bounding box
31#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
32#[derive(Clone, Copy, Debug, Eq, PartialEq)]
33pub struct Aabb3 <I> {
34  min : Point3 <I>,
35  max : Point3 <I>
36}
37
38/// Coordinate-wise min
39pub fn point2_min <I : Integer> (a : Point2 <I>, b : Point2 <I>) -> Point2 <I> {
40  [ Ord::min (a.0.x, b.0.x),
41    Ord::min (a.0.y, b.0.y)
42  ].into()
43}
44/// Coordinate-wise max
45pub fn point2_max <I : Integer> (a : Point2 <I>, b : Point2 <I>) -> Point2 <I> {
46  [ Ord::max (a.0.x, b.0.x),
47    Ord::max (a.0.y, b.0.y)
48  ].into()
49}
50/// Minimum representable point
51pub fn point2_min_value <I : Integer> () -> Point2 <I> {
52  [ I::min_value(), I::min_value() ].into()
53}
54/// Maximum representable point
55pub fn point2_max_value <I : Integer> () -> Point2 <I> {
56  [ I::max_value(), I::max_value() ].into()
57}
58
59/// Coordinate-wise min
60pub fn point3_min <I : Integer> (a : Point3 <I>, b : Point3 <I>) -> Point3 <I> {
61  [ Ord::min (a.0.x, b.0.x),
62    Ord::min (a.0.y, b.0.y),
63    Ord::min (a.0.z, b.0.z)
64  ].into()
65}
66/// Coordinate-wise max
67pub fn point3_max <I : Integer> (a : Point3 <I>, b : Point3 <I>) -> Point3 <I> {
68  [ Ord::max (a.0.x, b.0.x),
69    Ord::max (a.0.y, b.0.y),
70    Ord::max (a.0.z, b.0.z)
71  ].into()
72}
73/// Minimum representable point
74pub fn point3_min_value <I : Integer> () -> Point3 <I> {
75  [ I::min_value(), I::min_value(), I::min_value() ].into()
76}
77/// Maximum representable point
78pub fn point3_max_value <I : Integer> () -> Point3 <I> {
79  [ I::max_value(), I::max_value(), I::max_value() ].into()
80}
81
82impl <I : Integer> Interval <I> {
83  /// Construct a new AABB with given min and max points.
84  ///
85  /// Debug panic if points are not min/max:
86  ///
87  /// ```should_panic
88  /// # use math_utils::geometry::integer::*;
89  /// let b = Interval::with_minmax (1, 0);  // panic!
90  /// ```
91  ///
92  /// Identical points are allowed:
93  ///
94  /// ```
95  /// # use math_utils::geometry::integer::*;
96  /// let b = Interval::with_minmax (0, 0);  // ok
97  /// ```
98  #[inline]
99  pub fn with_minmax (min : I, max : I) -> Self where I : std::fmt::Debug {
100    debug_assert_eq!(min, Ord::min (min, max));
101    debug_assert_eq!(max, Ord::max (min, max));
102    Interval { min, max }
103  }
104  /// Construct a new AABB using the two given points to determine min/max.
105  ///
106  /// Identical points are allowed:
107  ///
108  /// ```
109  /// # use math_utils::geometry::integer::*;
110  /// let b = Interval::from_points (0, 0);  // ok!
111  /// ```
112  #[inline]
113  pub fn from_points (a : I, b : I) -> Self {
114    let min = Ord::min (a, b);
115    let max = Ord::max (a, b);
116    Interval { min, max }
117  }
118  /// Construct the minimum AABB containing the given set of points.
119  ///
120  /// Identical points are allowed:
121  ///
122  /// ```
123  /// # use math_utils::geometry::integer::*;
124  /// let b = Interval::containing (&[0, 0]);  // ok!
125  /// ```
126  ///
127  /// A single point is allowed:
128  ///
129  /// ```
130  /// # use math_utils::geometry::integer::*;
131  /// let b = Interval::containing (&[0]);  // ok!
132  /// ```
133  ///
134  /// Debug panic if no points are given:
135  ///
136  /// ```should_panic
137  /// # use math_utils::geometry::integer::*;
138  /// let b = Interval::<i32>::containing (&[]);  // panic!
139  /// ```
140  #[inline]
141  pub fn containing (points : &[I]) -> Self where I : std::fmt::Debug {
142    debug_assert!(!points.is_empty());
143    let mut min = I::max_value();
144    let mut max = I::min_value();
145    for point in points {
146      if *point < min {
147        min = *point;
148      }
149      if *point > max {
150        max = *point;
151      }
152    }
153    Interval::with_minmax (min, max)
154  }
155  /// Create a new AABB that is the union of the two input AABBs
156  #[inline]
157  pub fn union (a : Interval <I>, b : Interval <I>) -> Self where I : std::fmt::Debug {
158    Interval::with_minmax (
159      Ord::min (a.min(), b.min()),
160      Ord::max (a.max(), b.max())
161    )
162  }
163  #[inline]
164  pub const fn min (self) -> I {
165    self.min
166  }
167  #[inline]
168  pub const fn max (self) -> I {
169    self.max
170  }
171  #[inline]
172  pub fn width (self) -> Positive <I> {
173    Positive::unchecked (self.max - self.min + I::one())
174  }
175  #[inline]
176  pub fn contains (self, point : I) -> bool {
177    self.min <= point && point <= self.max
178  }
179  /// Clamp a given point to the AABB interval.
180  ///
181  /// ```
182  /// # use math_utils::geometry::integer::*;
183  /// let b = Interval::from_points (-1, 1);
184  /// assert_eq!(b.clamp (-2), -1);
185  /// assert_eq!(b.clamp ( 2),  1);
186  /// assert_eq!(b.clamp ( 0),  0);
187  /// ```
188  pub fn clamp (self, point : I) -> I {
189    Ord::max (Ord::min (self.max, point), self.min)
190  }
191  /// Generate a random point contained in the AABB.
192  ///
193  /// ```
194  /// # use rand;
195  /// # use rand_xorshift;
196  /// # use math_utils::geometry::integer::*;
197  /// # fn main () {
198  /// use rand_xorshift;
199  /// use rand::SeedableRng;
200  /// // random sequence will be the same each time this is run
201  /// let mut rng = rand_xorshift::XorShiftRng::seed_from_u64 (0);
202  /// let aabb = Interval::<i32>::with_minmax (-10, 10);
203  /// let point = aabb.rand_point (&mut rng);
204  /// assert!(aabb.contains (point));
205  /// let point = aabb.rand_point (&mut rng);
206  /// assert!(aabb.contains (point));
207  /// let point = aabb.rand_point (&mut rng);
208  /// assert!(aabb.contains (point));
209  /// let point = aabb.rand_point (&mut rng);
210  /// assert!(aabb.contains (point));
211  /// let point = aabb.rand_point (&mut rng);
212  /// assert!(aabb.contains (point));
213  /// # }
214  /// ```
215  #[inline]
216  pub fn rand_point <R> (self, rng : &mut R) -> I where
217    I : rand::distr::uniform::SampleUniform,
218    R : rand::RngExt
219  {
220    // NOTE: integer aabbs include their max points so we increase the max by 1
221    rng.random_range (self.min..self.max + I::one())
222  }
223  #[inline]
224  pub fn intersects (self, other : Interval <I>) -> bool {
225    intersect::integer::discrete_interval (self, other)
226  }
227}
228
229impl <I : Integer> Aabb2 <I> {
230  /// Construct a new AABB with given min and max points.
231  ///
232  /// Debug panic if points are not min/max:
233  ///
234  /// ```should_panic
235  /// # use math_utils::geometry::integer::*;
236  /// let b = Aabb2::with_minmax ([1, 1].into(), [0, 0].into());  // panic!
237  /// ```
238  ///
239  /// Identical points are allowed:
240  ///
241  /// ```
242  /// # use math_utils::geometry::integer::*;
243  /// let b = Aabb2::with_minmax ([0, 0].into(), [0, 0].into());  // ok
244  /// ```
245  #[inline]
246  pub fn with_minmax (min : Point2 <I>, max : Point2 <I>) -> Self where
247    I : std::fmt::Debug
248  {
249    debug_assert_eq!(min, point2_min (min, max));
250    debug_assert_eq!(max, point2_max (min, max));
251    Aabb2 { min, max }
252  }
253  /// Construct a new AABB using the two given points to determine min/max.
254  ///
255  /// Identical points are allowed:
256  ///
257  /// ```
258  /// # use math_utils::geometry::integer::*;
259  /// let b = Aabb2::from_points ([0, 0].into(), [0, 0].into());  // ok
260  /// ```
261  #[inline]
262  pub fn from_points (a : Point2 <I>, b : Point2 <I>) -> Self {
263    let min = point2_min (a, b);
264    let max = point2_max (a, b);
265    Aabb2 { min, max }
266  }
267  /// Construct the minimum AABB containing the given set of points.
268  ///
269  /// Identical points are allowed:
270  ///
271  /// ```
272  /// # use math_utils::geometry::integer::*;
273  /// let b = Aabb2::containing (&[[0, 0].into(), [0, 0].into()]);  // ok!
274  /// ```
275  ///
276  /// A single point is allowed:
277  ///
278  /// ```
279  /// # use math_utils::geometry::integer::*;
280  /// let b = Aabb2::containing (&[[0, 0].into()]);  // ok!
281  /// ```
282  ///
283  /// Debug panic if no points are given:
284  ///
285  /// ```should_panic
286  /// # use math_utils::geometry::integer::*;
287  /// let b = Aabb2::<i32>::containing (&[]);  // panic!
288  /// ```
289  #[inline]
290  pub fn containing (points : &[Point2 <I>]) -> Self where I : std::fmt::Debug {
291    debug_assert!(!points.is_empty());
292    let mut min = point2_max_value();
293    let mut max = point2_min_value();
294    for point in points {
295      min = point2_min (min, *point);
296      max = point2_max (max, *point);
297    }
298    Aabb2::with_minmax (min, max)
299  }
300  /// Create a new AABB that is the union of the two input AABBs
301  #[inline]
302  pub fn union (a : Aabb2 <I>, b : Aabb2 <I>) -> Self where I : std::fmt::Debug {
303    Aabb2::with_minmax (point2_min (a.min(), b.min()), point2_max (a.max(), b.max()))
304  }
305  #[inline]
306  pub const fn min (self) -> Point2 <I> {
307    self.min
308  }
309  #[inline]
310  pub const fn max (self) -> Point2 <I> {
311    self.max
312  }
313  #[inline]
314  pub fn dimensions (self) -> Vector2 <Positive <I>> {
315    (self.max.0 - self.min.0 + I::one()).map (Positive::unchecked)
316  }
317  #[inline]
318  pub fn width (self) -> Positive <I> {
319    Positive::unchecked (self.max.0.x - self.min.0.x + I::one())
320  }
321  #[inline]
322  pub fn height (self) -> Positive <I> {
323    Positive::unchecked (self.max.0.y - self.min.0.y + I::one())
324  }
325  #[inline]
326  pub fn contains (self, point : Point2 <I>) -> bool {
327    self.min.0.x <= point.0.x && point.0.x <= self.max.0.x &&
328    self.min.0.y <= point.0.y && point.0.y <= self.max.0.y
329  }
330  /// Clamp a given point to the AABB.
331  ///
332  /// ```
333  /// # use math_utils::geometry::integer::*;
334  /// let b = Aabb2::from_points ([-1, -1].into(), [1, 1].into());
335  /// assert_eq!(b.clamp ([-2, 0].into()), [-1, 0].into());
336  /// assert_eq!(b.clamp ([ 2, 2].into()), [ 1, 1].into());
337  /// assert_eq!(b.clamp ([ 0, 0].into()), [ 0, 0].into());
338  /// ```
339  pub fn clamp (self, point : Point2 <I>) -> Point2 <I> {
340    [ Ord::max (Ord::min (self.max.0.x, point.0.x), self.min.0.x),
341      Ord::max (Ord::min (self.max.0.y, point.0.y), self.min.0.y),
342    ].into()
343  }
344  /// Generate a random point contained in the AABB
345  ///
346  /// ```
347  /// # use rand;
348  /// # use rand_xorshift;
349  /// # use math_utils::geometry::integer::*;
350  /// # fn main () {
351  /// use rand_xorshift;
352  /// use rand::SeedableRng;
353  /// // random sequence will be the same each time this is run
354  /// let mut rng = rand_xorshift::XorShiftRng::seed_from_u64 (0);
355  /// let aabb = Aabb2::<i32>::with_minmax (
356  ///   [-10, -10].into(),
357  ///   [ 10,  10].into());
358  /// let point = aabb.rand_point (&mut rng);
359  /// assert!(aabb.contains (point));
360  /// let point = aabb.rand_point (&mut rng);
361  /// assert!(aabb.contains (point));
362  /// let point = aabb.rand_point (&mut rng);
363  /// assert!(aabb.contains (point));
364  /// let point = aabb.rand_point (&mut rng);
365  /// assert!(aabb.contains (point));
366  /// let point = aabb.rand_point (&mut rng);
367  /// assert!(aabb.contains (point));
368  /// # }
369  /// ```
370  #[inline]
371  pub fn rand_point <R> (self, rng : &mut R) -> Point2 <I> where
372    I : rand::distr::uniform::SampleUniform,
373    R : rand::RngExt
374  {
375    // NOTE: integer aabbs include their max points so we increase the max by 1
376    [ rng.random_range (self.min.0.x..self.max.0.x+I::one()),
377      rng.random_range (self.min.0.y..self.max.0.y+I::one())
378    ].into()
379  }
380  #[inline]
381  pub fn intersects (self, other : Aabb2 <I>) -> bool {
382    intersect::integer::discrete_aabb2_aabb2 (self, other)
383  }
384}
385
386impl <I : Integer> Aabb3 <I> {
387  /// Construct a new AABB with given min and max points.
388  ///
389  /// Debug panic if points are not min/max:
390  ///
391  /// ```should_panic
392  /// # use math_utils::geometry::integer::*;
393  /// let b = Aabb3::with_minmax ([1, 1, 1].into(), [0, 0, 0].into());  // panic!
394  /// ```
395  ///
396  /// Identical points are allowed:
397  ///
398  /// ```
399  /// # use math_utils::geometry::integer::*;
400  /// let b = Aabb3::with_minmax ([0, 0, 0].into(), [0, 0, 0].into());  // ok
401  /// ```
402  #[inline]
403  pub fn with_minmax (min : Point3 <I>, max : Point3 <I>) -> Self where
404    I : std::fmt::Debug
405  {
406    debug_assert_eq!(min, point3_min (min, max));
407    debug_assert_eq!(max, point3_max (min, max));
408    Aabb3 { min, max }
409  }
410  /// Construct a new AABB using the two given points to determine min/max.
411  ///
412  /// Identical points are allowed:
413  ///
414  /// ```
415  /// # use math_utils::geometry::integer::*;
416  /// let b = Aabb3::from_points ([0, 0, 0].into(), [0, 0, 0].into());  // ok
417  /// ```
418  #[inline]
419  pub fn from_points (a : Point3 <I>, b : Point3 <I>) -> Self {
420    let min = point3_min (a, b);
421    let max = point3_max (a, b);
422    Aabb3 { min, max }
423  }
424  /// Construct the minimum AABB containing the given set of points.
425  ///
426  /// Identical points are allowed:
427  ///
428  /// ```
429  /// # use math_utils::geometry::integer::*;
430  /// let b = Aabb3::containing (&[[0, 0, 0].into(), [0, 0, 0].into()]);  // ok!
431  /// ```
432  ///
433  /// A single point is allowed:
434  ///
435  /// ```
436  /// # use math_utils::geometry::integer::*;
437  /// let b = Aabb3::containing (&[[0, 0, 0].into()]);  // ok!
438  /// ```
439  ///
440  /// Debug panic if no points are given:
441  ///
442  /// ```should_panic
443  /// # use math_utils::geometry::integer::*;
444  /// let b = Aabb3::<i32>::containing (&[]);  // panic!
445  /// ```
446  #[inline]
447  pub fn containing (points : &[Point3 <I>]) -> Self where I : std::fmt::Debug {
448    debug_assert!(!points.is_empty());
449    let mut min = point3_max_value();
450    let mut max = point3_min_value();
451    for point in points {
452      min = point3_min (min, *point);
453      max = point3_max (max, *point);
454    }
455    Aabb3::with_minmax (min, max)
456  }
457  /// Create a new AABB that is the union of the two input AABBs
458  #[inline]
459  pub fn union (a : Aabb3 <I>, b : Aabb3 <I>) -> Self where I : std::fmt::Debug {
460    Aabb3::with_minmax (point3_min (a.min(), b.min()), point3_max (a.max(), b.max()))
461  }
462  #[inline]
463  pub const fn min (self) -> Point3 <I> {
464    self.min
465  }
466  #[inline]
467  pub const fn max (self) -> Point3 <I> {
468    self.max
469  }
470  #[inline]
471  pub fn dimensions (self) -> Vector3 <Positive <I>> {
472    (self.max.0 - self.min.0 + I::one()).map (Positive::unchecked)
473  }
474  #[inline]
475  pub fn width (self) -> Positive <I> {
476    Positive::unchecked (self.max.0.x - self.min.0.x + I::one())
477  }
478  #[inline]
479  pub fn height (self) -> Positive <I> {
480    Positive::unchecked (self.max.0.y - self.min.0.y + I::one())
481  }
482  #[inline]
483  pub fn depth (self) -> Positive <I> {
484    Positive::unchecked (self.max.0.z - self.min.0.z + I::one())
485  }
486  #[inline]
487  pub fn contains (self, point : Point3 <I>) -> bool {
488    self.min.0.x <= point.0.x && point.0.x <= self.max.0.x &&
489    self.min.0.y <= point.0.y && point.0.y <= self.max.0.y &&
490    self.min.0.z <= point.0.z && point.0.z <= self.max.0.z
491  }
492  /// Clamp a given point to the AABB.
493  ///
494  /// ```
495  /// # use math_utils::geometry::integer::*;
496  /// let b = Aabb3::with_minmax ([-1, -1, -1].into(), [1, 1, 1].into());
497  /// assert_eq!(b.clamp ([-2, 0, 0].into()), [-1, 0, 0].into());
498  /// assert_eq!(b.clamp ([ 2, 2, 0].into()), [ 1, 1, 0].into());
499  /// assert_eq!(b.clamp ([-1, 2, 3].into()), [-1, 1, 1].into());
500  /// assert_eq!(b.clamp ([ 0, 0, 0].into()), [ 0, 0, 0].into());
501  /// ```
502  pub fn clamp (self, point : Point3 <I>) -> Point3 <I> {
503    [ Ord::max (Ord::min (self.max.0.x, point.0.x), self.min.0.x),
504      Ord::max (Ord::min (self.max.0.y, point.0.y), self.min.0.y),
505      Ord::max (Ord::min (self.max.0.z, point.0.z), self.min.0.z)
506    ].into()
507  }
508  /// Generate a random point contained in the AABB
509  ///
510  /// ```
511  /// # use rand;
512  /// # use rand_xorshift;
513  /// # use math_utils::geometry::integer::*;
514  /// # fn main () {
515  /// use rand::SeedableRng;
516  /// // random sequence will be the same each time this is run
517  /// let mut rng = rand_xorshift::XorShiftRng::seed_from_u64 (0);
518  /// let aabb = Aabb3::<i32>::with_minmax (
519  ///   [-10, -10, -10].into(),
520  ///   [ 10,  10,  10].into());
521  /// let point = aabb.rand_point (&mut rng);
522  /// assert!(aabb.contains (point));
523  /// let point = aabb.rand_point (&mut rng);
524  /// assert!(aabb.contains (point));
525  /// let point = aabb.rand_point (&mut rng);
526  /// assert!(aabb.contains (point));
527  /// let point = aabb.rand_point (&mut rng);
528  /// assert!(aabb.contains (point));
529  /// let point = aabb.rand_point (&mut rng);
530  /// assert!(aabb.contains (point));
531  /// # }
532  /// ```
533  #[inline]
534  pub fn rand_point <R> (self, rng : &mut R) -> Point3 <I> where
535    I : rand::distr::uniform::SampleUniform,
536    R : rand::RngExt
537  {
538    // NOTE: integer aabbs include their max points so we increase the max by 1
539    [ rng.random_range (self.min.0.x..self.max.0.x+I::one()),
540      rng.random_range (self.min.0.y..self.max.0.y+I::one()),
541      rng.random_range (self.min.0.z..self.max.0.z+I::one())
542    ].into()
543  }
544  #[inline]
545  pub fn intersects (self, other : Aabb3 <I>) -> bool {
546    intersect::integer::discrete_aabb3_aabb3 (self, other)
547  }
548}