iron_shapes/rect.rs
1// Copyright (c) 2018-2020 Thomas Kramer.
2// SPDX-FileCopyrightText: 2018-2022 Thomas Kramer
3//
4// SPDX-License-Identifier: AGPL-3.0-or-later
5
6//! Data structures and functions for dealing with rectangles which consist of
7//! vertical and horizontal edges.
8
9use crate::cmp::{max, min};
10use crate::edge::IntoEdges;
11use crate::polygon::{Polygon, ToPolygon};
12use crate::prelude::{Point, REdge, Vector};
13use crate::traits::*;
14use crate::CoordinateType;
15use num_traits::{NumCast, One, Zero};
16use std::ops::{Add, Div, Mul, Sub};
17
18/// A rectangle which is oriented along the x an y axis and
19/// represented by its lower left and upper right corner.
20#[derive(Clone, Copy, Hash, Debug, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
22pub struct Rect<T> {
23 /// Lower left corner of the rectangle.
24 pub lower_left: Point<T>,
25 /// Upper right corner of the rectangle.
26 pub upper_right: Point<T>,
27}
28
29impl<T: PartialOrd + Copy> Rect<T> {
30 /// Construct the bounding box of the two points. Order does not matter.
31 ///
32 /// # Examples
33 ///
34 /// ```
35 /// use iron_shapes::prelude::*;
36 ///
37 /// // Create a rectangle based on two corner points.
38 /// let rect1 = Rect::new(Point::new(0, 0), Point::new(1, 2));
39 /// // Any type that implements `Into<Point<T>>` can be used for the corner points.
40 /// let rect2 = Rect::new((1, 2), (0, 0));
41 /// // Ordering of the corner points does not matter.
42 /// assert_eq!(rect1, rect2);
43 /// // Even though `(0, 0)` was passed as second argument it is recognized as lower left corner.
44 /// assert_eq!(rect2.lower_left(), Point::new(0, 0));
45 /// ```
46 pub fn new<C>(c1: C, c2: C) -> Self
47 where
48 C: Into<Point<T>>,
49 {
50 let p1 = c1.into();
51 let p2 = c2.into();
52
53 let (x1, x2) = if p1.x < p2.x {
54 (p1.x, p2.x)
55 } else {
56 (p2.x, p1.x)
57 };
58
59 let (y1, y2) = if p1.y < p2.y {
60 (p1.y, p2.y)
61 } else {
62 (p2.y, p1.y)
63 };
64
65 Rect {
66 lower_left: Point::new(x1, y1),
67 upper_right: Point::new(x2, y2),
68 }
69 }
70}
71
72impl<T: Copy> Rect<T> {
73 /// Get the lower left corner.
74 #[inline]
75 pub fn lower_left(&self) -> Point<T> {
76 self.lower_left
77 }
78
79 /// Get the upper left corner.
80 #[inline]
81 pub fn upper_left(&self) -> Point<T> {
82 Point::new(self.lower_left.x, self.upper_right.y)
83 }
84
85 /// Get the upper right corner.
86 #[inline]
87 pub fn upper_right(&self) -> Point<T> {
88 self.upper_right
89 }
90
91 /// Get the lower right corner.
92 #[inline]
93 pub fn lower_right(&self) -> Point<T> {
94 Point::new(self.upper_right.x, self.lower_left.y)
95 }
96}
97
98impl<T: PartialOrd + Copy> Rect<T> {
99 /// Check if rectangle contains the point.
100 /// Inclusive boundaries.
101 ///
102 /// # Example
103 /// ```
104 /// use iron_shapes::prelude::*;
105 /// let rect = Rect::new((0, 0), (10, 20));
106 /// // Contains point somewhere in the center.
107 /// assert!(rect.contains_point(Point::new(5, 5)));
108 /// // Also contains point on the boundaries.
109 /// assert!(rect.contains_point(Point::new(0, 0)));
110 /// // Does not contain point outside of the rectangle.
111 /// assert!(!rect.contains_point(Point::new(10, 21)));
112 /// ```
113 pub fn contains_point(&self, p: Point<T>) -> bool {
114 self.lower_left.x <= p.x
115 && p.x <= self.upper_right.x
116 && self.lower_left.y <= p.y
117 && p.y <= self.upper_right.y
118 }
119
120 /// Check if rectangle contains the point.
121 /// Exclusive boundaries.
122 ///
123 /// # Example
124 /// ```
125 /// use iron_shapes::prelude::*;
126 /// let rect = Rect::new((0, 0), (10, 20));
127 /// // Contains point somewhere in the center.
128 /// assert!(rect.contains_point_exclusive(Point::new(5, 5)));
129 /// // Does not contain points on boundaries.
130 /// assert!(!rect.contains_point_exclusive(Point::new(0, 0)));
131 /// // Does not contain point outside of the rectangle.
132 /// assert!(!rect.contains_point_exclusive(Point::new(10, 21)));
133 /// ```
134 pub fn contains_point_exclusive(&self, p: Point<T>) -> bool {
135 self.lower_left.x < p.x
136 && p.x < self.upper_right.x
137 && self.lower_left.y < p.y
138 && p.y < self.upper_right.y
139 }
140
141 /// Check if rectangle contains other rectangle.
142 /// Inclusive boundaries.
143 ///
144 /// # Example
145 ///
146 /// ```
147 /// use iron_shapes::prelude::*;
148 ///
149 /// let outer = Rect::new((0, 0), (2, 2));
150 /// let inner = Rect::new((0, 0), (1, 1));
151 /// assert!(outer.contains_rectangle(&inner));
152 /// assert!(!inner.contains_rectangle(&outer));
153 /// ```
154 pub fn contains_rectangle(&self, other: &Self) -> bool {
155 self.contains_point(other.lower_left) && self.contains_point(other.upper_right)
156 }
157
158 /// Check if rectangle contains other rectangle.
159 /// Exclusive boundaries.
160 ///
161 /// # Example
162 ///
163 /// ```
164 /// use iron_shapes::prelude::*;
165 ///
166 /// let outer = Rect::new((0, 0), (3, 3));
167 /// let inner = Rect::new((1, 1), (2, 2));
168 /// assert!(outer.contains_rectangle_exclusive(&inner));
169 /// assert!(!inner.contains_rectangle_exclusive(&outer));
170 ///
171 /// let not_inner = Rect::new((0, 0), (1, 1)); // This shares the boundary with `outer`.
172 /// assert!(!outer.contains_rectangle_exclusive(¬_inner));
173 /// ```
174 pub fn contains_rectangle_exclusive(&self, other: &Self) -> bool {
175 self.contains_point_exclusive(other.lower_left)
176 && self.contains_point_exclusive(other.upper_right)
177 }
178
179 /// Test if the both rectangles touch each other, i.e. if they either share a boundary or are overlapping.
180 pub fn touches(&self, other: &Self) -> bool {
181 !(self.lower_left.x > other.upper_right.x
182 || self.lower_left.y > other.upper_right.y
183 || self.upper_right.x < other.lower_left.x
184 || self.upper_right.y < other.lower_left.y)
185 }
186
187 /// Compute the boolean intersection of two rectangles.
188 /// This function excludes the boundaries, hence a zero-area intersection is considered `None`.
189 /// See `intersection_inclusive_bounds()` zero-area intersections should be returned as `Some(rectangle)`.
190 ///
191 /// # Example
192 ///
193 /// ```
194 /// use iron_shapes::prelude::*;
195 ///
196 /// // Create two overlapping rectangles.
197 /// let a = Rect::new((0, 0), (2, 2));
198 /// let b = Rect::new((1, 1), (3, 3));
199 ///
200 /// // Compute the intersection.
201 /// assert_eq!(a.intersection(&b), Some(Rect::new((1, 1), (2, 2))));
202 ///
203 /// // Create a non-overlapping rectangle.
204 /// let c = Rect::new((100, 100), (200, 200));
205 /// // The intersection with a non-overlapping rectangle is `None`.
206 /// assert_eq!(a.intersection(&c), None);
207 /// ```
208 pub fn intersection(&self, other: &Self) -> Option<Self> {
209 let llx = max(self.lower_left.x, other.lower_left.x);
210 let lly = max(self.lower_left.y, other.lower_left.y);
211
212 let urx = min(self.upper_right.x, other.upper_right.x);
213 let ury = min(self.upper_right.y, other.upper_right.y);
214
215 if llx < urx && lly < ury {
216 Some(Rect::new((llx, lly), (urx, ury)))
217 } else {
218 None
219 }
220 }
221
222 /// Compute the boolean intersection of two rectangles and include the boundaries.
223 /// This allows to get zero-area intersection results for example if the two
224 /// rectangles touch on a boundary or one of the rectangle is already zero-area.
225 ///
226 /// # Example
227 ///
228 /// ```
229 /// use iron_shapes::prelude::*;
230 ///
231 /// // Create two rectangles which intersect in a single point.
232 /// let a = Rect::new((0, 0), (2, 2));
233 /// let b = Rect::new((2, 2), (3, 3));
234 ///
235 /// // Compute the intersection.
236 /// assert_eq!(a.intersection_inclusive_bounds(&b), Some(Rect::new((2, 2), (2, 2))));
237 ///
238 /// ```
239 pub fn intersection_inclusive_bounds(&self, other: &Self) -> Option<Self> {
240 let llx = max(self.lower_left.x, other.lower_left.x);
241 let lly = max(self.lower_left.y, other.lower_left.y);
242
243 let urx = min(self.upper_right.x, other.upper_right.x);
244 let ury = min(self.upper_right.y, other.upper_right.y);
245
246 if llx <= urx && lly <= ury {
247 Some(Rect::new((llx, lly), (urx, ury)))
248 } else {
249 None
250 }
251 }
252
253 /// Create the smallest `Rect` that contains the original `Rect` and the `point`.
254 ///
255 /// # Example
256 ///
257 /// ```
258 /// use iron_shapes::prelude::*;
259 ///
260 /// let r1 = Rect::new((0,0), (1,2));
261 ///
262 /// let r2 = r1.add_point(Point::new(10, 11));
263 ///
264 /// assert_eq!(r2, Rect::new((0,0), (10,11)));
265 ///
266 /// ```
267 pub fn add_point(&self, point: Point<T>) -> Self {
268 Rect::new(
269 Point::new(
270 min(self.lower_left.x, point.x),
271 min(self.lower_left.y, point.y),
272 ),
273 Point::new(
274 max(self.upper_right.x, point.x),
275 max(self.upper_right.y, point.y),
276 ),
277 )
278 }
279
280 /// Get the smallest `Rect` that contains both rectangles `self` and `rect`.
281 ///
282 /// # Example
283 ///
284 /// ```
285 /// use iron_shapes::prelude::*;
286 ///
287 /// let r1 = Rect::new((0,0), (1,2));
288 /// let r2 = Rect::new((4,5), (6,7));
289 ///
290 /// let r3 = r1.add_rect(&r2);
291 ///
292 /// assert_eq!(r3, Rect::new((0,0), (6,7)));
293 ///
294 /// ```
295 pub fn add_rect(&self, rect: &Self) -> Self {
296 self.add_point(rect.lower_left).add_point(rect.upper_right)
297 }
298}
299
300impl<T: Sub<Output = T> + Copy + Ord + Zero> Rect<T> {
301 /// Compute the shortest from the rectangle to the point `p`.
302 /// The distance is zero if the point is inside the rectangle.
303 ///
304 /// # Example
305 ///
306 /// ```
307 /// use iron_shapes::prelude::*;
308 ///
309 /// let r = Rect::new((0,0), (10, 10));
310 ///
311 /// assert_eq!(r.distance_to_point((5, 15).into()), Vector::new(0, 5));
312 ///
313 /// // Distance to point inside the rectangle is zero.
314 /// assert_eq!(r.distance_to_point((5, 5).into()), Vector::new(0, 0));
315 ///
316 /// ```
317 pub fn distance_to_point(&self, p: Point<T>) -> Vector<T> {
318 let ll = self.lower_left();
319 let ul = self.upper_right();
320
321 // Compute x component of distance.
322 let dx_neg = (p.x - ll.x).min(Zero::zero());
323 let dx_pos = (p.x - ul.x).max(Zero::zero());
324 let dx = dx_neg + dx_pos;
325
326 // Compute y component of distance.
327 let dy_neg = (p.y - ll.y).min(Zero::zero());
328 let dy_pos = (p.y - ul.y).max(Zero::zero());
329 let dy = dy_neg + dy_pos;
330
331 Vector::new(dx, dy)
332 }
333}
334
335#[test]
336fn test_distance_to_point() {
337 let rect = Rect::new((10, 10), (20, 20));
338
339 assert_eq!(rect.distance_to_point((15, 15).into()).norm1(), 0);
340 assert_eq!(rect.distance_to_point((10, 10).into()).norm1(), 0);
341 assert_eq!(rect.distance_to_point((10, 20).into()).norm1(), 0);
342
343 assert_eq!(rect.distance_to_point((0, 0).into()).norm1(), 20);
344 assert_eq!(rect.distance_to_point((0, 5).into()).norm1(), 15);
345 assert_eq!(rect.distance_to_point((0, 10).into()).norm1(), 10);
346 assert_eq!(rect.distance_to_point((0, 15).into()).norm1(), 10);
347 assert_eq!(rect.distance_to_point((0, 20).into()).norm1(), 10);
348 assert_eq!(rect.distance_to_point((0, 25).into()).norm1(), 15);
349}
350
351impl<T: Copy + Sub<Output = T>> Rect<T> {
352 /// Compute the width of the rectangle.
353 #[inline]
354 pub fn width(&self) -> T {
355 self.upper_right.x - self.lower_left.x
356 }
357
358 /// Compute the height of the rectangle.
359 #[inline]
360 pub fn height(&self) -> T {
361 self.upper_right.y - self.lower_left.y
362 }
363}
364
365impl<T: Copy + Add<Output = T> + Div<Output = T> + One> Rect<T> {
366 /// Get the center point of the rectangle.
367 /// When using integer coordinates the resulting
368 /// coordinates will be truncated to the next integers.
369 pub fn center(&self) -> Point<T> {
370 let two = T::one() + T::one();
371 (self.lower_left() + self.upper_right()) / two
372 }
373}
374
375impl<T: Copy + Add<Output = T> + Sub<Output = T> + PartialOrd> Rect<T> {
376 /// Create an enlarged copy of this rectangle.
377 /// The vertical boundaries will be shifted towards the outside by `add_x`.
378 /// The horizontal boundaries will be shifted towards the outside by `add_y`.
379 pub fn sized(&self, add_x: T, add_y: T) -> Self {
380 Rect::new(
381 (self.lower_left.x - add_x, self.lower_left.y - add_y),
382 (self.upper_right.x + add_x, self.upper_right.y + add_y),
383 )
384 }
385
386 /// Create an enlarged copy of this rectangle.
387 pub fn sized_isotropic(&self, add: T) -> Self {
388 self.sized(add, add)
389 }
390}
391
392impl<T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T>> DoubledOrientedArea<T>
393 for Rect<T>
394{
395 /// Calculate doubled oriented area of rectangle.
396 fn area_doubled_oriented(&self) -> T {
397 let diff = self.upper_right - self.lower_left;
398 let area = diff.x * diff.y;
399 area + area
400 }
401}
402
403impl<T: Copy> BoundingBox<T> for Rect<T> {
404 /// Get bounding box of rectangle (which is equal to the rectangle itself).
405 fn bounding_box(&self) -> Rect<T> {
406 *self
407 }
408}
409
410impl<T: Copy> TryBoundingBox<T> for Rect<T> {
411 /// Get bounding box of rectangle (always exists).
412 fn try_bounding_box(&self) -> Option<Rect<T>> {
413 Some(*self)
414 }
415}
416
417/// Point wise transformation of the two corner points.
418impl<T: Copy + PartialOrd> MapPointwise<T> for Rect<T> {
419 /// Point wise transformation.
420 fn transform<F>(&self, transformation: F) -> Self
421 where
422 F: Fn(Point<T>) -> Point<T>,
423 {
424 Self::new(
425 transformation(self.lower_left),
426 transformation(self.upper_right),
427 )
428 }
429}
430
431/// Iterate over all points of the rectangle.
432/// Starts with the lower left corner and iterates counter clock-wise.
433impl<'a, T> IntoIterator for &'a Rect<T>
434where
435 T: Copy,
436{
437 type Item = Point<T>;
438 type IntoIter = std::array::IntoIter<Self::Item, 4>;
439
440 fn into_iter(self) -> Self::IntoIter {
441 [
442 self.lower_left(),
443 self.lower_right(),
444 self.upper_right(),
445 self.upper_left(),
446 ]
447 .into_iter()
448 }
449}
450
451/// Iterate over all points of the rectangle.
452/// Starts with the lower left corner and iterates counter clock-wise.
453impl<T> IntoIterator for Rect<T>
454where
455 T: Copy,
456{
457 type Item = Point<T>;
458 type IntoIter = std::array::IntoIter<Self::Item, 4>;
459
460 fn into_iter(self) -> Self::IntoIter {
461 (&self).into_iter()
462 }
463}
464
465impl<T: Copy> ToPolygon<T> for Rect<T> {
466 fn to_polygon(&self) -> Polygon<T> {
467 Polygon::from(self)
468 }
469}
470
471impl<T: CoordinateType + NumCast, Dst: CoordinateType + NumCast> TryCastCoord<T, Dst> for Rect<T> {
472 type Output = Rect<Dst>;
473
474 fn try_cast(&self) -> Option<Self::Output> {
475 match (self.lower_left.try_cast(), self.upper_right.try_cast()) {
476 (Some(ll), Some(ur)) => Some(Rect::new(ll, ur)),
477 _ => None,
478 }
479 }
480}
481
482#[cfg(test)]
483mod tests {
484 use super::*;
485
486 #[test]
487 fn test_rect_intersection() {
488 let a = Rect::new((0, 0), (2, 4));
489 let b = Rect::new((1, 2), (3, 5));
490 assert_eq!(a.intersection(&b), Some(Rect::new((1, 2), (2, 4))));
491
492 let a = Rect::new((0, 0), (2, 2));
493 let b = Rect::new((1, 1), (3, 3));
494 assert_eq!(a.intersection(&b), Some(Rect::new((1, 1), (2, 2))));
495
496 let a = Rect::new((0, 0), (1, 1));
497 let b = Rect::new((2, 2), (3, 3));
498 assert_eq!(a.intersection(&b), None);
499
500 let a = Rect::new((0, 0), (2, 2));
501 let b = Rect::new((1, 2), (5, 5));
502 assert_eq!(a.intersection(&b), None);
503 }
504}
505
506/// Iterator over edges of a rectangle.
507#[derive(Clone)]
508pub struct RectEdgeIterator<T> {
509 rect: Rect<T>,
510 pos: u8,
511}
512
513impl<T> RectEdgeIterator<T> {
514 fn new(rect: Rect<T>) -> Self {
515 Self { rect, pos: 0 }
516 }
517}
518
519impl<T: CoordinateType> Iterator for RectEdgeIterator<T> {
520 type Item = REdge<T>;
521
522 fn next(&mut self) -> Option<Self::Item> {
523 if self.pos >= 4 {
524 None
525 } else {
526 let point = |idx: u8| -> Point<T> {
527 match idx {
528 0 => self.rect.lower_right(),
529 1 => self.rect.upper_right(),
530 2 => self.rect.upper_left(),
531 3 => self.rect.lower_left(),
532 _ => unreachable!(),
533 }
534 };
535 let edge = REdge::new(point(self.pos), point((self.pos + 1) % 4));
536 self.pos += 1;
537 Some(edge)
538 }
539 }
540}
541
542impl<T: CoordinateType> IntoEdges<T> for &Rect<T> {
543 type Edge = REdge<T>;
544 type EdgeIter = RectEdgeIterator<T>;
545
546 fn into_edges(self) -> Self::EdgeIter {
547 RectEdgeIterator::new(*self)
548 }
549}
550
551#[test]
552fn test_edges_iterator() {
553 let rect = Rect::new((1, 2), (3, 4));
554 let edges: Vec<_> = rect.into_edges().collect();
555 assert_eq!(
556 edges,
557 vec![
558 REdge::new((3, 2), (3, 4)),
559 REdge::new((3, 4), (1, 4)),
560 REdge::new((1, 4), (1, 2)),
561 REdge::new((1, 2), (3, 2)),
562 ]
563 );
564}