1use std::fmt;
4use crate::EPS;
5
6#[derive(Clone, Copy, PartialEq)]
8pub struct Interval {
9 pub lo: f64,
11 pub hi: f64,
13}
14
15impl Interval {
16 pub const EMPTY: Self = Self { lo: f64::INFINITY, hi: f64::NEG_INFINITY };
18
19 pub const UNIVERSE: Self = Self { lo: f64::NEG_INFINITY, hi: f64::INFINITY };
21
22 #[inline]
24 pub fn new(lo: f64, hi: f64) -> Self {
25 debug_assert!(lo <= hi, "Interval::new: lo ({lo}) > hi ({hi})");
26 Self { lo, hi }
27 }
28
29 #[inline]
31 pub fn from_unordered(a: f64, b: f64) -> Self {
32 Self { lo: a.min(b), hi: a.max(b) }
33 }
34
35 #[inline]
37 pub fn union(self, other: Self) -> Self {
38 Self { lo: self.lo.min(other.lo), hi: self.hi.max(other.hi) }
39 }
40
41 #[inline]
43 pub fn intersect(self, other: Self) -> Self {
44 Self {
45 lo: self.lo.max(other.lo),
46 hi: self.hi.min(other.hi),
47 }
48 }
49
50 #[inline]
52 pub fn length(self) -> f64 { (self.hi - self.lo).max(0.0) }
53
54 #[inline]
56 pub fn is_empty(self) -> bool { self.lo > self.hi + EPS }
57
58 #[inline]
60 pub fn contains(self, v: f64) -> bool { v >= self.lo - EPS && v <= self.hi + EPS }
61
62 #[inline]
64 pub fn clamp(self, v: f64) -> f64 { v.max(self.lo).min(self.hi) }
65
66 #[inline]
68 pub fn midpoint(self) -> f64 { 0.5 * (self.lo + self.hi) }
69
70 #[inline]
72 pub fn expand(self, margin: f64) -> Self { Self::new(self.lo - margin, self.hi + margin) }
73}
74
75impl fmt::Debug for Interval {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 write!(f, "[{:.6}, {:.6}]", self.lo, self.hi)
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
86 fn union_span() {
87 let a = Interval::new(0.0, 3.0);
88 let b = Interval::new(2.0, 5.0);
89 let u = a.union(b);
90 assert_eq!(u.lo, 0.0);
91 assert_eq!(u.hi, 5.0);
92 }
93
94 #[test]
95 fn intersection_empty() {
96 let a = Interval::new(0.0, 1.0);
97 let b = Interval::new(2.0, 3.0);
98 assert!(a.intersect(b).is_empty());
99 }
100}