Skip to main content

emath/
rect.rs

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/// A rectangular region of space.
7///
8/// Usually a [`Rect`] has a positive (or zero) size,
9/// and then [`Self::min`] `<=` [`Self::max`].
10/// In these cases [`Self::min`] is the left-top corner
11/// and [`Self::max`] is the right-bottom corner.
12///
13/// A rectangle is allowed to have a negative size, which happens when the order
14/// of `min` and `max` are swapped. These are usually a sign of an error.
15///
16/// Normally the unit is points (logical pixels) in screen space coordinates.
17///
18/// `Rect` does NOT implement `Default`, because there is no obvious default value.
19/// [`Rect::ZERO`] may seem reasonable, but when used as a bounding box, [`Rect::NOTHING`]
20/// is a better default - so be explicit instead!
21#[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    /// One of the corners of the rectangle, usually the left top one.
27    pub min: Pos2,
28
29    /// The other corner, opposing [`Self::min`]. Usually the right bottom one.
30    pub max: Pos2,
31}
32
33impl Rect {
34    /// Infinite rectangle that contains every point.
35    pub const EVERYTHING: Self = Self {
36        min: pos2(-f32::INFINITY, -f32::INFINITY),
37        max: pos2(f32::INFINITY, f32::INFINITY),
38    };
39
40    /// The inverse of [`Self::EVERYTHING`]: stretches from positive infinity to negative infinity.
41    /// Contains no points.
42    ///
43    /// This is useful as the seed for bounding boxes.
44    ///
45    /// # Example:
46    /// ```
47    /// # use emath::*;
48    /// let mut rect = Rect::NOTHING;
49    /// assert!(rect.size() == Vec2::splat(-f32::INFINITY));
50    /// assert!(rect.contains(pos2(0.0, 0.0)) == false);
51    /// rect.extend_with(pos2(2.0, 1.0));
52    /// rect.extend_with(pos2(0.0, 3.0));
53    /// assert_eq!(rect, Rect::from_min_max(pos2(0.0, 1.0), pos2(2.0, 3.0)))
54    /// ```
55    pub const NOTHING: Self = Self {
56        min: pos2(f32::INFINITY, f32::INFINITY),
57        max: pos2(-f32::INFINITY, -f32::INFINITY),
58    };
59
60    /// An invalid [`Rect`] filled with [`f32::NAN`].
61    pub const NAN: Self = Self {
62        min: pos2(f32::NAN, f32::NAN),
63        max: pos2(f32::NAN, f32::NAN),
64    };
65
66    /// A [`Rect`] filled with zeroes.
67    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    /// left-top corner plus a size (stretching right-down).
78    #[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    /// Returns the bounding rectangle of the two points.
105    #[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    /// A zero-sized rect at a specific point.
114    #[inline]
115    pub fn from_pos(point: Pos2) -> Self {
116        Self {
117            min: point,
118            max: point,
119        }
120    }
121
122    /// Bounding-box around the points.
123    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    /// A [`Rect`] that contains every point to the right of the given X coordinate.
132    #[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    /// A [`Rect`] that contains every point to the left of the given X coordinate.
140    #[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    /// A [`Rect`] that contains every point below a certain y coordinate
148    #[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    /// A [`Rect`] that contains every point above a certain y coordinate
156    #[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    /// Expand by this much in each direction, keeping the center
192    #[must_use]
193    pub fn expand(self, amnt: f32) -> Self {
194        self.expand2(Vec2::splat(amnt))
195    }
196
197    /// Expand by this much in each direction, keeping the center
198    #[must_use]
199    pub fn expand2(self, amnt: Vec2) -> Self {
200        Self::from_min_max(self.min - amnt, self.max + amnt)
201    }
202
203    /// Scale up by this factor in each direction, keeping the center
204    #[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    /// Scale up by this factor in each direction, keeping the center
210    #[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    /// Shrink by this much in each direction, keeping the center
216    #[must_use]
217    pub fn shrink(self, amnt: f32) -> Self {
218        self.shrink2(Vec2::splat(amnt))
219    }
220
221    /// Shrink by this much in each direction, keeping the center
222    #[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    /// Rotate the bounds (will expand the [`Rect`])
234    #[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    /// keep min
258    pub fn set_width(&mut self, w: f32) {
259        self.max.x = self.min.x + w;
260    }
261
262    /// keep min
263    pub fn set_height(&mut self, h: f32) {
264        self.max.y = self.min.y + h;
265    }
266
267    /// Keep size
268    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    /// Return the given points clamped to be inside the rectangle
284    /// Panics if [`Self::is_negative`].
285    #[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    /// Expand to include the given x coordinate
298    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    /// Expand to include the given y coordinate
305    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    /// The union of two bounding rectangle, i.e. the minimum [`Rect`]
311    /// that contains both input rectangles.
312    #[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    /// The intersection of two [`Rect`], i.e. the area covered by both.
322    #[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    /// `rect.size() == Vec2 { x: rect.width(), y: rect.height() }`
340    #[inline(always)]
341    pub fn size(&self) -> Vec2 {
342        self.max - self.min
343    }
344
345    /// Note: this can be negative.
346    #[inline(always)]
347    pub fn width(&self) -> f32 {
348        self.max.x - self.min.x
349    }
350
351    /// Note: this can be negative.
352    #[inline(always)]
353    pub fn height(&self) -> f32 {
354        self.max.y - self.min.y
355    }
356
357    /// Width / height
358    ///
359    /// * `aspect_ratio < 1`: portrait / high
360    /// * `aspect_ratio = 1`: square
361    /// * `aspect_ratio > 1`: landscape / wide
362    pub fn aspect_ratio(&self) -> f32 {
363        self.width() / self.height()
364    }
365
366    /// `[2, 1]` for wide screen, and `[1, 2]` for portrait, etc.
367    /// At least one dimension = 1, the other >= 1
368    /// Returns the proportions required to letter-box a square view area.
369    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    /// This is never negative, and instead returns zero for negative rectangles.
380    #[inline(always)]
381    pub fn area(&self) -> f32 {
382        self.width().at_least(0.0) * self.height().at_least(0.0)
383    }
384
385    /// The distance from the rect to the position.
386    ///
387    /// The distance is zero when the position is in the interior of the rectangle.
388    ///
389    /// [Negative rectangles](Self::is_negative) always return [`f32::INFINITY`].
390    #[inline]
391    pub fn distance_to_pos(&self, pos: Pos2) -> f32 {
392        self.distance_sq_to_pos(pos).sqrt()
393    }
394
395    /// The distance from the rect to the position, squared.
396    ///
397    /// The distance is zero when the position is in the interior of the rectangle.
398    ///
399    /// [Negative rectangles](Self::is_negative) always return [`f32::INFINITY`].
400    #[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    /// Signed distance to the edge of the box.
426    ///
427    /// Negative inside the box.
428    ///
429    /// [Negative rectangles](Self::is_negative) always return [`f32::INFINITY`].
430    ///
431    /// ```
432    /// # use emath::{pos2, Rect};
433    /// let rect = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0));
434    /// assert_eq!(rect.signed_distance_to_pos(pos2(0.50, 0.50)), -0.50);
435    /// assert_eq!(rect.signed_distance_to_pos(pos2(0.75, 0.50)), -0.25);
436    /// assert_eq!(rect.signed_distance_to_pos(pos2(1.50, 0.50)), 0.50);
437    /// ```
438    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    /// Linearly interpolate so that `[0, 0]` is [`Self::min`] and
450    /// `[1, 1]` is [`Self::max`].
451    #[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    /// Linearly self towards other rect.
461    #[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    /// The extent along the given axis: `0` for x, `1` for y.
480    ///
481    /// Equivalent to [`Self::x_range`] for `axis == 0` and [`Self::y_range`] for `axis == 1`.
482    ///
483    /// # Panics
484    /// If `axis` is not `0` or `1`.
485    #[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    /// The size along the given axis: `0` for x (width), `1` for y (height).
495    ///
496    /// Equivalent to `self.size()[axis]`.
497    ///
498    /// # Panics
499    /// If `axis` is not `0` or `1`.
500    #[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    /// `width < 0 || height < 0`
511    #[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    /// `width > 0 && height > 0`
517    #[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    /// True if all members are also finite.
523    #[inline(always)]
524    pub fn is_finite(&self) -> bool {
525        self.min.is_finite() && self.max.is_finite()
526    }
527
528    /// True if any member is NaN.
529    #[inline(always)]
530    pub fn any_nan(self) -> bool {
531        self.min.any_nan() || self.max.any_nan()
532    }
533}
534
535/// ## Convenience functions (assumes origin is towards left top):
536impl Rect {
537    /// `min.x`
538    #[inline(always)]
539    pub fn left(&self) -> f32 {
540        self.min.x
541    }
542
543    /// `min.x`
544    #[inline(always)]
545    pub fn left_mut(&mut self) -> &mut f32 {
546        &mut self.min.x
547    }
548
549    /// `min.x`
550    #[inline(always)]
551    pub fn set_left(&mut self, x: f32) {
552        self.min.x = x;
553    }
554
555    /// `max.x`
556    #[inline(always)]
557    pub fn right(&self) -> f32 {
558        self.max.x
559    }
560
561    /// `max.x`
562    #[inline(always)]
563    pub fn right_mut(&mut self) -> &mut f32 {
564        &mut self.max.x
565    }
566
567    /// `max.x`
568    #[inline(always)]
569    pub fn set_right(&mut self, x: f32) {
570        self.max.x = x;
571    }
572
573    /// `min.y`
574    #[inline(always)]
575    pub fn top(&self) -> f32 {
576        self.min.y
577    }
578
579    /// `min.y`
580    #[inline(always)]
581    pub fn top_mut(&mut self) -> &mut f32 {
582        &mut self.min.y
583    }
584
585    /// `min.y`
586    #[inline(always)]
587    pub fn set_top(&mut self, y: f32) {
588        self.min.y = y;
589    }
590
591    /// `max.y`
592    #[inline(always)]
593    pub fn bottom(&self) -> f32 {
594        self.max.y
595    }
596
597    /// `max.y`
598    #[inline(always)]
599    pub fn bottom_mut(&mut self) -> &mut f32 {
600        &mut self.max.y
601    }
602
603    /// `max.y`
604    #[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    /// Split rectangle in left and right halves. `t` is expected to be in the (0,1) range.
654    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    /// Split rectangle in left and right halves at the given `x` coordinate.
659    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    /// Split rectangle in top and bottom halves. `t` is expected to be in the (0,1) range.
666    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    /// Split rectangle in top and bottom halves at the given `y` coordinate.
671    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    /// Does this Rect intersect the given ray (where `d` is normalized)?
680    ///
681    /// A ray that starts inside the rect will return `true`.
682    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    /// Where does a ray from the center intersect the rectangle?
712    ///
713    /// `d` is the direction of the ray and assumed to be normalized.
714    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
763/// from (min, max) or (left top, right bottom)
764impl 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); // left of
835        assert_eq!(r.distance_sq_to_pos(pos2(25.0, 15.0)), 25.0); // right of
836        assert_eq!(r.distance_sq_to_pos(pos2(15.0, 5.0)), 25.0); // above
837        assert_eq!(r.distance_sq_to_pos(pos2(15.0, 25.0)), 25.0); // below
838        assert_eq!(r.distance_sq_to_pos(pos2(25.0, 5.0)), 50.0); // right and above
839    }
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}