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
3708269
    fn min<T: PartialOrd>(x: T, y: T) -> T {
16
3708269
        if x <= y {
17
480720
            x
18
        } else {
19
3227549
            y
20
        }
21
3708269
    }
22

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

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

            
39
    impl<T> Rect<T> {
40
        #[inline]
41
4283620
        pub fn new(x0: T, y0: T, x1: T, y1: T) -> Self {
42
4283620
            Self { x0, y0, x1, y1 }
43
4283620
        }
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
55041
        pub fn from_size(w: T, h: T) -> Self {
52
55041
            Self {
53
55041
                x0: Zero::zero(),
54
55041
                y0: Zero::zero(),
55
                x1: w,
56
                y1: h,
57
            }
58
55041
        }
59

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

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

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

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

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

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

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

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

            
109
745
            if x1 > x0 && y1 > y0 {
110
741
                Some(Self { x0, y0, x1, y1 })
111
            } else {
112
4
                None
113
            }
114
745
        }
115

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

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

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

            
146
    impl Rect<f64> {
147
        #[inline]
148
503188
        pub fn is_empty(&self) -> bool {
149
503188
            self.width().approx_eq_cairo(0.0) || self.height().approx_eq_cairo(0.0)
150
503188
        }
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
55
        pub fn approx_eq(&self, other: &Self) -> bool {
163
            // FIXME: this is super fishy; shouldn't we be using 2x the epsilon against the width/height
164
            // instead of the raw coordinates?
165
56
            approx_eq!(f64, self.x0, other.x0, epsilon = 0.0001)
166
54
                && approx_eq!(f64, self.y0, other.y0, epsilon = 0.0001)
167
53
                && approx_eq!(f64, self.x1, other.x1, epsilon = 0.0001)
168
53
                && approx_eq!(f64, self.y1, other.y1, epsilon = 0.00012)
169
53
        }
170
    }
171
}
172

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

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

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

            
199
impl From<Rect> for cairo::Rectangle {
200
    #[inline]
201
682
    fn from(r: Rect) -> Self {
202
682
        Self::new(r.x0, r.y0, r.x1 - r.x0, r.y1 - r.y0)
203
682
    }
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
243
pub fn rect_to_transform(rect: &Option<Rect>, units: CoordUnits) -> Result<Transform, ()> {
219
243
    match units {
220
146
        CoordUnits::UserSpaceOnUse => Ok(Transform::identity()),
221
        CoordUnits::ObjectBoundingBox => {
222
189
            if rect.as_ref().map_or(true, |r| r.is_empty()) {
223
5
                Err(())
224
            } else {
225
92
                let r = rect.as_ref().unwrap();
226
92
                let t = Transform::new_unchecked(r.width(), 0.0, 0.0, r.height(), r.x0, r.y0);
227

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

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

            
240
impl From<IRect> for Rect {
241
    #[inline]
242
115
    fn from(r: IRect) -> Self {
243
115
        Self {
244
115
            x0: f64::from(r.x0),
245
115
            y0: f64::from(r.y0),
246
115
            x1: f64::from(r.x1),
247
115
            y1: f64::from(r.y1),
248
        }
249
115
    }
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
171
    fn from(r: IRect) -> Self {
267
171
        Self::new(
268
171
            f64::from(r.x0),
269
171
            f64::from(r.y0),
270
171
            f64::from(r.x1 - r.x0),
271
171
            f64::from(r.y1 - r.y0),
272
        )
273
171
    }
274
}