1
//! Types for rectangles.
2

            
3
use crate::coord_units::CoordUnits;
4
use crate::transform::Transform;
5

            
6
#[allow(clippy::module_inception)]
7
mod rect {
8
    use crate::float_eq_cairo::ApproxEqCairo;
9
    use core::ops::{Add, Range, Sub};
10
    use float_cmp::approx_eq;
11
    use num_traits::Zero;
12

            
13
    // Use our own min() and max() that are acceptable for floating point
14

            
15
70481154
    fn min<T: PartialOrd>(x: T, y: T) -> T {
16
70481154
        if x <= y {
17
9492630
            x
18
        } else {
19
60988524
            y
20
        }
21
70481154
    }
22

            
23
70481154
    fn max<T: PartialOrd>(x: T, y: T) -> T {
24
70481154
        if x >= y {
25
44209776
            x
26
        } else {
27
26271378
            y
28
        }
29
70481154
    }
30

            
31
    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
32
    pub struct Rect<T> {
33
        pub x0: T,
34
        pub y0: T,
35
        pub x1: T,
36
        pub y1: T,
37
    }
38

            
39
    impl<T> Rect<T> {
40
        #[inline]
41
81526743
        pub fn new(x0: T, y0: T, x1: T, y1: T) -> Self {
42
81526743
            Self { x0, y0, x1, y1 }
43
81526743
        }
44
    }
45

            
46
    impl<T> Rect<T>
47
    where
48
        T: Copy + PartialOrd + PartialEq + Add<T, Output = T> + Sub<T, Output = T> + Zero,
49
    {
50
        #[inline]
51
1044504
        pub fn from_size(w: T, h: T) -> Self {
52
1044504
            Self {
53
1044504
                x0: Zero::zero(),
54
1044504
                y0: Zero::zero(),
55
1044504
                x1: w,
56
1044504
                y1: h,
57
1044504
            }
58
1044504
        }
59

            
60
        #[inline]
61
44782798
        pub fn width(&self) -> T {
62
44782798
            self.x1 - self.x0
63
44782798
        }
64

            
65
        #[inline]
66
38658365
        pub fn height(&self) -> T {
67
38658365
            self.y1 - self.y0
68
38658365
        }
69

            
70
        #[inline]
71
59472
        pub fn size(&self) -> (T, T) {
72
59472
            (self.width(), self.height())
73
59472
        }
74

            
75
        #[inline]
76
21071
        pub fn x_range(&self) -> Range<T> {
77
21071
            self.x0..self.x1
78
21071
        }
79

            
80
        #[inline]
81
380
        pub fn y_range(&self) -> Range<T> {
82
380
            self.y0..self.y1
83
380
        }
84

            
85
        #[inline]
86
75774095
        pub fn contains(self, x: T, y: T) -> bool {
87
75774095
            x >= self.x0 && x < self.x1 && y >= self.y0 && y < self.y1
88
75774095
        }
89

            
90
        #[inline]
91
41198
        pub fn translate(&self, by: (T, T)) -> Self {
92
41198
            Self {
93
41198
                x0: self.x0 + by.0,
94
41198
                y0: self.y0 + by.1,
95
41198
                x1: self.x1 + by.0,
96
41198
                y1: self.y1 + by.1,
97
41198
            }
98
41198
        }
99

            
100
        #[inline]
101
14119
        pub fn intersection(&self, rect: &Self) -> Option<Self> {
102
14119
            let (x0, y0, x1, y1) = (
103
14119
                max(self.x0, rect.x0),
104
14119
                max(self.y0, rect.y0),
105
14119
                min(self.x1, rect.x1),
106
14119
                min(self.y1, rect.y1),
107
14119
            );
108
14119

            
109
14119
            if x1 > x0 && y1 > y0 {
110
14061
                Some(Self { x0, y0, x1, y1 })
111
            } else {
112
58
                None
113
            }
114
14119
        }
115

            
116
        #[inline]
117
35226458
        pub fn union(&self, rect: &Self) -> Self {
118
35226458
            Self {
119
35226458
                x0: min(self.x0, rect.x0),
120
35226458
                y0: min(self.y0, rect.y0),
121
35226458
                x1: max(self.x1, rect.x1),
122
35226458
                y1: max(self.y1, rect.y1),
123
35226458
            }
124
35226458
        }
125
    }
126

            
127
    impl Rect<i32> {
128
        #[inline]
129
57
        pub fn is_empty(&self) -> bool {
130
57
            // Give an explicit type to the right hand side of the ==, since sometimes
131
57
            // type inference fails to figure it out.  I have no idea why.
132
57
            self.width() == <i32 as Zero>::zero() || self.height() == <i32 as Zero>::zero()
133
57
        }
134

            
135
        #[inline]
136
95
        pub fn scale(self, x: f64, y: f64) -> Self {
137
95
            Self {
138
95
                x0: (f64::from(self.x0) * x).floor() as i32,
139
95
                y0: (f64::from(self.y0) * y).floor() as i32,
140
95
                x1: (f64::from(self.x1) * x).ceil() as i32,
141
95
                y1: (f64::from(self.y1) * y).ceil() as i32,
142
95
            }
143
95
        }
144
    }
145

            
146
    impl Rect<f64> {
147
        #[inline]
148
9559648
        pub fn is_empty(&self) -> bool {
149
9559648
            self.width().approx_eq_cairo(0.0) || self.height().approx_eq_cairo(0.0)
150
9559648
        }
151

            
152
        #[inline]
153
        pub fn scale(self, x: f64, y: f64) -> Self {
154
            Self {
155
                x0: self.x0 * x,
156
                y0: self.y0 * y,
157
                x1: self.x1 * x,
158
                y1: self.y1 * y,
159
            }
160
        }
161

            
162
1026
        pub fn approx_eq(&self, other: &Self) -> bool {
163
1026
            // FIXME: this is super fishy; shouldn't we be using 2x the epsilon against the width/height
164
1026
            // instead of the raw coordinates?
165
1026
            approx_eq!(f64, self.x0, other.x0, epsilon = 0.0001)
166
1026
                && approx_eq!(f64, self.y0, other.y0, epsilon = 0.0001)
167
1026
                && approx_eq!(f64, self.x1, other.x1, epsilon = 0.0001)
168
1026
                && approx_eq!(f64, self.y1, other.y1, epsilon = 0.00012)
169
1026
        }
170
    }
171
}
172

            
173
pub type Rect = rect::Rect<f64>;
174

            
175
impl From<Rect> for IRect {
176
    #[inline]
177
13129
    fn from(r: Rect) -> Self {
178
13129
        Self {
179
13129
            x0: r.x0.floor() as i32,
180
13129
            y0: r.y0.floor() as i32,
181
13129
            x1: r.x1.ceil() as i32,
182
13129
            y1: r.y1.ceil() as i32,
183
13129
        }
184
13129
    }
185
}
186

            
187
impl From<cairo::Rectangle> for Rect {
188
    #[inline]
189
20430
    fn from(r: cairo::Rectangle) -> Self {
190
20430
        Self {
191
20430
            x0: r.x(),
192
20430
            y0: r.y(),
193
20430
            x1: r.x() + r.width(),
194
20430
            y1: r.y() + r.height(),
195
20430
        }
196
20430
    }
197
}
198

            
199
impl From<Rect> for cairo::Rectangle {
200
    #[inline]
201
11647
    fn from(r: Rect) -> Self {
202
11647
        Self::new(r.x0, r.y0, r.x1 - r.x0, r.y1 - r.y0)
203
11647
    }
204
}
205

            
206
/// Creates a transform to map to a rectangle.
207
///
208
/// The rectangle is an `Option<Rect>` to indicate the possibility that there is no
209
/// bounding box from where the rectangle could be obtained.
210
///
211
/// This depends on a `CoordUnits` parameter.  When this is
212
/// `CoordUnits::ObjectBoundingBox`, the bounding box must not be empty, since the calling
213
/// code would then not have a usable size to work with.  In that case, if the bbox is
214
/// empty, this function returns `Err(())`.
215
///
216
/// Usually calling code can simply ignore the action it was about to take if this
217
/// function returns an error.
218
5339
pub fn rect_to_transform(rect: &Option<Rect>, units: CoordUnits) -> Result<Transform, ()> {
219
5339
    match units {
220
3306
        CoordUnits::UserSpaceOnUse => Ok(Transform::identity()),
221
        CoordUnits::ObjectBoundingBox => {
222
2033
            if rect.as_ref().is_none_or(|r| r.is_empty()) {
223
114
                Err(())
224
            } else {
225
1919
                let r = rect.as_ref().unwrap();
226
1919
                let t = Transform::new_unchecked(r.width(), 0.0, 0.0, r.height(), r.x0, r.y0);
227
1919

            
228
1919
                if t.is_invertible() {
229
1919
                    Ok(t)
230
                } else {
231
                    Err(())
232
                }
233
            }
234
        }
235
    }
236
5339
}
237

            
238
pub type IRect = rect::Rect<i32>;
239

            
240
impl From<IRect> for Rect {
241
    #[inline]
242
2185
    fn from(r: IRect) -> Self {
243
2185
        Self {
244
2185
            x0: f64::from(r.x0),
245
2185
            y0: f64::from(r.y0),
246
2185
            x1: f64::from(r.x1),
247
2185
            y1: f64::from(r.y1),
248
2185
        }
249
2185
    }
250
}
251

            
252
impl From<cairo::Rectangle> for IRect {
253
    #[inline]
254
    fn from(r: cairo::Rectangle) -> Self {
255
        Self {
256
            x0: r.x().floor() as i32,
257
            y0: r.y().floor() as i32,
258
            x1: (r.x() + r.width()).ceil() as i32,
259
            y1: (r.y() + r.height()).ceil() as i32,
260
        }
261
    }
262
}
263

            
264
impl From<IRect> for cairo::Rectangle {
265
    #[inline]
266
3173
    fn from(r: IRect) -> Self {
267
3173
        Self::new(
268
3173
            f64::from(r.x0),
269
3173
            f64::from(r.y0),
270
3173
            f64::from(r.x1 - r.x0),
271
3173
            f64::from(r.y1 - r.y0),
272
3173
        )
273
3173
    }
274
}