svg_nd/
range.rs

1/*a Copyright
2
3Licensed under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License.
5You may obtain a copy of the License at
6
7  http://www.apache.org/licenses/LICENSE-2.0
8
9Unless required by applicable law or agreed to in writing, software
10distributed under the License is distributed on an "AS IS" BASIS,
11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12See the License for the specific language governing permissions and
13limitations under the License.
14
15@file    range.rs
16@brief   Part of SVG library
17 */
18
19//a Range
20//tp Range
21#[derive(Debug, Clone, Copy, PartialEq)]
22/// This is a simple 'range' class for a single dimension
23///
24/// min <= max for a valid range; min > max indicates an empty range
25pub struct Range {
26    /// Minimum coordinate of the range
27    min: f64,
28    /// Maximum coordinate of the range
29    max: f64,
30}
31
32//ti Display for Range
33impl std::fmt::Display for Range {
34    /// Display the [Range] as (min to max)
35    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
36        write!(f, "({} to {})", self.min, self.max)
37    }
38}
39
40//tp Default for Range
41impl std::default::Default for Range {
42    fn default() -> Self {
43        Self::none()
44    }
45}
46
47//ti Range
48impl Range {
49    //fp new
50    /// Create a new point from (min,max)
51    #[must_use]
52    #[inline]
53    pub fn new(min: f64, max: f64) -> Self {
54        Self { min, max }
55    }
56
57    //fp none
58    /// Create a new empty range (0,0)
59    #[must_use]
60    #[inline]
61    pub const fn none() -> Self {
62        Self { min: 0., max: -1. }
63    }
64
65    //fp is_none
66    /// Return true if the range is empty
67    #[inline]
68    pub fn is_none(&self) -> bool {
69        self.min > self.max
70    }
71
72    //fp of_pts
73    #[must_use]
74    #[inline]
75    pub fn of_pts(a: f64, b: f64) -> Self {
76        if a < b {
77            Self::new(a, b)
78        } else {
79            Self::new(b, a)
80        }
81    }
82
83    //mp size
84    /// Return the size of the range
85    #[inline]
86    pub fn size(&self) -> f64 {
87        if self.is_none() {
88            0.
89        } else {
90            self.max - self.min
91        }
92    }
93
94    //mp center
95    /// Return the center of the range
96    #[inline]
97    pub fn center(&self) -> f64 {
98        (self.max + self.min) / 2.0
99    }
100
101    //cp include
102    /// Include a point into the range, exanding min or max if required
103    #[must_use]
104    #[inline]
105    pub fn include(mut self, x: f64) -> Self {
106        if self.is_none() {
107            self.min = x;
108            self.max = x;
109        } else {
110            if x < self.min {
111                self.min = x;
112            }
113            if x > self.max {
114                self.max = x;
115            }
116        }
117        self
118    }
119
120    //cp enlarge
121    /// Enlarge by an amount
122    #[must_use]
123    #[inline]
124    pub fn enlarge(mut self, value: f64) -> Self {
125        if !self.is_none() {
126            self.min -= value;
127            self.max += value;
128        }
129        self
130    }
131
132    //cp reduce
133    /// Reduce by an amount
134    #[must_use]
135    #[inline]
136    pub fn reduce(mut self, value: f64) -> Self {
137        if !self.is_none() {
138            self.min += value;
139            self.max -= value;
140        }
141        self
142    }
143
144    //cp union
145    /// Consume the range, and find the union min and max of this with
146    /// another, returning the new region
147    #[must_use]
148    #[inline]
149    pub fn union(mut self, other: &Range) -> Self {
150        if other.is_none() {
151            self
152        } else if self.is_none() {
153            self.min = other.min;
154            self.max = other.max;
155            self
156        } else {
157            if other.min < self.min {
158                self.min = other.min;
159            }
160            if other.max > self.max {
161                self.max = other.max;
162            }
163            self
164        }
165    }
166
167    //cp intersect
168    /// Consume the range, and find the intersection min and max of this with
169    /// another, returning the new region
170    #[must_use]
171    #[inline]
172    pub fn intersect(mut self, other: &Range) -> Self {
173        if other.is_none() {
174            self
175        } else if self.is_none() {
176            self.min = other.min;
177            self.max = other.max;
178            self
179        } else {
180            if other.min > self.min {
181                self.min = other.min;
182            }
183            if other.max < self.max {
184                self.max = other.max;
185            }
186            self
187        }
188    }
189}
190
191//ip std::ops::Add<f64> for Range
192impl std::ops::Add<f64> for Range {
193    type Output = Self;
194    fn add(self, scale: f64) -> Self {
195        Self {
196            min: self.min + scale,
197            max: self.max + scale,
198        }
199    }
200}
201
202//ip std::ops::AddAssign<f64> for Range
203impl std::ops::AddAssign<f64> for Range {
204    fn add_assign(&mut self, delta: f64) {
205        self.min += delta;
206        self.max += delta;
207    }
208}
209
210//ip std::ops::Sub<f64> for Range
211impl std::ops::Sub<f64> for Range {
212    type Output = Self;
213    fn sub(self, scale: f64) -> Self {
214        Self {
215            min: self.min - scale,
216            max: self.max - scale,
217        }
218    }
219}
220
221//ip std::ops::SubAssign<f64> for Range
222impl std::ops::SubAssign<f64> for Range {
223    fn sub_assign(&mut self, delta: f64) {
224        self.min -= delta;
225        self.max -= delta;
226    }
227}
228
229//ip std::ops::Mul<f64> for Range
230impl std::ops::Mul<f64> for Range {
231    type Output = Self;
232    fn mul(self, scale: f64) -> Self {
233        if scale < 0. {
234            Self {
235                min: self.max * scale,
236                max: self.min * scale,
237            }
238        } else {
239            Self {
240                min: self.min * scale,
241                max: self.max * scale,
242            }
243        }
244    }
245}
246//ip std::ops::MulAssign<f64> for Range
247impl std::ops::MulAssign<f64> for Range {
248    fn mul_assign(&mut self, scale: f64) {
249        self.max *= scale;
250        self.min *= scale;
251    }
252}
253
254//ip std::ops::Div<f64> for Range
255impl std::ops::Div<f64> for Range {
256    type Output = Self;
257    fn div(self, scale: f64) -> Self {
258        if scale < 0. {
259            Self {
260                min: self.max / scale,
261                max: self.min / scale,
262            }
263        } else {
264            Self {
265                min: self.min / scale,
266                max: self.max / scale,
267            }
268        }
269    }
270}
271//ip std::ops::DivAssign<f64> for Range
272impl std::ops::DivAssign<f64> for Range {
273    fn div_assign(&mut self, scale: f64) {
274        self.max /= scale;
275        self.min /= scale;
276    }
277}
278
279//ip std::ops::Index<usize> for Range
280impl std::ops::Index<usize> for Range {
281    type Output = f64;
282
283    #[inline]
284    fn index(&self, index: usize) -> &Self::Output {
285        assert!(index < 2);
286        if index == 0 {
287            &self.min
288        } else {
289            &self.max
290        }
291    }
292}
293
294//a Tests
295//mt Test for Range
296#[cfg(test)]
297mod test_range {
298    use super::*;
299    pub fn rng_eq(rng: &Range, min: f64, max: f64) {
300        assert!(
301            (rng.min - min).abs() < 1E-8,
302            "mismatch in x {:?} {} {}",
303            rng,
304            min,
305            max
306        );
307        assert!(
308            (rng.max - max).abs() < 1E-8,
309            "mismatch in x {:?} {} {}",
310            rng,
311            min,
312            max
313        );
314    }
315    #[test]
316    fn test_simple() {
317        assert!(Range::none().is_none());
318        rng_eq(&Range::new(1., 2.), 1., 2.);
319        assert!(Range::new(0.1, 0.).is_none());
320        assert!(!Range::new(0., 0.1).is_none());
321        rng_eq(&(Range::new(1., 2.) * 3.), 3., 6.);
322        rng_eq(&(Range::new(3., 6.) / 3.), 1., 2.);
323
324        assert_eq!(Range::none().size(), 0.);
325        assert_eq!(Range::new(1., 0.).size(), 0.);
326        assert_eq!(Range::new(0., 1.).size(), 1.);
327        assert_eq!(Range::new(2., 0.).size(), 0.);
328        assert_eq!(Range::new(0., 2.).size(), 2.);
329    }
330    #[test]
331    fn test_union() {
332        rng_eq(&Range::new(0., 4.).union(&Range::new(0., 4.)), 0., 4.);
333        rng_eq(&Range::new(0., 4.).union(&Range::new(0., 5.)), 0., 5.);
334        rng_eq(&Range::new(0., 4.).union(&Range::new(2., 5.)), 0., 5.);
335        rng_eq(&Range::new(0., 4.).union(&Range::new(2., 3.)), 0., 4.);
336        rng_eq(&Range::new(0., 4.).union(&Range::new(-1., 3.)), -1., 4.);
337        rng_eq(&Range::new(0., 4.).union(&Range::new(-1., 5.)), -1., 5.);
338    }
339    #[test]
340    fn test_intersect() {
341        rng_eq(&Range::new(0., 4.).intersect(&Range::new(0., 4.)), 0., 4.);
342        rng_eq(&Range::new(0., 4.).intersect(&Range::new(0., 5.)), 0., 4.);
343        rng_eq(&Range::new(0., 4.).intersect(&Range::new(2., 5.)), 2., 4.);
344        rng_eq(&Range::new(0., 4.).intersect(&Range::new(2., 3.)), 2., 3.);
345        rng_eq(&Range::new(0., 4.).intersect(&Range::new(-1., 3.)), 0., 3.);
346        rng_eq(&Range::new(0., 4.).intersect(&Range::new(-1., 5.)), 0., 4.);
347    }
348}