geo_types/geometry/rect.rs
1use crate::{coord, polygon, Coord, CoordFloat, CoordNum, Line, Polygon};
2
3/// An _axis-aligned_ bounded 2D rectangle whose area is
4/// defined by minimum and maximum `Coord`s.
5///
6/// The constructors and setters ensure the maximum
7/// `Coord` is greater than or equal to the minimum.
8/// Thus, a `Rect`s width, height, and area is guaranteed to
9/// be greater than or equal to zero.
10///
11/// **Note.** While `Rect` implements `MapCoords` and
12/// `RotatePoint` algorithmic traits, the usage is expected
13/// to maintain the axis alignment. In particular, only
14/// rotation by integer multiples of 90 degrees, will
15/// preserve the original shape. In other cases, the min,
16/// and max points are rotated or transformed, and a new
17/// rectangle is created (with coordinate swaps to ensure
18/// min < max).
19///
20/// # Examples
21///
22/// ```
23/// use geo_types::{coord, Rect};
24///
25/// let rect = Rect::new(
26/// coord! { x: 0., y: 4.},
27/// coord! { x: 3., y: 10.},
28/// );
29///
30/// assert_eq!(3., rect.width());
31/// assert_eq!(6., rect.height());
32/// assert_eq!(
33/// coord! { x: 1.5, y: 7. },
34/// rect.center()
35/// );
36/// ```
37#[derive(Eq, PartialEq, Clone, Copy, Hash)]
38#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
39pub struct Rect<T: CoordNum = f64> {
40 min: Coord<T>,
41 max: Coord<T>,
42}
43
44impl<T: CoordNum> Rect<T> {
45 /// Creates a new rectangle from two corner coordinates.
46 ///
47 /// Coords are stored and returned (by iterators) in CCW order
48 ///
49 /// # Examples
50 ///
51 /// ```
52 /// use geo_types::{coord, Rect};
53 ///
54 /// let rect = Rect::new(
55 /// coord! { x: 10., y: 20. },
56 /// coord! { x: 30., y: 10. }
57 /// );
58 /// assert_eq!(rect.min(), coord! { x: 10., y: 10. });
59 /// assert_eq!(rect.max(), coord! { x: 30., y: 20. });
60 /// ```
61 pub fn new<C>(c1: C, c2: C) -> Self
62 where
63 C: Into<Coord<T>>,
64 {
65 let c1 = c1.into();
66 let c2 = c2.into();
67 let (min_x, max_x) = if c1.x < c2.x {
68 (c1.x, c2.x)
69 } else {
70 (c2.x, c1.x)
71 };
72 let (min_y, max_y) = if c1.y < c2.y {
73 (c1.y, c2.y)
74 } else {
75 (c2.y, c1.y)
76 };
77 Self {
78 min: coord! { x: min_x, y: min_y },
79 max: coord! { x: max_x, y: max_y },
80 }
81 }
82
83 #[deprecated(
84 since = "0.6.2",
85 note = "Use `Rect::new` instead, since `Rect::try_new` will never Error"
86 )]
87 #[allow(deprecated)]
88 pub fn try_new<C>(c1: C, c2: C) -> Result<Rect<T>, InvalidRectCoordinatesError>
89 where
90 C: Into<Coord<T>>,
91 {
92 Ok(Rect::new(c1, c2))
93 }
94
95 /// Returns the minimum `Coord` of the `Rect`.
96 ///
97 /// # Examples
98 ///
99 /// ```rust
100 /// use geo_types::{coord, Rect};
101 ///
102 /// let rect = Rect::new(
103 /// coord! { x: 5., y: 5. },
104 /// coord! { x: 15., y: 15. },
105 /// );
106 ///
107 /// assert_eq!(rect.min(), coord! { x: 5., y: 5. });
108 /// ```
109 pub fn min(self) -> Coord<T> {
110 self.min
111 }
112
113 /// Set the `Rect`’s minimum coordinate.
114 ///
115 /// # Panics
116 ///
117 /// Panics if `min`’s x/y is greater than the maximum coordinate’s x/y.
118 pub fn set_min<C>(&mut self, min: C)
119 where
120 C: Into<Coord<T>>,
121 {
122 self.min = min.into();
123 self.assert_valid_bounds();
124 }
125
126 /// Returns the maximum `Coord` of the `Rect`.
127 ///
128 /// # Examples
129 ///
130 /// ```rust
131 /// use geo_types::{coord, Rect};
132 ///
133 /// let rect = Rect::new(
134 /// coord! { x: 5., y: 5. },
135 /// coord! { x: 15., y: 15. },
136 /// );
137 ///
138 /// assert_eq!(rect.max(), coord! { x: 15., y: 15. });
139 /// ```
140 pub fn max(self) -> Coord<T> {
141 self.max
142 }
143
144 /// Set the `Rect`’s maximum coordinate.
145 ///
146 /// # Panics
147 ///
148 /// Panics if `max`’s x/y is less than the minimum coordinate’s x/y.
149 pub fn set_max<C>(&mut self, max: C)
150 where
151 C: Into<Coord<T>>,
152 {
153 self.max = max.into();
154 self.assert_valid_bounds();
155 }
156
157 /// Returns the width of the `Rect`.
158 ///
159 /// # Examples
160 ///
161 /// ```rust
162 /// use geo_types::{coord, Rect};
163 ///
164 /// let rect = Rect::new(
165 /// coord! { x: 5., y: 5. },
166 /// coord! { x: 15., y: 15. },
167 /// );
168 ///
169 /// assert_eq!(rect.width(), 10.);
170 /// ```
171 pub fn width(self) -> T {
172 self.max().x - self.min().x
173 }
174
175 /// Returns the height of the `Rect`.
176 ///
177 /// # Examples
178 ///
179 /// ```rust
180 /// use geo_types::{coord, Rect};
181 ///
182 /// let rect = Rect::new(
183 /// coord! { x: 5., y: 5. },
184 /// coord! { x: 15., y: 15. },
185 /// );
186 ///
187 /// assert_eq!(rect.height(), 10.);
188 /// ```
189 pub fn height(self) -> T {
190 self.max().y - self.min().y
191 }
192
193 /// Create a `Polygon` from the `Rect`.
194 ///
195 /// # Examples
196 ///
197 /// ```rust
198 /// use geo_types::{coord, Rect, polygon};
199 ///
200 /// let rect = Rect::new(
201 /// coord! { x: 0., y: 0. },
202 /// coord! { x: 1., y: 2. },
203 /// );
204 ///
205 /// // Output is CCW
206 /// assert_eq!(
207 /// rect.to_polygon(),
208 /// polygon![
209 /// (x: 1., y: 0.),
210 /// (x: 1., y: 2.),
211 /// (x: 0., y: 2.),
212 /// (x: 0., y: 0.),
213 /// (x: 1., y: 0.),
214 /// ],
215 /// );
216 /// ```
217 pub fn to_polygon(self) -> Polygon<T> {
218 polygon![
219 (x: self.max.x, y: self.min.y),
220 (x: self.max.x, y: self.max.y),
221 (x: self.min.x, y: self.max.y),
222 (x: self.min.x, y: self.min.y),
223 (x: self.max.x, y: self.min.y),
224 ]
225 }
226
227 pub fn to_lines(&self) -> [Line<T>; 4] {
228 [
229 Line::new(
230 coord! {
231 x: self.max.x,
232 y: self.min.y,
233 },
234 coord! {
235 x: self.max.x,
236 y: self.max.y,
237 },
238 ),
239 Line::new(
240 coord! {
241 x: self.max.x,
242 y: self.max.y,
243 },
244 coord! {
245 x: self.min.x,
246 y: self.max.y,
247 },
248 ),
249 Line::new(
250 coord! {
251 x: self.min.x,
252 y: self.max.y,
253 },
254 coord! {
255 x: self.min.x,
256 y: self.min.y,
257 },
258 ),
259 Line::new(
260 coord! {
261 x: self.min.x,
262 y: self.min.y,
263 },
264 coord! {
265 x: self.max.x,
266 y: self.min.y,
267 },
268 ),
269 ]
270 }
271
272 /// Split a rectangle into two rectangles along the X-axis with equal widths.
273 ///
274 /// # Examples
275 ///
276 /// ```
277 /// let rect = geo_types::Rect::new(
278 /// geo_types::coord! { x: 0., y: 0. },
279 /// geo_types::coord! { x: 4., y: 4. },
280 /// );
281 ///
282 /// let [rect1, rect2] = rect.split_x();
283 ///
284 /// assert_eq!(
285 /// geo_types::Rect::new(
286 /// geo_types::coord! { x: 0., y: 0. },
287 /// geo_types::coord! { x: 2., y: 4. },
288 /// ),
289 /// rect1,
290 /// );
291 /// assert_eq!(
292 /// geo_types::Rect::new(
293 /// geo_types::coord! { x: 2., y: 0. },
294 /// geo_types::coord! { x: 4., y: 4. },
295 /// ),
296 /// rect2,
297 /// );
298 /// ```
299 pub fn split_x(self) -> [Rect<T>; 2] {
300 let two = T::one() + T::one();
301 let mid_x = self.min().x + self.width() / two;
302 [
303 Rect::new(self.min(), coord! { x: mid_x, y: self.max().y }),
304 Rect::new(coord! { x: mid_x, y: self.min().y }, self.max()),
305 ]
306 }
307
308 /// Split a rectangle into two rectangles along the Y-axis with equal heights.
309 ///
310 /// # Examples
311 ///
312 /// ```
313 /// let rect = geo_types::Rect::new(
314 /// geo_types::coord! { x: 0., y: 0. },
315 /// geo_types::coord! { x: 4., y: 4. },
316 /// );
317 ///
318 /// let [rect1, rect2] = rect.split_y();
319 ///
320 /// assert_eq!(
321 /// geo_types::Rect::new(
322 /// geo_types::coord! { x: 0., y: 0. },
323 /// geo_types::coord! { x: 4., y: 2. },
324 /// ),
325 /// rect1,
326 /// );
327 /// assert_eq!(
328 /// geo_types::Rect::new(
329 /// geo_types::coord! { x: 0., y: 2. },
330 /// geo_types::coord! { x: 4., y: 4. },
331 /// ),
332 /// rect2,
333 /// );
334 /// ```
335 pub fn split_y(self) -> [Rect<T>; 2] {
336 let two = T::one() + T::one();
337 let mid_y = self.min().y + self.height() / two;
338 [
339 Rect::new(self.min(), coord! { x: self.max().x, y: mid_y }),
340 Rect::new(coord! { x: self.min().x, y: mid_y }, self.max()),
341 ]
342 }
343
344 fn assert_valid_bounds(&self) {
345 if !self.has_valid_bounds() {
346 panic!("{}", RECT_INVALID_BOUNDS_ERROR);
347 }
348 }
349
350 fn has_valid_bounds(&self) -> bool {
351 self.min.x <= self.max.x && self.min.y <= self.max.y
352 }
353}
354
355impl<T: CoordFloat> Rect<T> {
356 /// Returns the center `Coord` of the `Rect`.
357 ///
358 /// # Examples
359 ///
360 /// ```rust
361 /// use geo_types::{coord, Rect};
362 ///
363 /// let rect = Rect::new(
364 /// coord! { x: 5., y: 5. },
365 /// coord! { x: 15., y: 15. },
366 /// );
367 ///
368 /// assert_eq!(rect.center(), coord! { x: 10., y: 10. });
369 /// ```
370 pub fn center(self) -> Coord<T> {
371 let two = T::one() + T::one();
372 coord! {
373 x: (self.max.x + self.min.x) / two,
374 y: (self.max.y + self.min.y) / two,
375 }
376 }
377}
378
379static RECT_INVALID_BOUNDS_ERROR: &str = "Failed to create Rect: 'min' coordinate's x/y value must be smaller or equal to the 'max' x/y value";
380
381#[cfg(any(feature = "approx", test))]
382mod approx_integration {
383 use super::*;
384 use approx::{AbsDiffEq, RelativeEq, UlpsEq};
385
386 impl<T> RelativeEq for Rect<T>
387 where
388 T: CoordNum + RelativeEq<Epsilon = T>,
389 {
390 #[inline]
391 fn default_max_relative() -> Self::Epsilon {
392 T::default_max_relative()
393 }
394
395 /// Equality assertion within a relative limit.
396 ///
397 /// # Examples
398 ///
399 /// ```
400 /// use geo_types::Rect;
401 ///
402 /// let a = Rect::new((0.0, 0.0), (10.0, 10.0));
403 /// let b = Rect::new((0.0, 0.0), (10.01, 10.0));
404 ///
405 /// approx::assert_relative_eq!(a, b, max_relative=0.1);
406 /// approx::assert_relative_ne!(a, b, max_relative=0.0001);
407 /// ```
408 #[inline]
409 fn relative_eq(
410 &self,
411 other: &Self,
412 epsilon: Self::Epsilon,
413 max_relative: Self::Epsilon,
414 ) -> bool {
415 if !self.min.relative_eq(&other.min, epsilon, max_relative) {
416 return false;
417 }
418
419 if !self.max.relative_eq(&other.max, epsilon, max_relative) {
420 return false;
421 }
422
423 true
424 }
425 }
426
427 impl<T> AbsDiffEq for Rect<T>
428 where
429 T: CoordNum + AbsDiffEq<Epsilon = T>,
430 {
431 type Epsilon = T;
432
433 #[inline]
434 fn default_epsilon() -> Self::Epsilon {
435 T::default_epsilon()
436 }
437
438 /// Equality assertion with an absolute limit.
439 ///
440 /// # Examples
441 ///
442 /// ```
443 /// use geo_types::{point, Rect};
444 ///
445 /// let a = Rect::new((0.0, 0.0), (10.0, 10.0));
446 /// let b = Rect::new((0.0, 0.0), (10.01, 10.0));
447 ///
448 /// approx::abs_diff_eq!(a, b, epsilon=0.1);
449 /// approx::abs_diff_ne!(a, b, epsilon=0.001);
450 /// ```
451 #[inline]
452 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
453 if !self.min.abs_diff_eq(&other.min, epsilon) {
454 return false;
455 }
456
457 if !self.max.abs_diff_eq(&other.max, epsilon) {
458 return false;
459 }
460
461 true
462 }
463 }
464 impl<T> UlpsEq for Rect<T>
465 where
466 T: CoordNum + UlpsEq<Epsilon = T>,
467 {
468 fn default_max_ulps() -> u32 {
469 T::default_max_ulps()
470 }
471
472 fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
473 if !self.min.ulps_eq(&other.min, epsilon, max_ulps) {
474 return false;
475 }
476 if !self.max.ulps_eq(&other.max, epsilon, max_ulps) {
477 return false;
478 }
479 true
480 }
481 }
482}
483
484#[deprecated(
485 since = "0.6.2",
486 note = "Use `Rect::new` instead, since `Rect::try_new` will never Error"
487)]
488#[derive(Debug, Copy, Clone, PartialEq, Eq)]
489pub struct InvalidRectCoordinatesError;
490
491#[cfg(feature = "std")]
492#[allow(deprecated)]
493impl std::error::Error for InvalidRectCoordinatesError {}
494
495#[allow(deprecated)]
496impl core::fmt::Display for InvalidRectCoordinatesError {
497 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
498 write!(f, "{RECT_INVALID_BOUNDS_ERROR}")
499 }
500}
501
502#[cfg(test)]
503mod test {
504 use super::*;
505 use crate::coord;
506
507 #[test]
508 fn rect() {
509 let rect = Rect::new((10, 10), (20, 20));
510 assert_eq!(rect.min, coord! { x: 10, y: 10 });
511 assert_eq!(rect.max, coord! { x: 20, y: 20 });
512
513 let rect = Rect::new((20, 20), (10, 10));
514 assert_eq!(rect.min, coord! { x: 10, y: 10 });
515 assert_eq!(rect.max, coord! { x: 20, y: 20 });
516
517 let rect = Rect::new((10, 20), (20, 10));
518 assert_eq!(rect.min, coord! { x: 10, y: 10 });
519 assert_eq!(rect.max, coord! { x: 20, y: 20 });
520 }
521
522 #[test]
523 fn rect_width() {
524 let rect = Rect::new((10, 10), (20, 20));
525 assert_eq!(rect.width(), 10);
526 }
527
528 #[test]
529 fn rect_height() {
530 let rect = Rect::new((10., 10.), (20., 20.));
531 assert_relative_eq!(rect.height(), 10.);
532 }
533
534 #[test]
535 fn rect_center() {
536 assert_relative_eq!(
537 Rect::new((0., 10.), (10., 90.)).center(),
538 Coord::from((5., 50.))
539 );
540 assert_relative_eq!(
541 Rect::new((-42., -42.), (42., 42.)).center(),
542 Coord::from((0., 0.))
543 );
544 assert_relative_eq!(
545 Rect::new((0., 0.), (0., 0.)).center(),
546 Coord::from((0., 0.))
547 );
548 }
549}