Skip to main content

font_types/
bbox.rs

1use core::ops::Mul;
2
3/// Minimum and maximum extents of a rectangular region.
4#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6pub struct BoundingBox<T> {
7    /// Minimum extent in the x direction-- the left side of a region.
8    pub x_min: T,
9    /// Minimum extent in the y direction. In a Y-up coordinate system,
10    /// which is used by fonts, this represents the bottom of a region.
11    pub y_min: T,
12    /// Maximum extent in the x direction-- the right side of a region.
13    pub x_max: T,
14    /// Maximum extend in the y direction. In a Y-up coordinate system,
15    /// which is used by fonts, this represents the top of the
16    /// region.
17    pub y_max: T,
18}
19
20impl<T> BoundingBox<T>
21where
22    T: Mul<Output = T> + Copy,
23{
24    /// Return a `BoundingBox` scaled by a scale factor of the same type
25    /// as the stored bounds.
26    ///
27    /// Note: a negative scale factor will flip the bounding box, so you may
28    /// want to call `normalize` after scaling.
29    pub fn scale(&self, factor: T) -> Self {
30        Self {
31            x_min: self.x_min * factor,
32            y_min: self.y_min * factor,
33            x_max: self.x_max * factor,
34            y_max: self.y_max * factor,
35        }
36    }
37}
38
39impl<T> BoundingBox<T>
40where
41    T: PartialOrd + Copy,
42{
43    /// Return `true` if the bounding box is normalized, i.e. `x_min <= x_max`
44    /// and `y_min <= y_max`.
45    pub fn is_normalized(&self) -> bool {
46        self.x_min <= self.x_max && self.y_min <= self.y_max
47    }
48
49    /// Return a `BoundingBox` with the min and max values normalized so that
50    /// `x_min <= x_max` and `y_min <= y_max`.
51    pub fn normalize(&self) -> Self {
52        let (x_min, x_max) = if self.x_min <= self.x_max {
53            (self.x_min, self.x_max)
54        } else {
55            (self.x_max, self.x_min)
56        };
57        let (y_min, y_max) = if self.y_min <= self.y_max {
58            (self.y_min, self.y_max)
59        } else {
60            (self.y_max, self.y_min)
61        };
62        Self {
63            x_min,
64            y_min,
65            x_max,
66            y_max,
67        }
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    #[test]
76    fn normalize() {
77        let bbox = BoundingBox {
78            x_min: 10,
79            y_min: 20,
80            x_max: 5,
81            y_max: 15,
82        };
83        assert!(!bbox.is_normalized());
84        let normalized = bbox.normalize();
85        assert!(normalized.is_normalized());
86        assert_eq!(normalized.x_min, 5);
87        assert_eq!(normalized.y_min, 15);
88        assert_eq!(normalized.x_max, 10);
89        assert_eq!(normalized.y_max, 20);
90    }
91
92    #[test]
93    fn normalize_negative_scaled() {
94        let bbox = BoundingBox {
95            x_min: 5,
96            y_min: 5,
97            x_max: 10,
98            y_max: 10,
99        };
100        assert_eq!(bbox.normalize(), bbox);
101        let scaled = bbox.scale(-2);
102        assert!(!scaled.is_normalized());
103        let normalized = scaled.normalize();
104        assert_eq!(normalized.x_min, -20);
105        assert_eq!(normalized.y_min, -20);
106        assert_eq!(normalized.x_max, -10);
107        assert_eq!(normalized.y_max, -10);
108    }
109}