geometry/rect.rs
1//! Axis-aligned rectangles.
2
3use serde::{Deserialize, Serialize};
4
5use crate::bbox::Bbox;
6use crate::contains::{Containment, Contains};
7use crate::corner::Corner;
8use crate::dims::Dims;
9use crate::dir::Dir;
10use crate::edge::Edge;
11use crate::intersect::Intersect;
12use crate::point::Point;
13use crate::side::{Side, Sides};
14use crate::span::Span;
15use crate::transform::{
16 Transform, TransformMut, TransformRef, Transformation, Translate, TranslateMut, TranslateRef,
17};
18use crate::union::BoundingUnion;
19
20/// An axis-aligned rectangle, specified by lower-left and upper-right corners.
21#[derive(
22 Debug, Default, Copy, Clone, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord,
23)]
24pub struct Rect {
25 /// The lower-left corner.
26 p0: Point,
27 /// The upper-right corner.
28 p1: Point,
29}
30
31impl Rect {
32 /// Creates a rectangle with corners `(0, 0), (dims.w(), dims.h())`.
33 ///
34 /// # Example
35 ///
36 /// ```
37 /// # use geometry::prelude::*;
38 /// let dims = Dims::new(100, 200);
39 /// let rect = Rect::from_dims(dims);
40 /// assert_eq!(rect.top(), 200);
41 /// assert_eq!(rect.bot(), 0);
42 /// assert_eq!(rect.left(), 0);
43 /// assert_eq!(rect.right(), 100);
44 /// ```
45 pub fn from_dims(dims: Dims) -> Self {
46 Self::new(Point::zero(), Point::new(dims.w(), dims.h()))
47 }
48
49 /// Returns the center point of the rectangle.
50 ///
51 /// Note that the center point will be rounded to integer coordinates.
52 /// The current behavior is to round down, but this is subject to change;
53 /// users should not rely on this behavior.
54 ///
55 /// # Examples
56 ///
57 /// ```
58 /// # use geometry::prelude::*;
59 /// let rect = Rect::from_sides(0, 0, 200, 100);
60 /// assert_eq!(rect.center(), Point::new(100, 50));
61 /// ```
62 ///
63 /// Center points are rounded:
64 /// ```
65 /// # use geometry::prelude::*;
66 /// let rect = Rect::from_sides(0, 0, 55, 45);
67 /// assert_eq!(rect.center(), Point::new(27, 22));
68 /// ```
69 pub const fn center(&self) -> Point {
70 Point::new((self.p0.x + self.p1.x) / 2, (self.p0.y + self.p1.y) / 2)
71 }
72
73 /// Creates a zero-area rectangle containing the given point.
74 ///
75 /// # Example
76 ///
77 /// ```
78 /// # use geometry::prelude::*;
79 /// let rect = Rect::from_point(Point::new(25, 60));
80 /// assert_eq!(rect.top(), 60);
81 /// assert_eq!(rect.bot(), 60);
82 /// assert_eq!(rect.left(), 25);
83 /// assert_eq!(rect.right(), 25);
84 /// ```
85 #[inline]
86 pub const fn from_point(p: Point) -> Self {
87 Self { p0: p, p1: p }
88 }
89
90 /// Creates a rectangle from all 4 sides (left, bottom, right, top).
91 ///
92 /// # Example
93 ///
94 /// ```
95 /// # use geometry::prelude::*;
96 /// let rect = Rect::from_sides(15, 20, 30, 40);
97 /// assert_eq!(rect.left(), 15);
98 /// assert_eq!(rect.bot(), 20);
99 /// assert_eq!(rect.right(), 30);
100 /// assert_eq!(rect.top(), 40);
101 /// ```
102 ///
103 /// # Panics
104 ///
105 /// This method panics if `left > right` or if `bot > top`.
106 ///
107 /// If you want sides to be sorted for you, consider using [`Rect::new`] instead.
108 #[inline]
109 pub fn from_sides(left: i64, bot: i64, right: i64, top: i64) -> Self {
110 assert!(
111 left <= right,
112 "Rect::from_sides requires that left ({}) <= right ({})",
113 left,
114 right
115 );
116 assert!(
117 bot <= top,
118 "Rect::from_sides requires that bot ({}) <= top ({})",
119 bot,
120 top
121 );
122 Self::new(Point::new(left, bot), Point::new(right, top))
123 }
124
125 /// Creates a rectangle from all 4 sides (left, bottom, right, top), without checking
126 /// that `left <= right` and `bot <= top`.
127 ///
128 /// # Safety
129 ///
130 /// The caller must ensure that `left <= right` and that `bot <= top`.
131 #[inline]
132 pub const unsafe fn from_sides_unchecked(left: i64, bot: i64, right: i64, top: i64) -> Self {
133 unsafe { Self::new_unchecked(Point::new(left, bot), Point::new(right, top)) }
134 }
135
136 /// Creates a rectangle from a lower left and upper right corner point,
137 /// but returns `None` if the given sides would make the rectangle empty.
138 ///
139 /// The rectangle is empty if the left edge is beyond the right edge,
140 /// or if the bottom edge is above the top edge.
141 ///
142 /// # Example
143 ///
144 /// ```
145 /// # use geometry::prelude::*;
146 /// let rect = Rect::from_corners_option(Point::new(15, 20), Point::new(30, 40));
147 /// assert_eq!(rect, Some(Rect::from_sides(15, 20, 30, 40)));
148 ///
149 /// let rect = Rect::from_corners_option(Point::new(10, 20), Point::new(0, 40));
150 /// assert_eq!(rect, None);
151 /// ```
152 #[inline]
153 pub fn from_corners_option(ll: Point, ur: Point) -> Option<Self> {
154 if ll.x > ur.x || ll.y > ur.y {
155 None
156 } else {
157 Some(unsafe { Self::new_unchecked(ll, ur) })
158 }
159 }
160
161 /// Creates a rectangle from all 4 sides (left, bottom, right, top),
162 /// but returns `None` if the given sides would make the rectangle empty.
163 ///
164 /// The rectangle is empty if the left edge is beyond the right edge,
165 /// or if the bottom edge is above the top edge.
166 ///
167 /// # Example
168 ///
169 /// ```
170 /// # use geometry::prelude::*;
171 /// let rect = Rect::from_sides_option(15, 20, 30, 40);
172 /// assert_eq!(rect, Some(Rect::from_sides(15, 20, 30, 40)));
173 ///
174 /// let rect = Rect::from_sides_option(10, 20, 0, 40);
175 /// assert_eq!(rect, None);
176 /// ```
177 #[inline]
178 pub fn from_sides_option(left: i64, bot: i64, right: i64, top: i64) -> Option<Self> {
179 if left > right || bot > top {
180 None
181 } else {
182 Some(Self::from_sides(left, bot, right, top))
183 }
184 }
185
186 /// Creates a zero-area empty rectangle containing the given `(x, y)` coordinates.
187 ///
188 /// # Example
189 ///
190 /// ```
191 /// # use geometry::prelude::*;
192 /// let rect = Rect::from_xy(25, 60);
193 /// assert_eq!(rect.top(), 60);
194 /// assert_eq!(rect.bot(), 60);
195 /// assert_eq!(rect.left(), 25);
196 /// assert_eq!(rect.right(), 25);
197 /// ```
198 pub const fn from_xy(x: i64, y: i64) -> Self {
199 let p = Point::new(x, y);
200 Self::from_point(p)
201 }
202
203 /// Creates a new rectangle from the given opposite corner points.
204 ///
205 /// # Examples
206 ///
207 /// Create a rectangle from the lower left and upper right corners:
208 ///
209 /// ```
210 /// # use geometry::prelude::*;
211 /// let rect = Rect::new(Point::new(15, 20), Point::new(30, 40));
212 /// assert_eq!(rect.left(), 15);
213 /// assert_eq!(rect.bot(), 20);
214 /// assert_eq!(rect.right(), 30);
215 /// assert_eq!(rect.top(), 40);
216 /// ```
217 ///
218 /// Create a rectangle from the lower right and upper left corners:
219 ///
220 /// ```
221 /// # use geometry::prelude::*;
222 /// let rect = Rect::new(Point::new(30, 20), Point::new(15, 40));
223 /// assert_eq!(rect.left(), 15);
224 /// assert_eq!(rect.bot(), 20);
225 /// assert_eq!(rect.right(), 30);
226 /// assert_eq!(rect.top(), 40);
227 /// ```
228 #[inline]
229 pub fn new(lower_left: Point, upper_right: Point) -> Self {
230 let p0 = lower_left;
231 let p1 = upper_right;
232 Self {
233 p0: Point::new(p0.x.min(p1.x), p0.y.min(p1.y)),
234 p1: Point::new(p0.x.max(p1.x), p0.y.max(p1.y)),
235 }
236 }
237
238 /// Creates a new rectangle from a lower-left corner and an upper-right corner,
239 /// without checking that coordinates are ordered correctly.
240 ///
241 /// # Safety
242 ///
243 /// The caller must ensure that `p0` is the lower-left corner and that
244 /// `p1` is the upper-right corner. In other words, you must ensure that
245 /// `p0.x <= p1.x` and `p0.y <= p1.y`.
246 ///
247 #[inline]
248 pub const unsafe fn new_unchecked(p0: Point, p1: Point) -> Self {
249 Self { p0, p1 }
250 }
251
252 /// Creates a rectangle from horizontal and vertical [`Span`]s.
253 ///
254 /// # Example
255 ///
256 /// ```
257 /// # use geometry::prelude::*;
258 /// let hspan = Span::new(15, 30);
259 /// let vspan = Span::new(20, 40);
260 /// let rect = Rect::from_spans(hspan, vspan);
261 /// assert_eq!(rect.left(), 15);
262 /// assert_eq!(rect.bot(), 20);
263 /// assert_eq!(rect.right(), 30);
264 /// assert_eq!(rect.top(), 40);
265 /// ```
266 pub const fn from_spans(h: Span, v: Span) -> Self {
267 Self {
268 p0: Point::new(h.start(), v.start()),
269 p1: Point::new(h.stop(), v.stop()),
270 }
271 }
272
273 /// Returns the bottom y-coordinate of the rectangle.
274 ///
275 /// # Example
276 ///
277 /// ```
278 /// # use geometry::prelude::*;
279 /// let rect = Rect::from_sides(10, 20, 30, 40);
280 /// assert_eq!(rect.bot(), 20);
281 /// ```
282 #[inline]
283 pub const fn bot(&self) -> i64 {
284 self.p0.y
285 }
286
287 /// Returns the top y-coordinate of the rectangle.
288 ///
289 /// # Example
290 ///
291 /// ```
292 /// # use geometry::prelude::*;
293 /// let rect = Rect::from_sides(10, 20, 30, 40);
294 /// assert_eq!(rect.top(), 40);
295 /// ```
296 #[inline]
297 pub const fn top(&self) -> i64 {
298 self.p1.y
299 }
300
301 /// Returns the left x-coordinate of the rectangle.
302 ///
303 /// # Example
304 ///
305 /// ```
306 /// # use geometry::prelude::*;
307 /// let rect = Rect::from_sides(10, 20, 30, 40);
308 /// assert_eq!(rect.left(), 10);
309 /// ```
310 #[inline]
311 pub const fn left(&self) -> i64 {
312 self.p0.x
313 }
314
315 /// Returns the right x-coordinate of the rectangle.
316 ///
317 /// # Example
318 ///
319 /// ```
320 /// # use geometry::prelude::*;
321 /// let rect = Rect::from_sides(10, 20, 30, 40);
322 /// assert_eq!(rect.right(), 30);
323 /// ```
324 #[inline]
325 pub const fn right(&self) -> i64 {
326 self.p1.x
327 }
328
329 /// Returns the horizontal [`Span`] of the rectangle.
330 ///
331 /// # Example
332 ///
333 /// ```
334 /// # use geometry::prelude::*;
335 /// let rect = Rect::from_sides(10, 20, 30, 40);
336 /// assert_eq!(rect.hspan(), Span::new(10, 30));
337 /// ```
338 pub const fn hspan(&self) -> Span {
339 unsafe {
340 // SAFETY: A valid Rect has p0.x <= p1.x
341 Span::new_unchecked(self.p0.x, self.p1.x)
342 }
343 }
344
345 /// Returns the vertical span of the rectangle.
346 ///
347 /// # Example
348 ///
349 /// ```
350 /// # use geometry::prelude::*;
351 /// let rect = Rect::from_sides(10, 20, 30, 40);
352 /// assert_eq!(rect.vspan(), Span::new(20, 40));
353 /// ```
354 pub const fn vspan(&self) -> Span {
355 unsafe {
356 // SAFETY: A valid Rect has p0.y <= p1.y
357 Span::new_unchecked(self.p0.y, self.p1.y)
358 }
359 }
360
361 /// Returns a new [`Rect`] with the given `hspan` and the same vertical span.
362 ///
363 /// # Example
364 ///
365 /// ```
366 /// # use geometry::prelude::*;
367 /// let rect = Rect::from_sides(10, 20, 30, 40);
368 /// let new_hspan = Span::new(100, 200);
369 /// let new_rect = rect.with_hspan(new_hspan);
370 /// assert_eq!(new_rect, Rect::from_sides(100, 20, 200, 40));
371 /// ```
372 pub fn with_hspan(self, hspan: Span) -> Self {
373 Rect::new(
374 Point::new(hspan.start(), self.p0.y),
375 Point::new(hspan.stop(), self.p1.y),
376 )
377 }
378
379 /// Returns a [`Rect`] with the given `vspan` and the same horizontal span.
380 ///
381 /// # Example
382 ///
383 /// ```
384 /// # use geometry::prelude::*;
385 /// let rect = Rect::from_sides(10, 20, 30, 40);
386 /// let new_vspan = Span::new(100, 200);
387 /// let new_rect = rect.with_vspan(new_vspan);
388 /// assert_eq!(new_rect, Rect::from_sides(10, 100, 30, 200));
389 /// ```
390 pub fn with_vspan(self, vspan: Span) -> Self {
391 Rect::new(
392 Point::new(self.p0.x, vspan.start()),
393 Point::new(self.p1.x, vspan.stop()),
394 )
395 }
396
397 /// Returns a [`Rect`] with the given `span` in the given `dir`, and the current span in the
398 /// other direction.
399 ///
400 /// # Example
401 ///
402 /// ```
403 /// # use geometry::prelude::*;
404 /// let rect = Rect::from_sides(10, 20, 30, 40);
405 /// let new_vspan = Span::new(100, 200);
406 /// let new_rect = rect.with_span(new_vspan, Dir::Vert);
407 /// assert_eq!(new_rect, Rect::from_sides(10, 100, 30, 200));
408 /// ```
409 pub fn with_span(self, span: Span, dir: Dir) -> Self {
410 match dir {
411 Dir::Vert => self.with_vspan(span),
412 Dir::Horiz => self.with_hspan(span),
413 }
414 }
415
416 /// Returns the horizontal width of the rectangle.
417 ///
418 /// # Example
419 ///
420 /// ```
421 /// # use geometry::prelude::*;
422 /// let rect = Rect::from_sides(10, 20, 30, 50);
423 /// assert_eq!(rect.width(), 20);
424 /// ```
425 #[inline]
426 pub const fn width(&self) -> i64 {
427 self.hspan().length()
428 }
429
430 /// Returns the vertical height of the rectangle.
431 ///
432 /// # Example
433 ///
434 /// ```
435 /// # use geometry::prelude::*;
436 /// let rect = Rect::from_sides(10, 20, 30, 50);
437 /// assert_eq!(rect.height(), 30);
438 /// ```
439 #[inline]
440 pub const fn height(&self) -> i64 {
441 self.vspan().length()
442 }
443
444 /// Returns the area of the rectangle.
445 ///
446 /// # Example
447 ///
448 /// ```
449 /// # use geometry::prelude::*;
450 /// let rect = Rect::from_sides(10, 20, 30, 50);
451 /// assert_eq!(rect.area(), 600);
452 /// ```
453 #[inline]
454 pub const fn area(&self) -> i64 {
455 self.width() * self.height()
456 }
457
458 /// Returns the lowest/leftmost coordinate of the rectangle in the given direction.
459 ///
460 /// For [`Dir::Horiz`], this is the left edge's x-coordinate.
461 /// For [`Dir::Vert`], this is the bottom edge's y-coordinate.
462 ///
463 /// # Example
464 ///
465 /// ```
466 /// # use geometry::prelude::*;
467 /// let rect = Rect::from_sides(10, 20, 30, 50);
468 /// assert_eq!(rect.lower_coord(Dir::Vert), 20);
469 /// assert_eq!(rect.lower_coord(Dir::Horiz), 10);
470 /// ```
471 pub const fn lower_coord(&self, dir: Dir) -> i64 {
472 self.span(dir).start()
473 }
474
475 /// Returns the highest/rightmost coordinate of the rectangle in the given direction.
476 ///
477 /// For [`Dir::Horiz`], this is the right edge's x-coordinate.
478 /// For [`Dir::Vert`], this is the top edge's y-coordinate.
479 ///
480 /// # Example
481 ///
482 /// ```
483 /// # use geometry::prelude::*;
484 /// let rect = Rect::from_sides(10, 20, 30, 50);
485 /// assert_eq!(rect.upper_coord(Dir::Vert), 50);
486 /// assert_eq!(rect.upper_coord(Dir::Horiz), 30);
487 /// ```
488 #[inline]
489 pub const fn upper_coord(&self, dir: Dir) -> i64 {
490 self.span(dir).stop()
491 }
492
493 /// Returns the span of the rectangle in the given direction.
494 ///
495 /// # Example
496 ///
497 /// ```
498 /// # use geometry::prelude::*;
499 /// let rect = Rect::from_sides(10, 20, 30, 50);
500 /// assert_eq!(rect.span(Dir::Vert), Span::new(20, 50));
501 /// assert_eq!(rect.span(Dir::Horiz), Span::new(10, 30));
502 /// ```
503 pub const fn span(&self, dir: Dir) -> Span {
504 match dir {
505 Dir::Horiz => self.hspan(),
506 Dir::Vert => self.vspan(),
507 }
508 }
509
510 /// Returns the edges of two rectangles along the given `dir` in increasing order.
511 ///
512 /// For [`Dir::Horiz`], returns the sorted x-coordinates of all **vertical** edges.
513 /// For [`Dir::Vert`], returns the sorted y-coordinates of all **horizontal** edges.
514 fn sorted_coords(&self, other: Self, dir: Dir) -> [i64; 4] {
515 let mut edges = [
516 self.lower_coord(dir),
517 self.upper_coord(dir),
518 other.lower_coord(dir),
519 other.upper_coord(dir),
520 ];
521 edges.sort();
522 edges
523 }
524
525 /// Returns the span between the inner two edges of two rectangles along the given direction.
526 ///
527 /// # Example
528 ///
529 /// ```
530 /// # use geometry::prelude::*;
531 /// let r1 = Rect::from_sides(10, 25, 30, 50);
532 /// let r2 = Rect::from_sides(20, 15, 70, 35);
533 /// assert_eq!(r1.inner_span(r2, Dir::Horiz), Span::new(20, 30));
534 /// assert_eq!(r1.inner_span(r2, Dir::Vert), Span::new(25, 35));
535 /// ```
536 ///
537 /// The "order" of `r1` and `r2` does not matter:
538 ///
539 /// ```
540 /// # use geometry::prelude::*;
541 /// let r1 = Rect::from_sides(10, 25, 30, 50);
542 /// let r2 = Rect::from_sides(20, 15, 70, 35);
543 /// assert_eq!(r2.inner_span(r1, Dir::Horiz), Span::new(20, 30));
544 /// assert_eq!(r2.inner_span(r1, Dir::Vert), Span::new(25, 35));
545 /// ```
546 #[inline]
547 pub fn inner_span(&self, other: Self, dir: Dir) -> Span {
548 let edges = self.sorted_coords(other, dir);
549 unsafe {
550 // SAFETY: sorted_coords returns edges in sorted order,
551 // so edges[1] <= edges[2].
552 Span::new_unchecked(edges[1], edges[2])
553 }
554 }
555
556 /// Returns the span between the outer two edges of two rectangles along the given direction.
557 ///
558 /// # Example
559 ///
560 /// ```
561 /// # use geometry::prelude::*;
562 /// let r1 = Rect::from_sides(10, 25, 30, 50);
563 /// let r2 = Rect::from_sides(20, 15, 70, 35);
564 /// assert_eq!(r1.outer_span(r2, Dir::Horiz), Span::new(10, 70));
565 /// assert_eq!(r1.outer_span(r2, Dir::Vert), Span::new(15, 50));
566 /// ```
567 ///
568 /// The "order" of `r1` and `r2` does not matter:
569 ///
570 /// ```
571 /// # use geometry::prelude::*;
572 /// let r1 = Rect::from_sides(10, 25, 30, 50);
573 /// let r2 = Rect::from_sides(20, 15, 70, 35);
574 /// assert_eq!(r2.outer_span(r1, Dir::Horiz), Span::new(10, 70));
575 /// assert_eq!(r2.outer_span(r1, Dir::Vert), Span::new(15, 50));
576 /// ```
577 #[inline]
578 pub fn outer_span(&self, other: Self, dir: Dir) -> Span {
579 let edges = self.sorted_coords(other, dir);
580 unsafe {
581 // SAFETY: sorted_coords returns edges in sorted order,
582 // so edges[0] <= edges[3].
583 Span::new_unchecked(edges[0], edges[3])
584 }
585 }
586
587 /// Returns the edge of a rectangle closest to the coordinate `x` along a given direction.
588 ///
589 /// # Example
590 ///
591 /// ```
592 /// # use geometry::prelude::*;
593 /// let rect = Rect::from_sides(10, 25, 30, 50);
594 /// assert_eq!(rect.edge_closer_to(14, Dir::Horiz), 10);
595 /// assert_eq!(rect.edge_closer_to(22, Dir::Horiz), 30);
596 /// assert_eq!(rect.edge_closer_to(23, Dir::Vert), 25);
597 /// assert_eq!(rect.edge_closer_to(37, Dir::Vert), 25);
598 /// assert_eq!(rect.edge_closer_to(38, Dir::Vert), 50);
599 /// assert_eq!(rect.edge_closer_to(59, Dir::Vert), 50);
600 /// ```
601 pub fn edge_closer_to(&self, x: i64, dir: Dir) -> i64 {
602 let (x0, x1) = self.span(dir).into();
603 if (x - x0).abs() <= (x - x1).abs() {
604 x0
605 } else {
606 x1
607 }
608 }
609
610 /// Returns the edge of a rectangle farthest from the coordinate `x` along a given direction.
611 ///
612 /// # Example
613 ///
614 /// ```
615 /// # use geometry::prelude::*;
616 /// let rect = Rect::from_sides(10, 25, 30, 50);
617 /// assert_eq!(rect.edge_farther_from(14, Dir::Horiz), 30);
618 /// assert_eq!(rect.edge_farther_from(22, Dir::Horiz), 10);
619 /// assert_eq!(rect.edge_farther_from(23, Dir::Vert), 50);
620 /// assert_eq!(rect.edge_farther_from(37, Dir::Vert), 50);
621 /// assert_eq!(rect.edge_farther_from(38, Dir::Vert), 25);
622 /// assert_eq!(rect.edge_farther_from(59, Dir::Vert), 25);
623 /// ```
624 pub fn edge_farther_from(&self, x: i64, dir: Dir) -> i64 {
625 let (x0, x1) = self.span(dir).into();
626 if (x - x0).abs() <= (x - x1).abs() {
627 x1
628 } else {
629 x0
630 }
631 }
632
633 /// Creates a rectangle from two [`Span`]s, where the first is parallel to `dir`,
634 /// and the second is perpendicular.
635 ///
636 /// # Example
637 ///
638 /// ```
639 /// # use geometry::prelude::*;
640 /// let span1 = Span::new(10, 30);
641 /// let span2 = Span::new(25, 50);
642 /// let rect = Rect::from_dir_spans(Dir::Horiz, span1, span2);
643 /// assert_eq!(rect, Rect::from_sides(10, 25, 30, 50));
644 /// let rect = Rect::from_dir_spans(Dir::Vert, span1, span2);
645 /// assert_eq!(rect, Rect::from_sides(25, 10, 50, 30));
646 /// ```
647 #[inline]
648 pub fn from_dir_spans(dir: Dir, parallel_span: Span, perp_span: Span) -> Self {
649 match dir {
650 Dir::Vert => Self::from_spans(perp_span, parallel_span),
651 Dir::Horiz => Self::from_spans(parallel_span, perp_span),
652 }
653 }
654
655 /// Returns the length of this rectangle in the given direction.
656 ///
657 /// # Example
658 ///
659 /// ```
660 /// # use geometry::prelude::*;
661 /// let rect = Rect::from_sides(0, 0, 200, 100);
662 /// assert_eq!(rect.length(Dir::Horiz), 200);
663 /// assert_eq!(rect.length(Dir::Vert), 100);
664 /// ```
665 ///
666 pub const fn length(&self, dir: Dir) -> i64 {
667 self.span(dir).length()
668 }
669
670 /// Returns the direction in which the rectangle is longer, choosing [`Dir::Horiz`] if the sides
671 /// are equal.
672 ///
673 /// # Example
674 ///
675 /// ```
676 /// # use geometry::prelude::*;
677 /// let rect = Rect::from_sides(0, 0, 100, 200);
678 /// assert_eq!(rect.longer_dir(), Dir::Vert);
679 /// let rect = Rect::from_sides(0, 0, 200, 100);
680 /// assert_eq!(rect.longer_dir(), Dir::Horiz);
681 /// let rect = Rect::from_sides(0, 0, 100, 100);
682 /// assert_eq!(rect.longer_dir(), Dir::Horiz);
683 /// ```
684 #[inline]
685 pub const fn longer_dir(&self) -> Dir {
686 if self.height() > self.width() {
687 Dir::Vert
688 } else {
689 Dir::Horiz
690 }
691 }
692
693 /// Returns the direction in which the rectangle is longer, returning [`None`] if the sides
694 /// are equal.
695 ///
696 /// # Example
697 ///
698 /// ```
699 /// # use geometry::prelude::*;
700 /// let rect = Rect::from_sides(0, 0, 100, 200);
701 /// assert_eq!(rect.longer_dir_strict(), Some(Dir::Vert));
702 /// let rect = Rect::from_sides(0, 0, 200, 100);
703 /// assert_eq!(rect.longer_dir_strict(), Some(Dir::Horiz));
704 /// let rect = Rect::from_sides(0, 0, 100, 100);
705 /// assert_eq!(rect.longer_dir_strict(), None);
706 /// ```
707 #[inline]
708 pub fn longer_dir_strict(&self) -> Option<Dir> {
709 use std::cmp::Ordering;
710 match self.height().cmp(&self.width()) {
711 Ordering::Less => Some(Dir::Horiz),
712 Ordering::Equal => None,
713 Ordering::Greater => Some(Dir::Vert),
714 }
715 }
716
717 /// Returns the direction in which the rectangle is shorter, choosing [`Dir::Vert`] if the sides
718 /// are equal.
719 ///
720 /// This always returns the opposite of [`Rect::longer_dir`].
721 ///
722 /// # Example
723 ///
724 /// ```
725 /// # use geometry::prelude::*;
726 /// let rect = Rect::from_sides(0, 0, 100, 200);
727 /// assert_eq!(rect.shorter_dir(), Dir::Horiz);
728 /// let rect = Rect::from_sides(0, 0, 200, 100);
729 /// assert_eq!(rect.shorter_dir(), Dir::Vert);
730 /// let rect = Rect::from_sides(0, 0, 100, 100);
731 /// assert_eq!(rect.shorter_dir(), Dir::Vert);
732 /// ```
733 #[inline]
734 pub const fn shorter_dir(&self) -> Dir {
735 self.longer_dir().other()
736 }
737
738 /// Returns the direction in which the rectangle is shorter, choosing [`None`] if the sides
739 /// are equal.
740 ///
741 /// This always returns the opposite of [`Rect::longer_dir_strict`].
742 ///
743 /// # Example
744 ///
745 /// ```
746 /// # use geometry::prelude::*;
747 /// let rect = Rect::from_sides(0, 0, 100, 200);
748 /// assert_eq!(rect.shorter_dir_strict(), Some(Dir::Horiz));
749 /// let rect = Rect::from_sides(0, 0, 200, 100);
750 /// assert_eq!(rect.shorter_dir_strict(), Some(Dir::Vert));
751 /// let rect = Rect::from_sides(0, 0, 100, 100);
752 /// assert_eq!(rect.shorter_dir_strict(), None);
753 /// ```
754 #[inline]
755 pub fn shorter_dir_strict(&self) -> Option<Dir> {
756 self.longer_dir_strict().as_ref().map(Dir::other)
757 }
758
759 /// Computes the rectangular union of this `Rect` with another `Rect`.
760 ///
761 /// # Example
762 ///
763 /// ```
764 /// # use geometry::prelude::*;
765 /// let r1 = Rect::from_sides(0, 0, 100, 200);
766 /// let r2 = Rect::from_sides(-50, 20, 120, 160);
767 /// assert_eq!(r1.union(r2), Rect::from_sides(-50, 0, 120, 200));
768 /// ```
769 pub fn union(self, other: Self) -> Self {
770 Rect::new(
771 Point::new(self.p0.x.min(other.p0.x), self.p0.y.min(other.p0.y)),
772 Point::new(self.p1.x.max(other.p1.x), self.p1.y.max(other.p1.y)),
773 )
774 }
775
776 /// Calculates the rectangular union of all rectangles provided.
777 ///
778 /// # Example
779 ///
780 /// ```
781 /// # use geometry::prelude::*;
782 /// let rects = vec![
783 /// Rect::from_sides(10, 20, 30, 40),
784 /// Rect::from_sides(-10, 25, 20, 35),
785 /// Rect::from_sides(15, 20, 25, 60),
786 /// ];
787 /// assert_eq!(Rect::union_all(rects.into_iter()), Rect::from_sides(-10, 20, 30, 60));
788 /// ```
789 ///
790 /// # Panics
791 ///
792 /// This function panics if the provided iterator has no elements.
793 /// If your iterator may be empty, consider using [`Rect::union_all_option`].
794 pub fn union_all<T>(rects: impl Iterator<Item = T>) -> Self
795 where
796 T: Into<Self>,
797 {
798 rects
799 .fold(None, |acc: Option<Rect>, r| match acc {
800 Some(acc) => Some(acc.union(r.into())),
801 None => Some(r.into()),
802 })
803 .unwrap()
804 }
805
806 /// Calculates the rectangular union of all `Option<Rect>`s provided.
807 ///
808 /// All `None` elements in the iterator are ignored.
809 /// If the iterator has no `Some(_)` elements, this function returns [`None`].
810 ///
811 /// # Example
812 ///
813 /// ```
814 /// # use geometry::prelude::*;
815 /// let rects = vec![
816 /// Some(Rect::from_sides(10, 20, 30, 40)),
817 /// Some(Rect::from_sides(-10, 25, 20, 35)),
818 /// None,
819 /// Some(Rect::from_sides(15, 20, 25, 60)),
820 /// ];
821 /// assert_eq!(Rect::union_all_option(rects.into_iter()), Some(Rect::from_sides(-10, 20, 30, 60)));
822 /// ```
823 pub fn union_all_option<T>(rects: impl Iterator<Item = T>) -> Option<Self>
824 where
825 T: Into<Option<Self>>,
826 {
827 rects
828 .filter_map(|r| r.into())
829 .fold(None, |acc, r| match acc {
830 Some(acc) => Some(acc.union(r)),
831 None => Some(r),
832 })
833 }
834
835 /// Computes the rectangular intersection of this `Rect` with another `Rect`.
836 ///
837 /// Returns `None` if the intersection is empty.
838 ///
839 /// # Example
840 ///
841 /// ```
842 /// # use geometry::prelude::*;
843 /// let r1 = Rect::from_sides(0, 0, 100, 200);
844 /// let r2 = Rect::from_sides(-50, 20, 120, 160);
845 /// assert_eq!(r1.intersection(r2), Some(Rect::from_sides(0, 20, 100, 160)));
846 ///
847 /// let r1 = Rect::from_sides(0, 0, 100, 200);
848 /// let r2 = Rect::from_sides(120, -60, 240, 800);
849 /// assert_eq!(r1.intersection(r2), None);
850 /// ```
851 pub fn intersection(self, other: Self) -> Option<Self> {
852 let pmin = Point::new(self.p0.x.max(other.p0.x), self.p0.y.max(other.p0.y));
853 let pmax = Point::new(self.p1.x.min(other.p1.x), self.p1.y.min(other.p1.y));
854
855 // Check for empty intersection, and return None
856 if pmin.x > pmax.x || pmin.y > pmax.y {
857 return None;
858 }
859
860 // Otherwise, return the intersection
861 Some(Rect::new(pmin, pmax))
862 }
863
864 /// Expands the rectangle by `amount` on all sides.
865 ///
866 /// # Example
867 ///
868 /// ```
869 /// # use geometry::prelude::*;
870 /// let rect = Rect::from_sides(0, 0, 100, 200);
871 /// assert_eq!(rect.expand_all(20), Rect::from_sides(-20, -20, 120, 220));
872 /// ```
873 #[inline]
874 pub fn expand_all(&self, amount: i64) -> Self {
875 Self::new(
876 Point::new(self.p0.x - amount, self.p0.y - amount),
877 Point::new(self.p1.x + amount, self.p1.y + amount),
878 )
879 }
880
881 /// Expands the rectangle by `amount` on both sides associated with the direction `dir`.
882 ///
883 /// # Example
884 ///
885 /// ```
886 /// # use geometry::prelude::*;
887 /// let rect = Rect::from_sides(0, 0, 100, 200);
888 /// assert_eq!(rect.expand_dir(Dir::Horiz, 20), Rect::from_sides(-20, 0, 120, 200));
889 /// assert_eq!(rect.expand_dir(Dir::Vert, 20), Rect::from_sides(0, -20, 100, 220));
890 /// ```
891 #[inline]
892 pub fn expand_dir(&self, dir: Dir, amount: i64) -> Self {
893 match dir {
894 Dir::Horiz => Self::new(
895 Point::new(self.p0.x - amount, self.p0.y),
896 Point::new(self.p1.x + amount, self.p1.y),
897 ),
898 Dir::Vert => Self::new(
899 Point::new(self.p0.x, self.p0.y - amount),
900 Point::new(self.p1.x, self.p1.y + amount),
901 ),
902 }
903 }
904
905 /// Expands the rectangle by `amount` on the given side.
906 ///
907 /// # Example
908 ///
909 /// ```
910 /// # use geometry::prelude::*;
911 /// let rect = Rect::from_sides(0, 0, 100, 200);
912 /// assert_eq!(rect.expand_side(Side::Top, 20), Rect::from_sides(0, 0, 100, 220));
913 /// assert_eq!(rect.expand_side(Side::Bot, 20), Rect::from_sides(0, -20, 100, 200));
914 /// assert_eq!(rect.expand_side(Side::Left, 20), Rect::from_sides(-20, 0, 100, 200));
915 /// assert_eq!(rect.expand_side(Side::Right, 20), Rect::from_sides(0, 0, 120, 200));
916 /// ```
917 #[inline]
918 pub fn expand_side(&self, side: Side, amount: i64) -> Self {
919 match side {
920 Side::Top => Self::new(
921 Point::new(self.p0.x, self.p0.y),
922 Point::new(self.p1.x, self.p1.y + amount),
923 ),
924 Side::Bot => Self::new(
925 Point::new(self.p0.x, self.p0.y - amount),
926 Point::new(self.p1.x, self.p1.y),
927 ),
928 Side::Right => Self::new(
929 Point::new(self.p0.x, self.p0.y),
930 Point::new(self.p1.x + amount, self.p1.y),
931 ),
932 Side::Left => Self::new(
933 Point::new(self.p0.x - amount, self.p0.y),
934 Point::new(self.p1.x, self.p1.y),
935 ),
936 }
937 }
938
939 /// Expands the rectangle by `amount` at the given corner.
940 ///
941 /// # Example
942 ///
943 /// ```
944 /// # use geometry::prelude::*;
945 /// let rect = Rect::from_sides(0, 0, 100, 200);
946 /// assert_eq!(rect.expand_corner(Corner::LowerLeft, 20), Rect::from_sides(-20, -20, 100, 200));
947 /// assert_eq!(rect.expand_corner(Corner::LowerRight, 20), Rect::from_sides(0, -20, 120, 200));
948 /// assert_eq!(rect.expand_corner(Corner::UpperLeft, 20), Rect::from_sides(-20, 0, 100, 220));
949 /// assert_eq!(rect.expand_corner(Corner::UpperRight, 20), Rect::from_sides(0, 0, 120, 220));
950 /// ```
951 #[inline]
952 pub fn expand_corner(self, corner: Corner, amount: i64) -> Self {
953 match corner {
954 Corner::LowerLeft => {
955 Self::from_sides(self.p0.x - amount, self.p0.y - amount, self.p1.x, self.p1.y)
956 }
957 Corner::LowerRight => {
958 Self::from_sides(self.p0.x, self.p0.y - amount, self.p1.x + amount, self.p1.y)
959 }
960 Corner::UpperLeft => {
961 Self::from_sides(self.p0.x - amount, self.p0.y, self.p1.x, self.p1.y + amount)
962 }
963 Corner::UpperRight => {
964 Self::from_sides(self.p0.x, self.p0.y, self.p1.x + amount, self.p1.y + amount)
965 }
966 }
967 }
968
969 /// Expands the rectangle by some (possibly different) amount on each side.
970 ///
971 /// # Example
972 ///
973 /// ```
974 /// # use geometry::prelude::*;
975 /// let rect = Rect::from_sides(0, 0, 100, 200);
976 /// let sides = Sides::new(10, 20, 30, 40);
977 /// assert_eq!(rect.expand_sides(sides), Rect::from_sides(-10, -20, 130, 240));
978 /// ```
979 pub fn expand_sides(&self, sides: Sides<i64>) -> Self {
980 Self::from_sides(
981 self.p0.x - sides[Side::Left],
982 self.p0.y - sides[Side::Bot],
983 self.p1.x + sides[Side::Right],
984 self.p1.y + sides[Side::Top],
985 )
986 }
987
988 /// Shrinks the rectangle by `amount` on all sides.
989 ///
990 /// Returns [`None`] if shrinking would make the rectangle invalid.
991 ///
992 /// # Example
993 ///
994 /// ```
995 /// # use geometry::prelude::*;
996 /// let rect = Rect::from_sides(0, 0, 100, 200);
997 /// assert_eq!(rect.shrink_all(20), Some(Rect::from_sides(20, 20, 80, 180)));
998 /// assert_eq!(rect.shrink_all(105), None);
999 /// ```
1000 #[inline]
1001 pub fn shrink_all(&self, amount: i64) -> Option<Self> {
1002 Self::from_sides_option(
1003 self.p0.x + amount,
1004 self.p0.y + amount,
1005 self.p1.x - amount,
1006 self.p1.y - amount,
1007 )
1008 }
1009
1010 /// Shrinks the rectangle by `amount` on both sides associated with the direction `dir`.
1011 ///
1012 /// Returns [`None`] if shrinking would make the rectangle invalid.
1013 ///
1014 /// # Example
1015 ///
1016 /// ```
1017 /// # use geometry::prelude::*;
1018 /// let rect = Rect::from_sides(0, 0, 100, 200);
1019 /// assert_eq!(rect.shrink_dir(Dir::Horiz, 20), Some(Rect::from_sides(20, 0, 80, 200)));
1020 /// assert_eq!(rect.shrink_dir(Dir::Vert, 20), Some(Rect::from_sides(0, 20, 100, 180)));
1021 /// assert_eq!(rect.shrink_dir(Dir::Vert, 120), None);
1022 /// ```
1023 #[inline]
1024 pub fn shrink_dir(&self, dir: Dir, amount: i64) -> Option<Self> {
1025 match dir {
1026 Dir::Horiz => Self::from_sides_option(
1027 self.p0.x + amount,
1028 self.p0.y,
1029 self.p1.x - amount,
1030 self.p1.y,
1031 ),
1032 Dir::Vert => Self::from_sides_option(
1033 self.p0.x,
1034 self.p0.y + amount,
1035 self.p1.x,
1036 self.p1.y - amount,
1037 ),
1038 }
1039 }
1040
1041 /// Shrinks the rectangle by `amount` on the given side.
1042 ///
1043 /// Returns [`None`] if shrinking would make the rectangle invalid.
1044 ///
1045 /// # Example
1046 ///
1047 /// ```
1048 /// # use geometry::prelude::*;
1049 /// let rect = Rect::from_sides(0, 0, 100, 200);
1050 /// assert_eq!(rect.shrink_side(Side::Top, 20), Some(Rect::from_sides(0, 0, 100, 180)));
1051 /// assert_eq!(rect.shrink_side(Side::Bot, 20), Some(Rect::from_sides(0, 20, 100, 200)));
1052 /// assert_eq!(rect.shrink_side(Side::Left, 20), Some(Rect::from_sides(20, 0, 100, 200)));
1053 /// assert_eq!(rect.shrink_side(Side::Right, 20), Some(Rect::from_sides(0, 0, 80, 200)));
1054 /// assert_eq!(rect.shrink_side(Side::Right, 210), None);
1055 /// ```
1056 #[inline]
1057 pub fn shrink_side(&self, side: Side, amount: i64) -> Option<Self> {
1058 match side {
1059 Side::Top => {
1060 Self::from_sides_option(self.p0.x, self.p0.y, self.p1.x, self.p1.y - amount)
1061 }
1062 Side::Bot => {
1063 Self::from_sides_option(self.p0.x, self.p0.y + amount, self.p1.x, self.p1.y)
1064 }
1065 Side::Right => {
1066 Self::from_sides_option(self.p0.x, self.p0.y, self.p1.x - amount, self.p1.y)
1067 }
1068 Side::Left => {
1069 Self::from_sides_option(self.p0.x + amount, self.p0.y, self.p1.x, self.p1.y)
1070 }
1071 }
1072 }
1073
1074 /// Shrinks the rectangle by some (possibly different) amount on each side.
1075 ///
1076 /// Returns [`None`] if shrinking would make the rectangle invalid.
1077 ///
1078 /// # Example
1079 ///
1080 /// ```
1081 /// # use geometry::prelude::*;
1082 /// let rect = Rect::from_sides(0, 0, 100, 200);
1083 /// let sides = Sides::new(10, 20, 30, 40);
1084 /// assert_eq!(rect.shrink_sides(sides), Some(Rect::from_sides(10, 20, 70, 160)));
1085 /// ```
1086 pub fn shrink_sides(&self, sides: Sides<i64>) -> Option<Self> {
1087 Self::from_sides_option(
1088 self.p0.x + sides[Side::Left],
1089 self.p0.y + sides[Side::Bot],
1090 self.p1.x - sides[Side::Right],
1091 self.p1.y - sides[Side::Top],
1092 )
1093 }
1094
1095 /// Shrinks the rectangle by `amount` at the given corner.
1096 ///
1097 /// Returns [`None`] if shrinking would make the rectangle invalid.
1098 ///
1099 /// # Example
1100 ///
1101 /// ```
1102 /// # use geometry::prelude::*;
1103 /// let rect = Rect::from_sides(0, 0, 100, 200);
1104 /// assert_eq!(rect.shrink_corner(Corner::LowerLeft, 20), Some(Rect::from_sides(20, 20, 100, 200)));
1105 /// assert_eq!(rect.shrink_corner(Corner::LowerRight, 20), Some(Rect::from_sides(0, 20, 80, 200)));
1106 /// assert_eq!(rect.shrink_corner(Corner::UpperLeft, 20), Some(Rect::from_sides(20, 0, 100, 180)));
1107 /// assert_eq!(rect.shrink_corner(Corner::UpperRight, 20), Some(Rect::from_sides(0, 0, 80, 180)));
1108 /// assert_eq!(rect.shrink_corner(Corner::UpperRight, 110), None);
1109 /// ```
1110 #[inline]
1111 pub fn shrink_corner(self, corner: Corner, amount: i64) -> Option<Self> {
1112 match corner {
1113 Corner::LowerLeft => Self::from_sides_option(
1114 self.p0.x + amount,
1115 self.p0.y + amount,
1116 self.p1.x,
1117 self.p1.y,
1118 ),
1119 Corner::LowerRight => Self::from_sides_option(
1120 self.p0.x,
1121 self.p0.y + amount,
1122 self.p1.x - amount,
1123 self.p1.y,
1124 ),
1125 Corner::UpperLeft => Self::from_sides_option(
1126 self.p0.x + amount,
1127 self.p0.y,
1128 self.p1.x,
1129 self.p1.y - amount,
1130 ),
1131 Corner::UpperRight => Self::from_sides_option(
1132 self.p0.x,
1133 self.p0.y,
1134 self.p1.x - amount,
1135 self.p1.y - amount,
1136 ),
1137 }
1138 }
1139
1140 /// Returns the dimensions of the rectangle.
1141 ///
1142 /// # Example
1143 ///
1144 /// ```
1145 /// # use geometry::prelude::*;
1146 /// let rect = Rect::from_sides(20, 20, 100, 200);
1147 /// assert_eq!(rect.dims(), Dims::new(80, 180));
1148 /// ```
1149 #[inline]
1150 pub fn dims(&self) -> Dims {
1151 Dims::new(self.width(), self.height())
1152 }
1153
1154 /// The lower left corner of the rectangle.
1155 ///
1156 /// # Example
1157 ///
1158 /// ```
1159 /// # use geometry::prelude::*;
1160 /// let rect = Rect::from_sides(20, 20, 100, 200);
1161 /// assert_eq!(rect.lower_left(), Point::new(20, 20));
1162 /// ```
1163 #[inline]
1164 pub fn lower_left(&self) -> Point {
1165 self.corner(Corner::LowerLeft)
1166 }
1167
1168 /// The lower right corner of the rectangle.
1169 ///
1170 /// # Example
1171 ///
1172 /// ```
1173 /// # use geometry::prelude::*;
1174 /// let rect = Rect::from_sides(20, 20, 100, 200);
1175 /// assert_eq!(rect.lower_right(), Point::new(100, 20));
1176 /// ```
1177 #[inline]
1178 pub fn lower_right(&self) -> Point {
1179 self.corner(Corner::LowerRight)
1180 }
1181
1182 /// The upper left corner of the rectangle.
1183 ///
1184 /// # Example
1185 ///
1186 /// ```
1187 /// # use geometry::prelude::*;
1188 /// let rect = Rect::from_sides(20, 20, 100, 200);
1189 /// assert_eq!(rect.upper_left(), Point::new(20, 200));
1190 /// ```
1191 #[inline]
1192 pub fn upper_left(&self) -> Point {
1193 self.corner(Corner::UpperLeft)
1194 }
1195
1196 /// The upper right corner of the rectangle.
1197 ///
1198 /// # Example
1199 ///
1200 /// ```
1201 /// # use geometry::prelude::*;
1202 /// let rect = Rect::from_sides(20, 20, 100, 200);
1203 /// assert_eq!(rect.upper_right(), Point::new(100, 200));
1204 /// ```
1205 #[inline]
1206 pub fn upper_right(&self) -> Point {
1207 self.corner(Corner::UpperRight)
1208 }
1209
1210 /// Returns the desired corner of the rectangle.
1211 ///
1212 /// # Example
1213 ///
1214 /// ```
1215 /// # use geometry::prelude::*;
1216 /// let rect = Rect::from_sides(20, 20, 100, 200);
1217 /// assert_eq!(rect.corner(Corner::LowerLeft), Point::new(20, 20));
1218 /// assert_eq!(rect.corner(Corner::LowerRight), Point::new(100, 20));
1219 /// assert_eq!(rect.corner(Corner::UpperLeft), Point::new(20, 200));
1220 /// assert_eq!(rect.corner(Corner::UpperRight), Point::new(100, 200));
1221 /// ```
1222 pub fn corner(&self, corner: Corner) -> Point {
1223 match corner {
1224 Corner::LowerLeft => self.p0,
1225 Corner::LowerRight => Point::new(self.p1.x, self.p0.y),
1226 Corner::UpperLeft => Point::new(self.p0.x, self.p1.y),
1227 Corner::UpperRight => self.p1,
1228 }
1229 }
1230
1231 /// Returns the desired side of the rectangle.
1232 ///
1233 /// # Example
1234 ///
1235 /// ```
1236 /// # use geometry::prelude::*;
1237 /// let rect = Rect::from_sides(20, 20, 100, 200);
1238 /// assert_eq!(rect.side(Side::Bot), 20);
1239 /// assert_eq!(rect.side(Side::Left), 20);
1240 /// assert_eq!(rect.side(Side::Top), 200);
1241 /// assert_eq!(rect.side(Side::Right), 100);
1242 /// ```
1243 #[inline]
1244 pub fn side(&self, side: Side) -> i64 {
1245 match side {
1246 Side::Top => self.top(),
1247 Side::Bot => self.bot(),
1248 Side::Right => self.right(),
1249 Side::Left => self.left(),
1250 }
1251 }
1252
1253 /// Returns the desired edge of the rectangle.
1254 ///
1255 /// # Example
1256 ///
1257 /// ```
1258 /// # use geometry::prelude::*;
1259 /// let rect = Rect::from_sides(20, 20, 100, 200);
1260 /// assert_eq!(rect.edge(Side::Bot), Edge::new(Side::Bot, 20, Span::new(20, 100)));
1261 /// assert_eq!(rect.edge(Side::Top), Edge::new(Side::Top, 200, Span::new(20, 100)));
1262 /// assert_eq!(rect.edge(Side::Left), Edge::new(Side::Left, 20, Span::new(20, 200)));
1263 /// assert_eq!(rect.edge(Side::Right), Edge::new(Side::Right, 100, Span::new(20, 200)));
1264 /// ```
1265 #[inline]
1266 pub fn edge(&self, side: Side) -> Edge {
1267 Edge::new(side, self.side(side), self.span(side.edge_dir()))
1268 }
1269
1270 /// Snaps the corners of this rectangle to the given grid.
1271 ///
1272 /// Note that the rectangle may have zero area after snapping.
1273 ///
1274 /// # Example
1275 ///
1276 /// ```
1277 /// # use geometry::prelude::*;
1278 /// let rect = Rect::from_sides(17, 23, 101, 204);
1279 /// assert_eq!(rect.snap_to_grid(5), Rect::from_sides(15, 25, 100, 205));
1280 ///
1281 /// let rect = Rect::from_sides(16, 17, 101, 104);
1282 /// assert_eq!(rect.snap_to_grid(5), Rect::from_sides(15, 15, 100, 105));
1283 ///
1284 /// let rect = Rect::from_sides(16, 17, 17, 18);
1285 /// assert_eq!(rect.snap_to_grid(5), Rect::from_sides(15, 15, 15, 20));
1286 /// ```
1287 #[inline]
1288 pub fn snap_to_grid(&self, grid: i64) -> Self {
1289 Self::new(self.p0.snap_to_grid(grid), self.p1.snap_to_grid(grid))
1290 }
1291
1292 /// Based on `clip`, cuts a hole in this rectangle and returns the four surrounding pieces.
1293 ///
1294 /// Assumes that `clip` is entirely contained by this rectangle.
1295 ///
1296 /// # Example
1297 ///
1298 /// ```
1299 /// # use geometry::prelude::*;
1300 /// let rect = Rect::from_sides(0, 0, 100, 100);
1301 /// let clip = Rect::from_sides(20, 20, 80, 80);
1302 /// assert_eq!(rect.cutout(clip), [
1303 /// Rect::from_sides(0, 80, 100, 100),
1304 /// Rect::from_sides(0, 0, 100, 20),
1305 /// Rect::from_sides(0, 0, 20, 100),
1306 /// Rect::from_sides(80, 0, 100, 100),
1307 /// ]);
1308 pub fn cutout(&self, clip: Rect) -> [Rect; 4] {
1309 let src = *self;
1310 let t_span = Span::new(clip.top(), src.top());
1311 let b_span = Span::new(src.bot(), clip.bot());
1312 let l_span = Span::new(src.left(), clip.left());
1313 let r_span = Span::new(clip.right(), src.right());
1314
1315 [
1316 Rect::from_spans(src.hspan(), t_span),
1317 Rect::from_spans(src.hspan(), b_span),
1318 Rect::from_spans(l_span, src.vspan()),
1319 Rect::from_spans(r_span, src.vspan()),
1320 ]
1321 }
1322
1323 /// Returns whether the rectangle's center has integer coordinates.
1324 ///
1325 /// # Example
1326 ///
1327 /// ```
1328 /// # use geometry::prelude::*;
1329 /// let rect = Rect::from_sides(0, 0, 100, 100);
1330 /// assert!(rect.has_integer_center());
1331 ///
1332 /// let rect = Rect::from_sides(0, 0, 99, 100);
1333 /// assert!(!rect.has_integer_center());
1334 /// ```
1335 pub fn has_integer_center(&self) -> bool {
1336 self.vspan().has_integer_center() && self.hspan().has_integer_center()
1337 }
1338}
1339
1340impl Bbox for Rect {
1341 fn bbox(&self) -> Option<Rect> {
1342 Some(*self)
1343 }
1344}
1345
1346impl Intersect<Rect> for Rect {
1347 type Output = Self;
1348
1349 fn intersect(&self, bounds: &Rect) -> Option<Self::Output> {
1350 self.intersection(*bounds)
1351 }
1352}
1353
1354impl TranslateRef for Rect {
1355 #[inline]
1356 fn translate_ref(&self, p: Point) -> Self {
1357 self.translate(p)
1358 }
1359}
1360
1361impl TranslateMut for Rect {
1362 fn translate_mut(&mut self, p: Point) {
1363 self.p0.translate_mut(p);
1364 self.p1.translate_mut(p);
1365 }
1366}
1367
1368impl TransformRef for Rect {
1369 #[inline]
1370 fn transform_ref(&self, trans: Transformation) -> Self {
1371 self.transform(trans)
1372 }
1373}
1374
1375impl TransformMut for Rect {
1376 fn transform_mut(&mut self, trans: Transformation) {
1377 let (mut p0, mut p1) = (self.p0, self.p1);
1378 p0.transform_mut(trans);
1379 p1.transform_mut(trans);
1380
1381 self.p0 = Point::new(std::cmp::min(p0.x, p1.x), std::cmp::min(p0.y, p1.y));
1382 self.p1 = Point::new(std::cmp::max(p0.x, p1.x), std::cmp::max(p0.y, p1.y));
1383 }
1384}
1385
1386impl BoundingUnion<Rect> for Rect {
1387 type Output = Rect;
1388 fn bounding_union(&self, other: &Rect) -> Self::Output {
1389 self.union(*other)
1390 }
1391}
1392
1393impl From<Dims> for Rect {
1394 /// Converts the [`Dims`] to a [`Rect`] as described in [`Rect::from_dims`].
1395 #[inline]
1396 fn from(value: Dims) -> Self {
1397 Self::from_dims(value)
1398 }
1399}
1400
1401impl Contains<Point> for Rect {
1402 fn contains(&self, other: &Point) -> Containment {
1403 if other.x >= self.p0.x
1404 && other.x <= self.p1.x
1405 && other.y >= self.p0.y
1406 && other.y <= self.p1.y
1407 {
1408 Containment::Full
1409 } else {
1410 Containment::None
1411 }
1412 }
1413}
1414
1415#[cfg(test)]
1416mod tests {
1417 use crate::prelude::*;
1418
1419 #[test]
1420 fn sorted_coords_works() {
1421 let r1 = Rect::from_sides(10, 25, 30, 50);
1422 let r2 = Rect::from_sides(20, 15, 70, 35);
1423 assert_eq!(r1.sorted_coords(r2, Dir::Horiz), [10, 20, 30, 70]);
1424 assert_eq!(r1.sorted_coords(r2, Dir::Vert), [15, 25, 35, 50]);
1425 assert_eq!(r2.sorted_coords(r1, Dir::Horiz), [10, 20, 30, 70]);
1426 assert_eq!(r2.sorted_coords(r1, Dir::Vert), [15, 25, 35, 50]);
1427 }
1428}