emath/
rect.rs

1use std::fmt;
2
3use crate::{lerp, pos2, vec2, Div, Mul, Pos2, Rangef, Rot2, Vec2};
4
5/// A rectangular region of space.
6///
7/// Usually a [`Rect`] has a positive (or zero) size,
8/// and then [`Self::min`] `<=` [`Self::max`].
9/// In these cases [`Self::min`] is the left-top corner
10/// and [`Self::max`] is the right-bottom corner.
11///
12/// A rectangle is allowed to have a negative size, which happens when the order
13/// of `min` and `max` are swapped. These are usually a sign of an error.
14///
15/// Normally the unit is points (logical pixels) in screen space coordinates.
16///
17/// `Rect` does NOT implement `Default`, because there is no obvious default value.
18/// [`Rect::ZERO`] may seem reasonable, but when used as a bounding box, [`Rect::NOTHING`]
19/// is a better default - so be explicit instead!
20#[repr(C)]
21#[derive(Clone, Copy, Eq, PartialEq)]
22#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
23#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
24pub struct Rect {
25    /// One of the corners of the rectangle, usually the left top one.
26    pub min: Pos2,
27
28    /// The other corner, opposing [`Self::min`]. Usually the right bottom one.
29    pub max: Pos2,
30}
31
32impl Rect {
33    /// Infinite rectangle that contains every point.
34    pub const EVERYTHING: Self = Self {
35        min: pos2(-f32::INFINITY, -f32::INFINITY),
36        max: pos2(f32::INFINITY, f32::INFINITY),
37    };
38
39    /// The inverse of [`Self::EVERYTHING`]: stretches from positive infinity to negative infinity.
40    /// Contains no points.
41    ///
42    /// This is useful as the seed for bounding boxes.
43    ///
44    /// # Example:
45    /// ```
46    /// # use emath::*;
47    /// let mut rect = Rect::NOTHING;
48    /// assert!(rect.size() == Vec2::splat(-f32::INFINITY));
49    /// assert!(rect.contains(pos2(0.0, 0.0)) == false);
50    /// rect.extend_with(pos2(2.0, 1.0));
51    /// rect.extend_with(pos2(0.0, 3.0));
52    /// assert_eq!(rect, Rect::from_min_max(pos2(0.0, 1.0), pos2(2.0, 3.0)))
53    /// ```
54    pub const NOTHING: Self = Self {
55        min: pos2(f32::INFINITY, f32::INFINITY),
56        max: pos2(-f32::INFINITY, -f32::INFINITY),
57    };
58
59    /// An invalid [`Rect`] filled with [`f32::NAN`].
60    pub const NAN: Self = Self {
61        min: pos2(f32::NAN, f32::NAN),
62        max: pos2(f32::NAN, f32::NAN),
63    };
64
65    /// A [`Rect`] filled with zeroes.
66    pub const ZERO: Self = Self {
67        min: Pos2::ZERO,
68        max: Pos2::ZERO,
69    };
70
71    #[inline(always)]
72    pub const fn from_min_max(min: Pos2, max: Pos2) -> Self {
73        Self { min, max }
74    }
75
76    /// left-top corner plus a size (stretching right-down).
77    #[inline(always)]
78    pub fn from_min_size(min: Pos2, size: Vec2) -> Self {
79        Self {
80            min,
81            max: min + size,
82        }
83    }
84
85    #[inline(always)]
86    pub fn from_center_size(center: Pos2, size: Vec2) -> Self {
87        Self {
88            min: center - size * 0.5,
89            max: center + size * 0.5,
90        }
91    }
92
93    #[inline(always)]
94    pub fn from_x_y_ranges(x_range: impl Into<Rangef>, y_range: impl Into<Rangef>) -> Self {
95        let x_range = x_range.into();
96        let y_range = y_range.into();
97        Self {
98            min: pos2(x_range.min, y_range.min),
99            max: pos2(x_range.max, y_range.max),
100        }
101    }
102
103    /// Returns the bounding rectangle of the two points.
104    #[inline]
105    pub fn from_two_pos(a: Pos2, b: Pos2) -> Self {
106        Self {
107            min: pos2(a.x.min(b.x), a.y.min(b.y)),
108            max: pos2(a.x.max(b.x), a.y.max(b.y)),
109        }
110    }
111
112    /// A zero-sized rect at a specific point.
113    #[inline]
114    pub fn from_pos(point: Pos2) -> Self {
115        Self {
116            min: point,
117            max: point,
118        }
119    }
120
121    /// Bounding-box around the points.
122    pub fn from_points(points: &[Pos2]) -> Self {
123        let mut rect = Self::NOTHING;
124        for &p in points {
125            rect.extend_with(p);
126        }
127        rect
128    }
129
130    /// A [`Rect`] that contains every point to the right of the given X coordinate.
131    #[inline]
132    pub fn everything_right_of(left_x: f32) -> Self {
133        let mut rect = Self::EVERYTHING;
134        rect.set_left(left_x);
135        rect
136    }
137
138    /// A [`Rect`] that contains every point to the left of the given X coordinate.
139    #[inline]
140    pub fn everything_left_of(right_x: f32) -> Self {
141        let mut rect = Self::EVERYTHING;
142        rect.set_right(right_x);
143        rect
144    }
145
146    /// A [`Rect`] that contains every point below a certain y coordinate
147    #[inline]
148    pub fn everything_below(top_y: f32) -> Self {
149        let mut rect = Self::EVERYTHING;
150        rect.set_top(top_y);
151        rect
152    }
153
154    /// A [`Rect`] that contains every point above a certain y coordinate
155    #[inline]
156    pub fn everything_above(bottom_y: f32) -> Self {
157        let mut rect = Self::EVERYTHING;
158        rect.set_bottom(bottom_y);
159        rect
160    }
161
162    #[must_use]
163    #[inline]
164    pub fn with_min_x(mut self, min_x: f32) -> Self {
165        self.min.x = min_x;
166        self
167    }
168
169    #[must_use]
170    #[inline]
171    pub fn with_min_y(mut self, min_y: f32) -> Self {
172        self.min.y = min_y;
173        self
174    }
175
176    #[must_use]
177    #[inline]
178    pub fn with_max_x(mut self, max_x: f32) -> Self {
179        self.max.x = max_x;
180        self
181    }
182
183    #[must_use]
184    #[inline]
185    pub fn with_max_y(mut self, max_y: f32) -> Self {
186        self.max.y = max_y;
187        self
188    }
189
190    /// Expand by this much in each direction, keeping the center
191    #[must_use]
192    pub fn expand(self, amnt: f32) -> Self {
193        self.expand2(Vec2::splat(amnt))
194    }
195
196    /// Expand by this much in each direction, keeping the center
197    #[must_use]
198    pub fn expand2(self, amnt: Vec2) -> Self {
199        Self::from_min_max(self.min - amnt, self.max + amnt)
200    }
201
202    /// Scale up by this factor in each direction, keeping the center
203    #[must_use]
204    pub fn scale_from_center(self, scale_factor: f32) -> Self {
205        self.scale_from_center2(Vec2::splat(scale_factor))
206    }
207
208    /// Scale up by this factor in each direction, keeping the center
209    #[must_use]
210    pub fn scale_from_center2(self, scale_factor: Vec2) -> Self {
211        Self::from_center_size(self.center(), self.size() * scale_factor)
212    }
213
214    /// Shrink by this much in each direction, keeping the center
215    #[must_use]
216    pub fn shrink(self, amnt: f32) -> Self {
217        self.shrink2(Vec2::splat(amnt))
218    }
219
220    /// Shrink by this much in each direction, keeping the center
221    #[must_use]
222    pub fn shrink2(self, amnt: Vec2) -> Self {
223        Self::from_min_max(self.min + amnt, self.max - amnt)
224    }
225
226    #[must_use]
227    #[inline]
228    pub fn translate(self, amnt: Vec2) -> Self {
229        Self::from_min_size(self.min + amnt, self.size())
230    }
231
232    /// Rotate the bounds (will expand the [`Rect`])
233    #[must_use]
234    #[inline]
235    pub fn rotate_bb(self, rot: Rot2) -> Self {
236        let a = rot * self.left_top().to_vec2();
237        let b = rot * self.right_top().to_vec2();
238        let c = rot * self.left_bottom().to_vec2();
239        let d = rot * self.right_bottom().to_vec2();
240
241        Self::from_min_max(
242            a.min(b).min(c).min(d).to_pos2(),
243            a.max(b).max(c).max(d).to_pos2(),
244        )
245    }
246
247    #[must_use]
248    #[inline]
249    pub fn intersects(self, other: Self) -> bool {
250        self.min.x <= other.max.x
251            && other.min.x <= self.max.x
252            && self.min.y <= other.max.y
253            && other.min.y <= self.max.y
254    }
255
256    /// keep min
257    pub fn set_width(&mut self, w: f32) {
258        self.max.x = self.min.x + w;
259    }
260
261    /// keep min
262    pub fn set_height(&mut self, h: f32) {
263        self.max.y = self.min.y + h;
264    }
265
266    /// Keep size
267    pub fn set_center(&mut self, center: Pos2) {
268        *self = self.translate(center - self.center());
269    }
270
271    #[must_use]
272    #[inline(always)]
273    pub fn contains(&self, p: Pos2) -> bool {
274        self.min.x <= p.x && p.x <= self.max.x && self.min.y <= p.y && p.y <= self.max.y
275    }
276
277    #[must_use]
278    pub fn contains_rect(&self, other: Self) -> bool {
279        self.contains(other.min) && self.contains(other.max)
280    }
281
282    /// Return the given points clamped to be inside the rectangle
283    /// Panics if [`Self::is_negative`].
284    #[must_use]
285    pub fn clamp(&self, p: Pos2) -> Pos2 {
286        p.clamp(self.min, self.max)
287    }
288
289    #[inline(always)]
290    pub fn extend_with(&mut self, p: Pos2) {
291        self.min = self.min.min(p);
292        self.max = self.max.max(p);
293    }
294
295    #[inline(always)]
296    /// Expand to include the given x coordinate
297    pub fn extend_with_x(&mut self, x: f32) {
298        self.min.x = self.min.x.min(x);
299        self.max.x = self.max.x.max(x);
300    }
301
302    #[inline(always)]
303    /// Expand to include the given y coordinate
304    pub fn extend_with_y(&mut self, y: f32) {
305        self.min.y = self.min.y.min(y);
306        self.max.y = self.max.y.max(y);
307    }
308
309    /// The union of two bounding rectangle, i.e. the minimum [`Rect`]
310    /// that contains both input rectangles.
311    #[inline(always)]
312    #[must_use]
313    pub fn union(self, other: Self) -> Self {
314        Self {
315            min: self.min.min(other.min),
316            max: self.max.max(other.max),
317        }
318    }
319
320    /// The intersection of two [`Rect`], i.e. the area covered by both.
321    #[inline]
322    #[must_use]
323    pub fn intersect(self, other: Self) -> Self {
324        Self {
325            min: self.min.max(other.min),
326            max: self.max.min(other.max),
327        }
328    }
329
330    #[inline(always)]
331    pub fn center(&self) -> Pos2 {
332        Pos2 {
333            x: (self.min.x + self.max.x) / 2.0,
334            y: (self.min.y + self.max.y) / 2.0,
335        }
336    }
337
338    /// `rect.size() == Vec2 { x: rect.width(), y: rect.height() }`
339    #[inline(always)]
340    pub fn size(&self) -> Vec2 {
341        self.max - self.min
342    }
343
344    #[inline(always)]
345    pub fn width(&self) -> f32 {
346        self.max.x - self.min.x
347    }
348
349    #[inline(always)]
350    pub fn height(&self) -> f32 {
351        self.max.y - self.min.y
352    }
353
354    /// Width / height
355    ///
356    /// * `aspect_ratio < 1`: portrait / high
357    /// * `aspect_ratio = 1`: square
358    /// * `aspect_ratio > 1`: landscape / wide
359    pub fn aspect_ratio(&self) -> f32 {
360        self.width() / self.height()
361    }
362
363    /// `[2, 1]` for wide screen, and `[1, 2]` for portrait, etc.
364    /// At least one dimension = 1, the other >= 1
365    /// Returns the proportions required to letter-box a square view area.
366    pub fn square_proportions(&self) -> Vec2 {
367        let w = self.width();
368        let h = self.height();
369        if w > h {
370            vec2(w / h, 1.0)
371        } else {
372            vec2(1.0, h / w)
373        }
374    }
375
376    #[inline(always)]
377    pub fn area(&self) -> f32 {
378        self.width() * self.height()
379    }
380
381    /// The distance from the rect to the position.
382    ///
383    /// The distance is zero when the position is in the interior of the rectangle.
384    ///
385    /// [Negative rectangles](Self::is_negative) always return [`f32::INFINITY`].
386    #[inline]
387    pub fn distance_to_pos(&self, pos: Pos2) -> f32 {
388        self.distance_sq_to_pos(pos).sqrt()
389    }
390
391    /// The distance from the rect to the position, squared.
392    ///
393    /// The distance is zero when the position is in the interior of the rectangle.
394    ///
395    /// [Negative rectangles](Self::is_negative) always return [`f32::INFINITY`].
396    #[inline]
397    pub fn distance_sq_to_pos(&self, pos: Pos2) -> f32 {
398        if self.is_negative() {
399            return f32::INFINITY;
400        }
401
402        let dx = if self.min.x > pos.x {
403            self.min.x - pos.x
404        } else if pos.x > self.max.x {
405            pos.x - self.max.x
406        } else {
407            0.0
408        };
409
410        let dy = if self.min.y > pos.y {
411            self.min.y - pos.y
412        } else if pos.y > self.max.y {
413            pos.y - self.max.y
414        } else {
415            0.0
416        };
417
418        dx * dx + dy * dy
419    }
420
421    /// Signed distance to the edge of the box.
422    ///
423    /// Negative inside the box.
424    ///
425    /// [Negative rectangles](Self::is_negative) always return [`f32::INFINITY`].
426    ///
427    /// ```
428    /// # use emath::{pos2, Rect};
429    /// let rect = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0));
430    /// assert_eq!(rect.signed_distance_to_pos(pos2(0.50, 0.50)), -0.50);
431    /// assert_eq!(rect.signed_distance_to_pos(pos2(0.75, 0.50)), -0.25);
432    /// assert_eq!(rect.signed_distance_to_pos(pos2(1.50, 0.50)), 0.50);
433    /// ```
434    pub fn signed_distance_to_pos(&self, pos: Pos2) -> f32 {
435        if self.is_negative() {
436            return f32::INFINITY;
437        }
438
439        let edge_distances = (pos - self.center()).abs() - self.size() * 0.5;
440        let inside_dist = edge_distances.max_elem().min(0.0);
441        let outside_dist = edge_distances.max(Vec2::ZERO).length();
442        inside_dist + outside_dist
443    }
444
445    /// Linearly interpolate so that `[0, 0]` is [`Self::min`] and
446    /// `[1, 1]` is [`Self::max`].
447    #[inline]
448    pub fn lerp_inside(&self, t: Vec2) -> Pos2 {
449        Pos2 {
450            x: lerp(self.min.x..=self.max.x, t.x),
451            y: lerp(self.min.y..=self.max.y, t.y),
452        }
453    }
454
455    /// Linearly self towards other rect.
456    #[inline]
457    pub fn lerp_towards(&self, other: &Self, t: f32) -> Self {
458        Self {
459            min: self.min.lerp(other.min, t),
460            max: self.max.lerp(other.max, t),
461        }
462    }
463
464    #[inline(always)]
465    pub fn x_range(&self) -> Rangef {
466        Rangef::new(self.min.x, self.max.x)
467    }
468
469    #[inline(always)]
470    pub fn y_range(&self) -> Rangef {
471        Rangef::new(self.min.y, self.max.y)
472    }
473
474    #[inline(always)]
475    pub fn bottom_up_range(&self) -> Rangef {
476        Rangef::new(self.max.y, self.min.y)
477    }
478
479    /// `width < 0 || height < 0`
480    #[inline(always)]
481    pub fn is_negative(&self) -> bool {
482        self.max.x < self.min.x || self.max.y < self.min.y
483    }
484
485    /// `width > 0 && height > 0`
486    #[inline(always)]
487    pub fn is_positive(&self) -> bool {
488        self.min.x < self.max.x && self.min.y < self.max.y
489    }
490
491    /// True if all members are also finite.
492    #[inline(always)]
493    pub fn is_finite(&self) -> bool {
494        self.min.is_finite() && self.max.is_finite()
495    }
496
497    /// True if any member is NaN.
498    #[inline(always)]
499    pub fn any_nan(self) -> bool {
500        self.min.any_nan() || self.max.any_nan()
501    }
502}
503
504/// ## Convenience functions (assumes origin is towards left top):
505impl Rect {
506    /// `min.x`
507    #[inline(always)]
508    pub fn left(&self) -> f32 {
509        self.min.x
510    }
511
512    /// `min.x`
513    #[inline(always)]
514    pub fn left_mut(&mut self) -> &mut f32 {
515        &mut self.min.x
516    }
517
518    /// `min.x`
519    #[inline(always)]
520    pub fn set_left(&mut self, x: f32) {
521        self.min.x = x;
522    }
523
524    /// `max.x`
525    #[inline(always)]
526    pub fn right(&self) -> f32 {
527        self.max.x
528    }
529
530    /// `max.x`
531    #[inline(always)]
532    pub fn right_mut(&mut self) -> &mut f32 {
533        &mut self.max.x
534    }
535
536    /// `max.x`
537    #[inline(always)]
538    pub fn set_right(&mut self, x: f32) {
539        self.max.x = x;
540    }
541
542    /// `min.y`
543    #[inline(always)]
544    pub fn top(&self) -> f32 {
545        self.min.y
546    }
547
548    /// `min.y`
549    #[inline(always)]
550    pub fn top_mut(&mut self) -> &mut f32 {
551        &mut self.min.y
552    }
553
554    /// `min.y`
555    #[inline(always)]
556    pub fn set_top(&mut self, y: f32) {
557        self.min.y = y;
558    }
559
560    /// `max.y`
561    #[inline(always)]
562    pub fn bottom(&self) -> f32 {
563        self.max.y
564    }
565
566    /// `max.y`
567    #[inline(always)]
568    pub fn bottom_mut(&mut self) -> &mut f32 {
569        &mut self.max.y
570    }
571
572    /// `max.y`
573    #[inline(always)]
574    pub fn set_bottom(&mut self, y: f32) {
575        self.max.y = y;
576    }
577
578    #[inline(always)]
579    #[doc(alias = "top_left")]
580    pub fn left_top(&self) -> Pos2 {
581        pos2(self.left(), self.top())
582    }
583
584    #[inline(always)]
585    pub fn center_top(&self) -> Pos2 {
586        pos2(self.center().x, self.top())
587    }
588
589    #[inline(always)]
590    #[doc(alias = "top_right")]
591    pub fn right_top(&self) -> Pos2 {
592        pos2(self.right(), self.top())
593    }
594
595    #[inline(always)]
596    pub fn left_center(&self) -> Pos2 {
597        pos2(self.left(), self.center().y)
598    }
599
600    #[inline(always)]
601    pub fn right_center(&self) -> Pos2 {
602        pos2(self.right(), self.center().y)
603    }
604
605    #[inline(always)]
606    #[doc(alias = "bottom_left")]
607    pub fn left_bottom(&self) -> Pos2 {
608        pos2(self.left(), self.bottom())
609    }
610
611    #[inline(always)]
612    pub fn center_bottom(&self) -> Pos2 {
613        pos2(self.center().x, self.bottom())
614    }
615
616    #[inline(always)]
617    #[doc(alias = "bottom_right")]
618    pub fn right_bottom(&self) -> Pos2 {
619        pos2(self.right(), self.bottom())
620    }
621
622    /// Split rectangle in left and right halves. `t` is expected to be in the (0,1) range.
623    pub fn split_left_right_at_fraction(&self, t: f32) -> (Self, Self) {
624        self.split_left_right_at_x(lerp(self.min.x..=self.max.x, t))
625    }
626
627    /// Split rectangle in left and right halves at the given `x` coordinate.
628    pub fn split_left_right_at_x(&self, split_x: f32) -> (Self, Self) {
629        let left = Self::from_min_max(self.min, Pos2::new(split_x, self.max.y));
630        let right = Self::from_min_max(Pos2::new(split_x, self.min.y), self.max);
631        (left, right)
632    }
633
634    /// Split rectangle in top and bottom halves. `t` is expected to be in the (0,1) range.
635    pub fn split_top_bottom_at_fraction(&self, t: f32) -> (Self, Self) {
636        self.split_top_bottom_at_y(lerp(self.min.y..=self.max.y, t))
637    }
638
639    /// Split rectangle in top and bottom halves at the given `y` coordinate.
640    pub fn split_top_bottom_at_y(&self, split_y: f32) -> (Self, Self) {
641        let top = Self::from_min_max(self.min, Pos2::new(self.max.x, split_y));
642        let bottom = Self::from_min_max(Pos2::new(self.min.x, split_y), self.max);
643        (top, bottom)
644    }
645}
646
647impl Rect {
648    /// Does this Rect intersect the given ray (where `d` is normalized)?
649    ///
650    /// A ray that starts inside the rect will return `true`.
651    pub fn intersects_ray(&self, o: Pos2, d: Vec2) -> bool {
652        debug_assert!(
653            d.is_normalized(),
654            "expected normalized direction, but `d` has length {}",
655            d.length()
656        );
657
658        let mut tmin = -f32::INFINITY;
659        let mut tmax = f32::INFINITY;
660
661        if d.x != 0.0 {
662            let tx1 = (self.min.x - o.x) / d.x;
663            let tx2 = (self.max.x - o.x) / d.x;
664
665            tmin = tmin.max(tx1.min(tx2));
666            tmax = tmax.min(tx1.max(tx2));
667        }
668
669        if d.y != 0.0 {
670            let ty1 = (self.min.y - o.y) / d.y;
671            let ty2 = (self.max.y - o.y) / d.y;
672
673            tmin = tmin.max(ty1.min(ty2));
674            tmax = tmax.min(ty1.max(ty2));
675        }
676
677        0.0 <= tmax && tmin <= tmax
678    }
679
680    /// Where does a ray from the center intersect the rectangle?
681    ///
682    /// `d` is the direction of the ray and assumed to be normalized.
683    pub fn intersects_ray_from_center(&self, d: Vec2) -> Pos2 {
684        debug_assert!(
685            d.is_normalized(),
686            "expected normalized direction, but `d` has length {}",
687            d.length()
688        );
689
690        let mut tmin = f32::NEG_INFINITY;
691        let mut tmax = f32::INFINITY;
692
693        for i in 0..2 {
694            let inv_d = 1.0 / -d[i];
695            let mut t0 = (self.min[i] - self.center()[i]) * inv_d;
696            let mut t1 = (self.max[i] - self.center()[i]) * inv_d;
697
698            if inv_d < 0.0 {
699                std::mem::swap(&mut t0, &mut t1);
700            }
701
702            tmin = tmin.max(t0);
703            tmax = tmax.min(t1);
704        }
705
706        let t = tmax.min(tmin);
707        self.center() + t * -d
708    }
709}
710
711impl fmt::Debug for Rect {
712    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
713        write!(f, "[{:?} - {:?}]", self.min, self.max)
714    }
715}
716
717impl fmt::Display for Rect {
718    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
719        f.write_str("[")?;
720        self.min.fmt(f)?;
721        f.write_str(" - ")?;
722        self.max.fmt(f)?;
723        f.write_str("]")?;
724        Ok(())
725    }
726}
727
728/// from (min, max) or (left top, right bottom)
729impl From<[Pos2; 2]> for Rect {
730    #[inline]
731    fn from([min, max]: [Pos2; 2]) -> Self {
732        Self { min, max }
733    }
734}
735
736impl Mul<f32> for Rect {
737    type Output = Self;
738
739    #[inline]
740    fn mul(self, factor: f32) -> Self {
741        Self {
742            min: self.min * factor,
743            max: self.max * factor,
744        }
745    }
746}
747
748impl Mul<Rect> for f32 {
749    type Output = Rect;
750
751    #[inline]
752    fn mul(self, vec: Rect) -> Rect {
753        Rect {
754            min: self * vec.min,
755            max: self * vec.max,
756        }
757    }
758}
759
760impl Div<f32> for Rect {
761    type Output = Self;
762
763    #[inline]
764    fn div(self, factor: f32) -> Self {
765        Self {
766            min: self.min / factor,
767            max: self.max / factor,
768        }
769    }
770}
771
772#[cfg(test)]
773mod tests {
774    use super::*;
775
776    #[test]
777    fn test_rect() {
778        let r = Rect::from_min_max(pos2(10.0, 10.0), pos2(20.0, 20.0));
779        assert_eq!(r.distance_sq_to_pos(pos2(15.0, 15.0)), 0.0);
780        assert_eq!(r.distance_sq_to_pos(pos2(10.0, 15.0)), 0.0);
781        assert_eq!(r.distance_sq_to_pos(pos2(10.0, 10.0)), 0.0);
782
783        assert_eq!(r.distance_sq_to_pos(pos2(5.0, 15.0)), 25.0); // left of
784        assert_eq!(r.distance_sq_to_pos(pos2(25.0, 15.0)), 25.0); // right of
785        assert_eq!(r.distance_sq_to_pos(pos2(15.0, 5.0)), 25.0); // above
786        assert_eq!(r.distance_sq_to_pos(pos2(15.0, 25.0)), 25.0); // below
787        assert_eq!(r.distance_sq_to_pos(pos2(25.0, 5.0)), 50.0); // right and above
788    }
789
790    #[test]
791    fn scale_rect() {
792        let c = pos2(100.0, 50.0);
793        let r = Rect::from_center_size(c, vec2(30.0, 60.0));
794
795        assert_eq!(
796            r.scale_from_center(2.0),
797            Rect::from_center_size(c, vec2(60.0, 120.0))
798        );
799        assert_eq!(
800            r.scale_from_center(0.5),
801            Rect::from_center_size(c, vec2(15.0, 30.0))
802        );
803        assert_eq!(
804            r.scale_from_center2(vec2(2.0, 3.0)),
805            Rect::from_center_size(c, vec2(60.0, 180.0))
806        );
807    }
808
809    #[test]
810    fn test_ray_intersection() {
811        let rect = Rect::from_min_max(pos2(1.0, 1.0), pos2(3.0, 3.0));
812
813        println!("Righward ray from left:");
814        assert!(rect.intersects_ray(pos2(0.0, 2.0), Vec2::RIGHT));
815
816        println!("Righward ray from center:");
817        assert!(rect.intersects_ray(pos2(2.0, 2.0), Vec2::RIGHT));
818
819        println!("Righward ray from right:");
820        assert!(!rect.intersects_ray(pos2(4.0, 2.0), Vec2::RIGHT));
821
822        println!("Leftward ray from left:");
823        assert!(!rect.intersects_ray(pos2(0.0, 2.0), Vec2::LEFT));
824
825        println!("Leftward ray from center:");
826        assert!(rect.intersects_ray(pos2(2.0, 2.0), Vec2::LEFT));
827
828        println!("Leftward ray from right:");
829        assert!(rect.intersects_ray(pos2(4.0, 2.0), Vec2::LEFT));
830    }
831
832    #[test]
833    fn test_ray_from_center_intersection() {
834        let rect = Rect::from_min_max(pos2(1.0, 1.0), pos2(3.0, 3.0));
835
836        assert_eq!(
837            rect.intersects_ray_from_center(Vec2::RIGHT),
838            pos2(3.0, 2.0),
839            "rightward ray"
840        );
841
842        assert_eq!(
843            rect.intersects_ray_from_center(Vec2::UP),
844            pos2(2.0, 1.0),
845            "upward ray"
846        );
847
848        assert_eq!(
849            rect.intersects_ray_from_center(Vec2::LEFT),
850            pos2(1.0, 2.0),
851            "leftward ray"
852        );
853
854        assert_eq!(
855            rect.intersects_ray_from_center(Vec2::DOWN),
856            pos2(2.0, 3.0),
857            "downward ray"
858        );
859
860        assert_eq!(
861            rect.intersects_ray_from_center((Vec2::LEFT + Vec2::DOWN).normalized()),
862            pos2(1.0, 3.0),
863            "bottom-left corner ray"
864        );
865
866        assert_eq!(
867            rect.intersects_ray_from_center((Vec2::LEFT + Vec2::UP).normalized()),
868            pos2(1.0, 1.0),
869            "top-left corner ray"
870        );
871
872        assert_eq!(
873            rect.intersects_ray_from_center((Vec2::RIGHT + Vec2::DOWN).normalized()),
874            pos2(3.0, 3.0),
875            "bottom-right corner ray"
876        );
877
878        assert_eq!(
879            rect.intersects_ray_from_center((Vec2::RIGHT + Vec2::UP).normalized()),
880            pos2(3.0, 1.0),
881            "top-right corner ray"
882        );
883    }
884}