Skip to main content

solstrale/util/
interval.rs

1//! Defines a range between min and max inclusive
2use derive_more::Constructor;
3use std::ops::{Add, Sub};
4
5/// Defines a range between min and max inclusive
6#[derive(Copy, Clone, PartialEq, Debug, Default, Constructor)]
7pub struct Interval {
8    /// Minimum value of the interval
9    pub min: f64,
10    /// Maximum value of the interval
11    pub max: f64,
12}
13
14/// Contains nothing
15pub const EMPTY_INTERVAL: Interval = Interval {
16    min: f64::INFINITY,
17    max: f64::NEG_INFINITY,
18};
19/// Contains everything
20pub const UNIVERSE_INTERVAL: Interval = Interval {
21    min: f64::NEG_INFINITY,
22    max: f64::INFINITY,
23};
24/// Default start interval for a ray
25pub const RAY_INTERVAL: Interval = Interval {
26    min: 0.001,
27    max: f64::INFINITY,
28};
29
30/// creates a new interval that is the union of the two given.
31/// If there is a gap between the intervals, that is included in the returned interval.
32pub fn combine_intervals(a: Interval, b: Interval) -> Interval {
33    Interval {
34        min: a.min.min(b.min),
35        max: a.max.max(b.max),
36    }
37}
38
39impl Add<f64> for Interval {
40    type Output = Interval;
41
42    /// returns a new interval that is increased with given value.
43    /// The returned interval has same size as original
44    fn add(self, rhs: f64) -> Self::Output {
45        Interval {
46            min: self.min + rhs,
47            max: self.max + rhs,
48        }
49    }
50}
51
52impl Sub<f64> for Interval {
53    type Output = Interval;
54
55    /// returns a new interval that is decreased with given value.
56    /// The returned interval has same size as original
57    fn sub(self, rhs: f64) -> Self::Output {
58        Interval {
59            min: self.min - rhs,
60            max: self.max - rhs,
61        }
62    }
63}
64
65impl Interval {
66    /// Checks if the interval contains a given value
67    pub fn contains(&self, x: f64) -> bool {
68        self.min <= x && x <= self.max
69    }
70
71    /// returns the given value clamped to the interval
72    pub fn clamp(&self, x: f64) -> f64 {
73        if x < self.min {
74            return self.min;
75        }
76        if x > self.max {
77            return self.max;
78        }
79        x
80    }
81
82    /// return the size of the interval
83    pub fn size(&self) -> f64 {
84        self.max - self.min
85    }
86
87    /// returns a new interval that is larger by given value delta.
88    /// Delta is added equally to both sides of the interval
89    pub fn expand(&self, delta: f64) -> Interval {
90        let padding = delta / 2.;
91        Interval {
92            min: self.min - padding,
93            max: self.max + padding,
94        }
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use crate::util::interval::{Interval, combine_intervals};
101
102    #[test]
103    fn test_combine_intervals() {
104        let mut res = combine_intervals(Interval::new(0., 2.), Interval::new(1., 3.));
105        assert_eq!(Interval::new(0., 3.), res);
106
107        res = combine_intervals(Interval::new(0., 1.), Interval::new(2., 3.));
108        assert_eq!(Interval::new(0., 3.), res);
109
110        res = combine_intervals(Interval::new(3., 3.), Interval::new(-1., -1.));
111        assert_eq!(Interval::new(-1., 3.), res);
112    }
113
114    #[test]
115    fn test_contains() {
116        let interval = Interval::new(-2., 2.);
117        assert!(!interval.contains(-3.));
118        assert!(interval.contains(-2.));
119        assert!(interval.contains(2.));
120        assert!(!interval.contains(3.));
121    }
122
123    #[test]
124    fn test_clamp() {
125        let interval = Interval::new(-2., 2.);
126        assert_eq!(-2., interval.clamp(-3.));
127        assert_eq!(-2., interval.clamp(-2.));
128        assert_eq!(0., interval.clamp(-0.));
129        assert_eq!(2., interval.clamp(2.));
130        assert_eq!(2., interval.clamp(3.));
131    }
132
133    #[test]
134    fn test_size() {
135        assert_eq!(0., Interval::new(0., 0.).size());
136        assert_eq!(2., Interval::new(-1., 1.).size());
137        assert_eq!(-2., Interval::new(1., -1.).size());
138    }
139
140    #[test]
141    fn test_expand() {
142        let interval = Interval::new(-2., 2.);
143        assert_eq!(Interval::new(-3., 3.), interval.expand(2.));
144        assert_eq!(
145            Interval {
146                min: -3.5,
147                max: 3.5
148            },
149            interval.expand(3.)
150        );
151        assert_eq!(Interval::new(-1., 1.), interval.expand(-2.));
152    }
153
154    #[test]
155    fn test_sub_and_add() {
156        let interval = Interval::new(-2., 2.);
157        assert_eq!(Interval::new(0., 4.), interval + 2.);
158        assert_eq!(Interval::new(1., 5.), interval + 3.);
159        assert_eq!(Interval::new(-4., 0.), interval - 2.);
160    }
161}