1use std::fmt;
2
3use crate::{Div, Mul, NumExt as _, Pos2, Rangef, Rot2, Vec2, fast_midpoint, lerp, pos2, vec2};
4use core::ops::{BitOr, BitOrAssign};
5
6#[repr(C)]
22#[derive(Clone, Copy, Eq, PartialEq)]
23#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
24#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
25pub struct Rect {
26 pub min: Pos2,
28
29 pub max: Pos2,
31}
32
33impl Rect {
34 pub const EVERYTHING: Self = Self {
36 min: pos2(-f32::INFINITY, -f32::INFINITY),
37 max: pos2(f32::INFINITY, f32::INFINITY),
38 };
39
40 pub const NOTHING: Self = Self {
56 min: pos2(f32::INFINITY, f32::INFINITY),
57 max: pos2(-f32::INFINITY, -f32::INFINITY),
58 };
59
60 pub const NAN: Self = Self {
62 min: pos2(f32::NAN, f32::NAN),
63 max: pos2(f32::NAN, f32::NAN),
64 };
65
66 pub const ZERO: Self = Self {
68 min: Pos2::ZERO,
69 max: Pos2::ZERO,
70 };
71
72 #[inline(always)]
73 pub const fn from_min_max(min: Pos2, max: Pos2) -> Self {
74 Self { min, max }
75 }
76
77 #[inline(always)]
79 pub fn from_min_size(min: Pos2, size: Vec2) -> Self {
80 Self {
81 min,
82 max: min + size,
83 }
84 }
85
86 #[inline(always)]
87 pub fn from_center_size(center: Pos2, size: Vec2) -> Self {
88 Self {
89 min: center - size * 0.5,
90 max: center + size * 0.5,
91 }
92 }
93
94 #[inline(always)]
95 pub fn from_x_y_ranges(x_range: impl Into<Rangef>, y_range: impl Into<Rangef>) -> Self {
96 let x_range = x_range.into();
97 let y_range = y_range.into();
98 Self {
99 min: pos2(x_range.min, y_range.min),
100 max: pos2(x_range.max, y_range.max),
101 }
102 }
103
104 #[inline]
106 pub fn from_two_pos(a: Pos2, b: Pos2) -> Self {
107 Self {
108 min: pos2(a.x.min(b.x), a.y.min(b.y)),
109 max: pos2(a.x.max(b.x), a.y.max(b.y)),
110 }
111 }
112
113 #[inline]
115 pub fn from_pos(point: Pos2) -> Self {
116 Self {
117 min: point,
118 max: point,
119 }
120 }
121
122 pub fn from_points(points: &[Pos2]) -> Self {
124 let mut rect = Self::NOTHING;
125 for &p in points {
126 rect.extend_with(p);
127 }
128 rect
129 }
130
131 #[inline]
133 pub fn everything_right_of(left_x: f32) -> Self {
134 let mut rect = Self::EVERYTHING;
135 rect.set_left(left_x);
136 rect
137 }
138
139 #[inline]
141 pub fn everything_left_of(right_x: f32) -> Self {
142 let mut rect = Self::EVERYTHING;
143 rect.set_right(right_x);
144 rect
145 }
146
147 #[inline]
149 pub fn everything_below(top_y: f32) -> Self {
150 let mut rect = Self::EVERYTHING;
151 rect.set_top(top_y);
152 rect
153 }
154
155 #[inline]
157 pub fn everything_above(bottom_y: f32) -> Self {
158 let mut rect = Self::EVERYTHING;
159 rect.set_bottom(bottom_y);
160 rect
161 }
162
163 #[must_use]
164 #[inline]
165 pub fn with_min_x(mut self, min_x: f32) -> Self {
166 self.min.x = min_x;
167 self
168 }
169
170 #[must_use]
171 #[inline]
172 pub fn with_min_y(mut self, min_y: f32) -> Self {
173 self.min.y = min_y;
174 self
175 }
176
177 #[must_use]
178 #[inline]
179 pub fn with_max_x(mut self, max_x: f32) -> Self {
180 self.max.x = max_x;
181 self
182 }
183
184 #[must_use]
185 #[inline]
186 pub fn with_max_y(mut self, max_y: f32) -> Self {
187 self.max.y = max_y;
188 self
189 }
190
191 #[must_use]
193 pub fn expand(self, amnt: f32) -> Self {
194 self.expand2(Vec2::splat(amnt))
195 }
196
197 #[must_use]
199 pub fn expand2(self, amnt: Vec2) -> Self {
200 Self::from_min_max(self.min - amnt, self.max + amnt)
201 }
202
203 #[must_use]
205 pub fn scale_from_center(self, scale_factor: f32) -> Self {
206 self.scale_from_center2(Vec2::splat(scale_factor))
207 }
208
209 #[must_use]
211 pub fn scale_from_center2(self, scale_factor: Vec2) -> Self {
212 Self::from_center_size(self.center(), self.size() * scale_factor)
213 }
214
215 #[must_use]
217 pub fn shrink(self, amnt: f32) -> Self {
218 self.shrink2(Vec2::splat(amnt))
219 }
220
221 #[must_use]
223 pub fn shrink2(self, amnt: Vec2) -> Self {
224 Self::from_min_max(self.min + amnt, self.max - amnt)
225 }
226
227 #[must_use]
228 #[inline]
229 pub fn translate(self, amnt: Vec2) -> Self {
230 Self::from_min_size(self.min + amnt, self.size())
231 }
232
233 #[must_use]
235 #[inline]
236 pub fn rotate_bb(self, rot: Rot2) -> Self {
237 let a = rot * self.left_top().to_vec2();
238 let b = rot * self.right_top().to_vec2();
239 let c = rot * self.left_bottom().to_vec2();
240 let d = rot * self.right_bottom().to_vec2();
241
242 Self::from_min_max(
243 a.min(b).min(c).min(d).to_pos2(),
244 a.max(b).max(c).max(d).to_pos2(),
245 )
246 }
247
248 #[must_use]
249 #[inline]
250 pub fn intersects(self, other: Self) -> bool {
251 self.min.x <= other.max.x
252 && other.min.x <= self.max.x
253 && self.min.y <= other.max.y
254 && other.min.y <= self.max.y
255 }
256
257 pub fn set_width(&mut self, w: f32) {
259 self.max.x = self.min.x + w;
260 }
261
262 pub fn set_height(&mut self, h: f32) {
264 self.max.y = self.min.y + h;
265 }
266
267 pub fn set_center(&mut self, center: Pos2) {
269 *self = self.translate(center - self.center());
270 }
271
272 #[must_use]
273 #[inline(always)]
274 pub fn contains(&self, p: Pos2) -> bool {
275 self.min.x <= p.x && p.x <= self.max.x && self.min.y <= p.y && p.y <= self.max.y
276 }
277
278 #[must_use]
279 pub fn contains_rect(&self, other: Self) -> bool {
280 self.contains(other.min) && self.contains(other.max)
281 }
282
283 #[must_use]
286 pub fn clamp(&self, p: Pos2) -> Pos2 {
287 p.clamp(self.min, self.max)
288 }
289
290 #[inline(always)]
291 pub fn extend_with(&mut self, p: Pos2) {
292 self.min = self.min.min(p);
293 self.max = self.max.max(p);
294 }
295
296 #[inline(always)]
297 pub fn extend_with_x(&mut self, x: f32) {
299 self.min.x = self.min.x.min(x);
300 self.max.x = self.max.x.max(x);
301 }
302
303 #[inline(always)]
304 pub fn extend_with_y(&mut self, y: f32) {
306 self.min.y = self.min.y.min(y);
307 self.max.y = self.max.y.max(y);
308 }
309
310 #[inline(always)]
313 #[must_use]
314 pub fn union(self, other: Self) -> Self {
315 Self {
316 min: self.min.min(other.min),
317 max: self.max.max(other.max),
318 }
319 }
320
321 #[inline]
323 #[must_use]
324 pub fn intersect(self, other: Self) -> Self {
325 Self {
326 min: self.min.max(other.min),
327 max: self.max.min(other.max),
328 }
329 }
330
331 #[inline(always)]
332 pub fn center(&self) -> Pos2 {
333 Pos2 {
334 x: fast_midpoint(self.min.x, self.max.x),
335 y: fast_midpoint(self.min.y, self.max.y),
336 }
337 }
338
339 #[inline(always)]
341 pub fn size(&self) -> Vec2 {
342 self.max - self.min
343 }
344
345 #[inline(always)]
347 pub fn width(&self) -> f32 {
348 self.max.x - self.min.x
349 }
350
351 #[inline(always)]
353 pub fn height(&self) -> f32 {
354 self.max.y - self.min.y
355 }
356
357 pub fn aspect_ratio(&self) -> f32 {
363 self.width() / self.height()
364 }
365
366 pub fn square_proportions(&self) -> Vec2 {
370 let w = self.width();
371 let h = self.height();
372 if w > h {
373 vec2(w / h, 1.0)
374 } else {
375 vec2(1.0, h / w)
376 }
377 }
378
379 #[inline(always)]
381 pub fn area(&self) -> f32 {
382 self.width().at_least(0.0) * self.height().at_least(0.0)
383 }
384
385 #[inline]
391 pub fn distance_to_pos(&self, pos: Pos2) -> f32 {
392 self.distance_sq_to_pos(pos).sqrt()
393 }
394
395 #[inline]
401 pub fn distance_sq_to_pos(&self, pos: Pos2) -> f32 {
402 if self.is_negative() {
403 return f32::INFINITY;
404 }
405
406 let dx = if self.min.x > pos.x {
407 self.min.x - pos.x
408 } else if pos.x > self.max.x {
409 pos.x - self.max.x
410 } else {
411 0.0
412 };
413
414 let dy = if self.min.y > pos.y {
415 self.min.y - pos.y
416 } else if pos.y > self.max.y {
417 pos.y - self.max.y
418 } else {
419 0.0
420 };
421
422 dx * dx + dy * dy
423 }
424
425 pub fn signed_distance_to_pos(&self, pos: Pos2) -> f32 {
439 if self.is_negative() {
440 return f32::INFINITY;
441 }
442
443 let edge_distances = (pos - self.center()).abs() - self.size() * 0.5;
444 let inside_dist = edge_distances.max_elem().min(0.0);
445 let outside_dist = edge_distances.max(Vec2::ZERO).length();
446 inside_dist + outside_dist
447 }
448
449 #[inline]
452 pub fn lerp_inside(&self, t: impl Into<Vec2>) -> Pos2 {
453 let t = t.into();
454 Pos2 {
455 x: lerp(self.min.x..=self.max.x, t.x),
456 y: lerp(self.min.y..=self.max.y, t.y),
457 }
458 }
459
460 #[inline]
462 pub fn lerp_towards(&self, other: &Self, t: f32) -> Self {
463 Self {
464 min: self.min.lerp(other.min, t),
465 max: self.max.lerp(other.max, t),
466 }
467 }
468
469 #[inline(always)]
470 pub fn x_range(&self) -> Rangef {
471 Rangef::new(self.min.x, self.max.x)
472 }
473
474 #[inline(always)]
475 pub fn y_range(&self) -> Rangef {
476 Rangef::new(self.min.y, self.max.y)
477 }
478
479 #[inline]
486 pub fn range_along(&self, axis: usize) -> Rangef {
487 match axis {
488 0 => self.x_range(),
489 1 => self.y_range(),
490 _ => panic!("axis must be 0 or 1, got {axis}"),
491 }
492 }
493
494 #[inline]
501 pub fn size_along(&self, axis: usize) -> f32 {
502 self.size()[axis]
503 }
504
505 #[inline(always)]
506 pub fn bottom_up_range(&self) -> Rangef {
507 Rangef::new(self.max.y, self.min.y)
508 }
509
510 #[inline(always)]
512 pub fn is_negative(&self) -> bool {
513 self.max.x < self.min.x || self.max.y < self.min.y
514 }
515
516 #[inline(always)]
518 pub fn is_positive(&self) -> bool {
519 self.min.x < self.max.x && self.min.y < self.max.y
520 }
521
522 #[inline(always)]
524 pub fn is_finite(&self) -> bool {
525 self.min.is_finite() && self.max.is_finite()
526 }
527
528 #[inline(always)]
530 pub fn any_nan(self) -> bool {
531 self.min.any_nan() || self.max.any_nan()
532 }
533}
534
535impl Rect {
537 #[inline(always)]
539 pub fn left(&self) -> f32 {
540 self.min.x
541 }
542
543 #[inline(always)]
545 pub fn left_mut(&mut self) -> &mut f32 {
546 &mut self.min.x
547 }
548
549 #[inline(always)]
551 pub fn set_left(&mut self, x: f32) {
552 self.min.x = x;
553 }
554
555 #[inline(always)]
557 pub fn right(&self) -> f32 {
558 self.max.x
559 }
560
561 #[inline(always)]
563 pub fn right_mut(&mut self) -> &mut f32 {
564 &mut self.max.x
565 }
566
567 #[inline(always)]
569 pub fn set_right(&mut self, x: f32) {
570 self.max.x = x;
571 }
572
573 #[inline(always)]
575 pub fn top(&self) -> f32 {
576 self.min.y
577 }
578
579 #[inline(always)]
581 pub fn top_mut(&mut self) -> &mut f32 {
582 &mut self.min.y
583 }
584
585 #[inline(always)]
587 pub fn set_top(&mut self, y: f32) {
588 self.min.y = y;
589 }
590
591 #[inline(always)]
593 pub fn bottom(&self) -> f32 {
594 self.max.y
595 }
596
597 #[inline(always)]
599 pub fn bottom_mut(&mut self) -> &mut f32 {
600 &mut self.max.y
601 }
602
603 #[inline(always)]
605 pub fn set_bottom(&mut self, y: f32) {
606 self.max.y = y;
607 }
608
609 #[inline(always)]
610 #[doc(alias = "top_left")]
611 pub fn left_top(&self) -> Pos2 {
612 pos2(self.left(), self.top())
613 }
614
615 #[inline(always)]
616 pub fn center_top(&self) -> Pos2 {
617 pos2(self.center().x, self.top())
618 }
619
620 #[inline(always)]
621 #[doc(alias = "top_right")]
622 pub fn right_top(&self) -> Pos2 {
623 pos2(self.right(), self.top())
624 }
625
626 #[inline(always)]
627 pub fn left_center(&self) -> Pos2 {
628 pos2(self.left(), self.center().y)
629 }
630
631 #[inline(always)]
632 pub fn right_center(&self) -> Pos2 {
633 pos2(self.right(), self.center().y)
634 }
635
636 #[inline(always)]
637 #[doc(alias = "bottom_left")]
638 pub fn left_bottom(&self) -> Pos2 {
639 pos2(self.left(), self.bottom())
640 }
641
642 #[inline(always)]
643 pub fn center_bottom(&self) -> Pos2 {
644 pos2(self.center().x, self.bottom())
645 }
646
647 #[inline(always)]
648 #[doc(alias = "bottom_right")]
649 pub fn right_bottom(&self) -> Pos2 {
650 pos2(self.right(), self.bottom())
651 }
652
653 pub fn split_left_right_at_fraction(&self, t: f32) -> (Self, Self) {
655 self.split_left_right_at_x(lerp(self.min.x..=self.max.x, t))
656 }
657
658 pub fn split_left_right_at_x(&self, split_x: f32) -> (Self, Self) {
660 let left = Self::from_min_max(self.min, Pos2::new(split_x, self.max.y));
661 let right = Self::from_min_max(Pos2::new(split_x, self.min.y), self.max);
662 (left, right)
663 }
664
665 pub fn split_top_bottom_at_fraction(&self, t: f32) -> (Self, Self) {
667 self.split_top_bottom_at_y(lerp(self.min.y..=self.max.y, t))
668 }
669
670 pub fn split_top_bottom_at_y(&self, split_y: f32) -> (Self, Self) {
672 let top = Self::from_min_max(self.min, Pos2::new(self.max.x, split_y));
673 let bottom = Self::from_min_max(Pos2::new(self.min.x, split_y), self.max);
674 (top, bottom)
675 }
676}
677
678impl Rect {
679 pub fn intersects_ray(&self, o: Pos2, d: Vec2) -> bool {
683 debug_assert!(
684 d.is_normalized(),
685 "Debug assert: expected normalized direction, but `d` has length {}",
686 d.length()
687 );
688
689 let mut tmin = -f32::INFINITY;
690 let mut tmax = f32::INFINITY;
691
692 if d.x != 0.0 {
693 let tx1 = (self.min.x - o.x) / d.x;
694 let tx2 = (self.max.x - o.x) / d.x;
695
696 tmin = tmin.max(tx1.min(tx2));
697 tmax = tmax.min(tx1.max(tx2));
698 }
699
700 if d.y != 0.0 {
701 let ty1 = (self.min.y - o.y) / d.y;
702 let ty2 = (self.max.y - o.y) / d.y;
703
704 tmin = tmin.max(ty1.min(ty2));
705 tmax = tmax.min(ty1.max(ty2));
706 }
707
708 0.0 <= tmax && tmin <= tmax
709 }
710
711 pub fn intersects_ray_from_center(&self, d: Vec2) -> Pos2 {
715 debug_assert!(
716 d.is_normalized(),
717 "expected normalized direction, but `d` has length {}",
718 d.length()
719 );
720
721 let mut tmin = f32::NEG_INFINITY;
722 let mut tmax = f32::INFINITY;
723
724 for i in 0..2 {
725 let inv_d = 1.0 / -d[i];
726 let mut t0 = (self.min[i] - self.center()[i]) * inv_d;
727 let mut t1 = (self.max[i] - self.center()[i]) * inv_d;
728
729 if inv_d < 0.0 {
730 core::mem::swap(&mut t0, &mut t1);
731 }
732
733 tmin = tmin.max(t0);
734 tmax = tmax.min(t1);
735 }
736
737 let t = tmax.min(tmin);
738 self.center() + t * -d
739 }
740}
741
742impl fmt::Debug for Rect {
743 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
744 if let Some(precision) = f.precision() {
745 write!(f, "[{1:.0$?} - {2:.0$?}]", precision, self.min, self.max)
746 } else {
747 write!(f, "[{:?} - {:?}]", self.min, self.max)
748 }
749 }
750}
751
752impl fmt::Display for Rect {
753 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
754 f.write_str("[")?;
755 self.min.fmt(f)?;
756 f.write_str(" - ")?;
757 self.max.fmt(f)?;
758 f.write_str("]")?;
759 Ok(())
760 }
761}
762
763impl From<[Pos2; 2]> for Rect {
765 #[inline]
766 fn from([min, max]: [Pos2; 2]) -> Self {
767 Self { min, max }
768 }
769}
770
771impl Mul<f32> for Rect {
772 type Output = Self;
773
774 #[inline]
775 fn mul(self, factor: f32) -> Self {
776 Self {
777 min: self.min * factor,
778 max: self.max * factor,
779 }
780 }
781}
782
783impl Mul<Rect> for f32 {
784 type Output = Rect;
785
786 #[inline]
787 fn mul(self, vec: Rect) -> Rect {
788 Rect {
789 min: self * vec.min,
790 max: self * vec.max,
791 }
792 }
793}
794
795impl Div<f32> for Rect {
796 type Output = Self;
797
798 #[inline]
799 fn div(self, factor: f32) -> Self {
800 Self {
801 min: self.min / factor,
802 max: self.max / factor,
803 }
804 }
805}
806
807impl BitOr for Rect {
808 type Output = Self;
809
810 #[inline]
811 fn bitor(self, other: Self) -> Self {
812 self.union(other)
813 }
814}
815
816impl BitOrAssign for Rect {
817 #[inline]
818 fn bitor_assign(&mut self, other: Self) {
819 *self = self.union(other);
820 }
821}
822
823#[cfg(test)]
824mod tests {
825 use super::*;
826
827 #[test]
828 fn test_rect() {
829 let r = Rect::from_min_max(pos2(10.0, 10.0), pos2(20.0, 20.0));
830 assert_eq!(r.distance_sq_to_pos(pos2(15.0, 15.0)), 0.0);
831 assert_eq!(r.distance_sq_to_pos(pos2(10.0, 15.0)), 0.0);
832 assert_eq!(r.distance_sq_to_pos(pos2(10.0, 10.0)), 0.0);
833
834 assert_eq!(r.distance_sq_to_pos(pos2(5.0, 15.0)), 25.0); assert_eq!(r.distance_sq_to_pos(pos2(25.0, 15.0)), 25.0); assert_eq!(r.distance_sq_to_pos(pos2(15.0, 5.0)), 25.0); assert_eq!(r.distance_sq_to_pos(pos2(15.0, 25.0)), 25.0); assert_eq!(r.distance_sq_to_pos(pos2(25.0, 5.0)), 50.0); }
840
841 #[test]
842 fn scale_rect() {
843 let c = pos2(100.0, 50.0);
844 let r = Rect::from_center_size(c, vec2(30.0, 60.0));
845
846 assert_eq!(
847 r.scale_from_center(2.0),
848 Rect::from_center_size(c, vec2(60.0, 120.0))
849 );
850 assert_eq!(
851 r.scale_from_center(0.5),
852 Rect::from_center_size(c, vec2(15.0, 30.0))
853 );
854 assert_eq!(
855 r.scale_from_center2(vec2(2.0, 3.0)),
856 Rect::from_center_size(c, vec2(60.0, 180.0))
857 );
858 }
859
860 #[expect(clippy::print_stdout)]
861 #[test]
862 fn test_ray_intersection() {
863 let rect = Rect::from_min_max(pos2(1.0, 1.0), pos2(3.0, 3.0));
864
865 println!("Righward ray from left:");
866 assert!(rect.intersects_ray(pos2(0.0, 2.0), Vec2::RIGHT));
867
868 println!("Righward ray from center:");
869 assert!(rect.intersects_ray(pos2(2.0, 2.0), Vec2::RIGHT));
870
871 println!("Righward ray from right:");
872 assert!(!rect.intersects_ray(pos2(4.0, 2.0), Vec2::RIGHT));
873
874 println!("Leftward ray from left:");
875 assert!(!rect.intersects_ray(pos2(0.0, 2.0), Vec2::LEFT));
876
877 println!("Leftward ray from center:");
878 assert!(rect.intersects_ray(pos2(2.0, 2.0), Vec2::LEFT));
879
880 println!("Leftward ray from right:");
881 assert!(rect.intersects_ray(pos2(4.0, 2.0), Vec2::LEFT));
882 }
883
884 #[test]
885 fn test_ray_from_center_intersection() {
886 let rect = Rect::from_min_max(pos2(1.0, 1.0), pos2(3.0, 3.0));
887
888 assert_eq!(
889 rect.intersects_ray_from_center(Vec2::RIGHT),
890 pos2(3.0, 2.0),
891 "rightward ray"
892 );
893
894 assert_eq!(
895 rect.intersects_ray_from_center(Vec2::UP),
896 pos2(2.0, 1.0),
897 "upward ray"
898 );
899
900 assert_eq!(
901 rect.intersects_ray_from_center(Vec2::LEFT),
902 pos2(1.0, 2.0),
903 "leftward ray"
904 );
905
906 assert_eq!(
907 rect.intersects_ray_from_center(Vec2::DOWN),
908 pos2(2.0, 3.0),
909 "downward ray"
910 );
911
912 assert_eq!(
913 rect.intersects_ray_from_center((Vec2::LEFT + Vec2::DOWN).normalized()),
914 pos2(1.0, 3.0),
915 "bottom-left corner ray"
916 );
917
918 assert_eq!(
919 rect.intersects_ray_from_center((Vec2::LEFT + Vec2::UP).normalized()),
920 pos2(1.0, 1.0),
921 "top-left corner ray"
922 );
923
924 assert_eq!(
925 rect.intersects_ray_from_center((Vec2::RIGHT + Vec2::DOWN).normalized()),
926 pos2(3.0, 3.0),
927 "bottom-right corner ray"
928 );
929
930 assert_eq!(
931 rect.intersects_ray_from_center((Vec2::RIGHT + Vec2::UP).normalized()),
932 pos2(3.0, 1.0),
933 "top-right corner ray"
934 );
935 }
936}