1use crate::EPS;
4use std::fmt;
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 {
18 lo: f64::INFINITY,
19 hi: f64::NEG_INFINITY,
20 };
21
22 pub const UNIVERSE: Self = Self {
24 lo: f64::NEG_INFINITY,
25 hi: f64::INFINITY,
26 };
27
28 #[inline]
30 pub fn new(lo: f64, hi: f64) -> Self {
31 debug_assert!(lo <= hi, "Interval::new: lo ({lo}) > hi ({hi})");
32 Self { lo, hi }
33 }
34
35 #[inline]
37 pub fn from_unordered(a: f64, b: f64) -> Self {
38 Self {
39 lo: a.min(b),
40 hi: a.max(b),
41 }
42 }
43
44 #[inline]
46 pub fn union(self, other: Self) -> Self {
47 Self {
48 lo: self.lo.min(other.lo),
49 hi: self.hi.max(other.hi),
50 }
51 }
52
53 #[inline]
55 pub fn intersect(self, other: Self) -> Self {
56 Self {
57 lo: self.lo.max(other.lo),
58 hi: self.hi.min(other.hi),
59 }
60 }
61
62 #[inline]
64 pub fn length(self) -> f64 {
65 (self.hi - self.lo).max(0.0)
66 }
67
68 #[inline]
70 pub fn is_empty(self) -> bool {
71 self.lo > self.hi + EPS
72 }
73
74 #[inline]
76 pub fn contains(self, v: f64) -> bool {
77 v >= self.lo - EPS && v <= self.hi + EPS
78 }
79
80 #[inline]
82 pub fn clamp(self, v: f64) -> f64 {
83 v.max(self.lo).min(self.hi)
84 }
85
86 #[inline]
88 pub fn midpoint(self) -> f64 {
89 0.5 * (self.lo + self.hi)
90 }
91
92 #[inline]
94 pub fn expand(self, margin: f64) -> Self {
95 Self::new(self.lo - margin, self.hi + margin)
96 }
97}
98
99impl fmt::Debug for Interval {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 write!(f, "[{:.6}, {:.6}]", self.lo, self.hi)
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn union_span() {
111 let a = Interval::new(0.0, 3.0);
112 let b = Interval::new(2.0, 5.0);
113 let u = a.union(b);
114 assert_eq!(u.lo, 0.0);
115 assert_eq!(u.hi, 5.0);
116 }
117
118 #[test]
119 fn intersection_empty() {
120 let a = Interval::new(0.0, 1.0);
121 let b = Interval::new(2.0, 3.0);
122 assert!(a.intersect(b).is_empty());
123 }
124}