1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//! Defines a range between min and max inclusive
use derive_more::Constructor;
use std::ops::{Add, Sub};

/// Defines a range between min and max inclusive
#[derive(Copy, Clone, PartialEq, Debug, Default, Constructor)]
pub struct Interval {
    /// Minimum value of the interval
    pub min: f64,
    /// Maximum value of the interval
    pub max: f64,
}

/// Contains nothing
pub const EMPTY_INTERVAL: Interval = Interval {
    min: f64::INFINITY,
    max: f64::NEG_INFINITY,
};
/// Contains everything
pub const UNIVERSE_INTERVAL: Interval = Interval {
    min: f64::NEG_INFINITY,
    max: f64::INFINITY,
};
/// Default start interval for a ray
pub const RAY_INTERVAL: Interval = Interval {
    min: 0.001,
    max: f64::INFINITY,
};

/// creates a new interval that is the union of the two given.
/// If there is a gap between the intervals, that is included in the returned interval.
pub fn combine_intervals(a: Interval, b: Interval) -> Interval {
    Interval {
        min: a.min.min(b.min),
        max: a.max.max(b.max),
    }
}

impl Add<f64> for Interval {
    type Output = Interval;

    /// returns a new interval that is increased with given value.
    /// The returned interval has same size as original
    fn add(self, rhs: f64) -> Self::Output {
        Interval {
            min: self.min + rhs,
            max: self.max + rhs,
        }
    }
}

impl Sub<f64> for Interval {
    type Output = Interval;

    /// returns a new interval that is decreased with given value.
    /// The returned interval has same size as original
    fn sub(self, rhs: f64) -> Self::Output {
        Interval {
            min: self.min - rhs,
            max: self.max - rhs,
        }
    }
}

impl Interval {
    /// Checks if the interval contains a given value
    pub fn contains(&self, x: f64) -> bool {
        self.min <= x && x <= self.max
    }

    /// returns the given value clamped to the interval
    pub fn clamp(&self, x: f64) -> f64 {
        if x < self.min {
            return self.min;
        }
        if x > self.max {
            return self.max;
        }
        x
    }

    /// return the size of the interval
    pub fn size(&self) -> f64 {
        self.max - self.min
    }

    /// returns a new interval that is larger by given value delta.
    /// Delta is added equally to both sides of the interval
    pub fn expand(&self, delta: f64) -> Interval {
        let padding = delta / 2.;
        Interval {
            min: self.min - padding,
            max: self.max + padding,
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::util::interval::{combine_intervals, Interval};

    #[test]
    fn test_combine_intervals() {
        let mut res = combine_intervals(Interval::new(0., 2.), Interval::new(1., 3.));
        assert_eq!(Interval::new(0., 3.), res);

        res = combine_intervals(Interval::new(0., 1.), Interval::new(2., 3.));
        assert_eq!(Interval::new(0., 3.), res);

        res = combine_intervals(Interval::new(3., 3.), Interval::new(-1., -1.));
        assert_eq!(Interval::new(-1., 3.), res);
    }

    #[test]
    fn test_contains() {
        let interval = Interval::new(-2., 2.);
        assert!(!interval.contains(-3.));
        assert!(interval.contains(-2.));
        assert!(interval.contains(2.));
        assert!(!interval.contains(3.));
    }

    #[test]
    fn test_clamp() {
        let interval = Interval::new(-2., 2.);
        assert_eq!(-2., interval.clamp(-3.));
        assert_eq!(-2., interval.clamp(-2.));
        assert_eq!(0., interval.clamp(-0.));
        assert_eq!(2., interval.clamp(2.));
        assert_eq!(2., interval.clamp(3.));
    }

    #[test]
    fn test_size() {
        assert_eq!(0., Interval::new(0., 0.).size());
        assert_eq!(2., Interval::new(-1., 1.).size());
        assert_eq!(-2., Interval::new(1., -1.).size());
    }

    #[test]
    fn test_expand() {
        let interval = Interval::new(-2., 2.);
        assert_eq!(Interval::new(-3., 3.), interval.expand(2.));
        assert_eq!(
            Interval {
                min: -3.5,
                max: 3.5
            },
            interval.expand(3.)
        );
        assert_eq!(Interval::new(-1., 1.), interval.expand(-2.));
    }

    #[test]
    fn test_sub_and_add() {
        let interval = Interval::new(-2., 2.);
        assert_eq!(Interval::new(0., 4.), interval + 2.);
        assert_eq!(Interval::new(1., 5.), interval + 3.);
        assert_eq!(Interval::new(-4., 0.), interval - 2.);
    }
}