emath/
rect.rs

1use std::f32::INFINITY;
2use std::fmt;
3
4use crate::*;
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(-INFINITY, -INFINITY),
37        max: pos2(INFINITY, 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(INFINITY, INFINITY),
57        max: pos2(-INFINITY, -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    /// Shrink by this much in each direction, keeping the center
204    #[must_use]
205    pub fn shrink(self, amnt: f32) -> Self {
206        self.shrink2(Vec2::splat(amnt))
207    }
208
209    /// Shrink by this much in each direction, keeping the center
210    #[must_use]
211    pub fn shrink2(self, amnt: Vec2) -> Self {
212        Self::from_min_max(self.min + amnt, self.max - amnt)
213    }
214
215    #[must_use]
216    #[inline]
217    pub fn translate(self, amnt: Vec2) -> Self {
218        Self::from_min_size(self.min + amnt, self.size())
219    }
220
221    /// Rotate the bounds (will expand the [`Rect`])
222    #[must_use]
223    #[inline]
224    pub fn rotate_bb(self, rot: Rot2) -> Self {
225        let a = rot * self.left_top().to_vec2();
226        let b = rot * self.right_top().to_vec2();
227        let c = rot * self.left_bottom().to_vec2();
228        let d = rot * self.right_bottom().to_vec2();
229
230        Self::from_min_max(
231            a.min(b).min(c).min(d).to_pos2(),
232            a.max(b).max(c).max(d).to_pos2(),
233        )
234    }
235
236    #[must_use]
237    #[inline]
238    pub fn intersects(self, other: Self) -> bool {
239        self.min.x <= other.max.x
240            && other.min.x <= self.max.x
241            && self.min.y <= other.max.y
242            && other.min.y <= self.max.y
243    }
244
245    /// keep min
246    pub fn set_width(&mut self, w: f32) {
247        self.max.x = self.min.x + w;
248    }
249
250    /// keep min
251    pub fn set_height(&mut self, h: f32) {
252        self.max.y = self.min.y + h;
253    }
254
255    /// Keep size
256    pub fn set_center(&mut self, center: Pos2) {
257        *self = self.translate(center - self.center());
258    }
259
260    #[must_use]
261    #[inline(always)]
262    pub fn contains(&self, p: Pos2) -> bool {
263        self.min.x <= p.x && p.x <= self.max.x && self.min.y <= p.y && p.y <= self.max.y
264    }
265
266    #[must_use]
267    pub fn contains_rect(&self, other: Self) -> bool {
268        self.contains(other.min) && self.contains(other.max)
269    }
270
271    /// Return the given points clamped to be inside the rectangle
272    /// Panics if [`Self::is_negative`].
273    #[must_use]
274    pub fn clamp(&self, p: Pos2) -> Pos2 {
275        p.clamp(self.min, self.max)
276    }
277
278    #[inline(always)]
279    pub fn extend_with(&mut self, p: Pos2) {
280        self.min = self.min.min(p);
281        self.max = self.max.max(p);
282    }
283
284    #[inline(always)]
285    /// Expand to include the given x coordinate
286    pub fn extend_with_x(&mut self, x: f32) {
287        self.min.x = self.min.x.min(x);
288        self.max.x = self.max.x.max(x);
289    }
290
291    #[inline(always)]
292    /// Expand to include the given y coordinate
293    pub fn extend_with_y(&mut self, y: f32) {
294        self.min.y = self.min.y.min(y);
295        self.max.y = self.max.y.max(y);
296    }
297
298    /// The union of two bounding rectangle, i.e. the minimum [`Rect`]
299    /// that contains both input rectangles.
300    #[inline(always)]
301    #[must_use]
302    pub fn union(self, other: Self) -> Self {
303        Self {
304            min: self.min.min(other.min),
305            max: self.max.max(other.max),
306        }
307    }
308
309    /// The intersection of two [`Rect`], i.e. the area covered by both.
310    #[inline]
311    #[must_use]
312    pub fn intersect(self, other: Self) -> Self {
313        Self {
314            min: self.min.max(other.min),
315            max: self.max.min(other.max),
316        }
317    }
318
319    #[inline(always)]
320    pub fn center(&self) -> Pos2 {
321        Pos2 {
322            x: (self.min.x + self.max.x) / 2.0,
323            y: (self.min.y + self.max.y) / 2.0,
324        }
325    }
326
327    /// `rect.size() == Vec2 { x: rect.width(), y: rect.height() }`
328    #[inline(always)]
329    pub fn size(&self) -> Vec2 {
330        self.max - self.min
331    }
332
333    #[inline(always)]
334    pub fn width(&self) -> f32 {
335        self.max.x - self.min.x
336    }
337
338    #[inline(always)]
339    pub fn height(&self) -> f32 {
340        self.max.y - self.min.y
341    }
342
343    /// Width / height
344    ///
345    /// * `aspect_ratio < 1`: portrait / high
346    /// * `aspect_ratio = 1`: square
347    /// * `aspect_ratio > 1`: landscape / wide
348    pub fn aspect_ratio(&self) -> f32 {
349        self.width() / self.height()
350    }
351
352    /// `[2, 1]` for wide screen, and `[1, 2]` for portrait, etc.
353    /// At least one dimension = 1, the other >= 1
354    /// Returns the proportions required to letter-box a square view area.
355    pub fn square_proportions(&self) -> Vec2 {
356        let w = self.width();
357        let h = self.height();
358        if w > h {
359            vec2(w / h, 1.0)
360        } else {
361            vec2(1.0, h / w)
362        }
363    }
364
365    #[inline(always)]
366    pub fn area(&self) -> f32 {
367        self.width() * self.height()
368    }
369
370    /// The distance from the rect to the position.
371    ///
372    /// The distance is zero when the position is in the interior of the rectangle.
373    ///
374    /// [Negative rectangles](Self::is_negative) always return [`f32::INFINITY`].
375    #[inline]
376    pub fn distance_to_pos(&self, pos: Pos2) -> f32 {
377        self.distance_sq_to_pos(pos).sqrt()
378    }
379
380    /// The distance from the rect to the position, squared.
381    ///
382    /// The distance is zero when the position is in the interior of the rectangle.
383    ///
384    /// [Negative rectangles](Self::is_negative) always return [`f32::INFINITY`].
385    #[inline]
386    pub fn distance_sq_to_pos(&self, pos: Pos2) -> f32 {
387        if self.is_negative() {
388            return f32::INFINITY;
389        }
390
391        let dx = if self.min.x > pos.x {
392            self.min.x - pos.x
393        } else if pos.x > self.max.x {
394            pos.x - self.max.x
395        } else {
396            0.0
397        };
398
399        let dy = if self.min.y > pos.y {
400            self.min.y - pos.y
401        } else if pos.y > self.max.y {
402            pos.y - self.max.y
403        } else {
404            0.0
405        };
406
407        dx * dx + dy * dy
408    }
409
410    /// Signed distance to the edge of the box.
411    ///
412    /// Negative inside the box.
413    ///
414    /// [Negative rectangles](Self::is_negative) always return [`f32::INFINITY`].
415    ///
416    /// ```
417    /// # use emath::{pos2, Rect};
418    /// let rect = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0));
419    /// assert_eq!(rect.signed_distance_to_pos(pos2(0.50, 0.50)), -0.50);
420    /// assert_eq!(rect.signed_distance_to_pos(pos2(0.75, 0.50)), -0.25);
421    /// assert_eq!(rect.signed_distance_to_pos(pos2(1.50, 0.50)), 0.50);
422    /// ```
423    pub fn signed_distance_to_pos(&self, pos: Pos2) -> f32 {
424        if self.is_negative() {
425            return f32::INFINITY;
426        }
427
428        let edge_distances = (pos - self.center()).abs() - self.size() * 0.5;
429        let inside_dist = edge_distances.max_elem().min(0.0);
430        let outside_dist = edge_distances.max(Vec2::ZERO).length();
431        inside_dist + outside_dist
432    }
433
434    /// Linearly interpolate so that `[0, 0]` is [`Self::min`] and
435    /// `[1, 1]` is [`Self::max`].
436    #[inline]
437    pub fn lerp_inside(&self, t: Vec2) -> Pos2 {
438        Pos2 {
439            x: lerp(self.min.x..=self.max.x, t.x),
440            y: lerp(self.min.y..=self.max.y, t.y),
441        }
442    }
443
444    /// Linearly self towards other rect.
445    #[inline]
446    pub fn lerp_towards(&self, other: &Self, t: f32) -> Self {
447        Self {
448            min: self.min.lerp(other.min, t),
449            max: self.max.lerp(other.max, t),
450        }
451    }
452
453    #[inline(always)]
454    pub fn x_range(&self) -> Rangef {
455        Rangef::new(self.min.x, self.max.x)
456    }
457
458    #[inline(always)]
459    pub fn y_range(&self) -> Rangef {
460        Rangef::new(self.min.y, self.max.y)
461    }
462
463    #[inline(always)]
464    pub fn bottom_up_range(&self) -> Rangef {
465        Rangef::new(self.max.y, self.min.y)
466    }
467
468    /// `width < 0 || height < 0`
469    #[inline(always)]
470    pub fn is_negative(&self) -> bool {
471        self.max.x < self.min.x || self.max.y < self.min.y
472    }
473
474    /// `width > 0 && height > 0`
475    #[inline(always)]
476    pub fn is_positive(&self) -> bool {
477        self.min.x < self.max.x && self.min.y < self.max.y
478    }
479
480    /// True if all members are also finite.
481    #[inline(always)]
482    pub fn is_finite(&self) -> bool {
483        self.min.is_finite() && self.max.is_finite()
484    }
485
486    /// True if any member is NaN.
487    #[inline(always)]
488    pub fn any_nan(self) -> bool {
489        self.min.any_nan() || self.max.any_nan()
490    }
491}
492
493/// ## Convenience functions (assumes origin is towards left top):
494impl Rect {
495    /// `min.x`
496    #[inline(always)]
497    pub fn left(&self) -> f32 {
498        self.min.x
499    }
500
501    /// `min.x`
502    #[inline(always)]
503    pub fn left_mut(&mut self) -> &mut f32 {
504        &mut self.min.x
505    }
506
507    /// `min.x`
508    #[inline(always)]
509    pub fn set_left(&mut self, x: f32) {
510        self.min.x = x;
511    }
512
513    /// `max.x`
514    #[inline(always)]
515    pub fn right(&self) -> f32 {
516        self.max.x
517    }
518
519    /// `max.x`
520    #[inline(always)]
521    pub fn right_mut(&mut self) -> &mut f32 {
522        &mut self.max.x
523    }
524
525    /// `max.x`
526    #[inline(always)]
527    pub fn set_right(&mut self, x: f32) {
528        self.max.x = x;
529    }
530
531    /// `min.y`
532    #[inline(always)]
533    pub fn top(&self) -> f32 {
534        self.min.y
535    }
536
537    /// `min.y`
538    #[inline(always)]
539    pub fn top_mut(&mut self) -> &mut f32 {
540        &mut self.min.y
541    }
542
543    /// `min.y`
544    #[inline(always)]
545    pub fn set_top(&mut self, y: f32) {
546        self.min.y = y;
547    }
548
549    /// `max.y`
550    #[inline(always)]
551    pub fn bottom(&self) -> f32 {
552        self.max.y
553    }
554
555    /// `max.y`
556    #[inline(always)]
557    pub fn bottom_mut(&mut self) -> &mut f32 {
558        &mut self.max.y
559    }
560
561    /// `max.y`
562    #[inline(always)]
563    pub fn set_bottom(&mut self, y: f32) {
564        self.max.y = y;
565    }
566
567    #[inline(always)]
568    #[doc(alias = "top_left")]
569    pub fn left_top(&self) -> Pos2 {
570        pos2(self.left(), self.top())
571    }
572
573    #[inline(always)]
574    pub fn center_top(&self) -> Pos2 {
575        pos2(self.center().x, self.top())
576    }
577
578    #[inline(always)]
579    #[doc(alias = "top_right")]
580    pub fn right_top(&self) -> Pos2 {
581        pos2(self.right(), self.top())
582    }
583
584    #[inline(always)]
585    pub fn left_center(&self) -> Pos2 {
586        pos2(self.left(), self.center().y)
587    }
588
589    #[inline(always)]
590    pub fn right_center(&self) -> Pos2 {
591        pos2(self.right(), self.center().y)
592    }
593
594    #[inline(always)]
595    #[doc(alias = "bottom_left")]
596    pub fn left_bottom(&self) -> Pos2 {
597        pos2(self.left(), self.bottom())
598    }
599
600    #[inline(always)]
601    pub fn center_bottom(&self) -> Pos2 {
602        pos2(self.center().x, self.bottom())
603    }
604
605    #[inline(always)]
606    #[doc(alias = "bottom_right")]
607    pub fn right_bottom(&self) -> Pos2 {
608        pos2(self.right(), self.bottom())
609    }
610
611    /// Split rectangle in left and right halves. `t` is expected to be in the (0,1) range.
612    pub fn split_left_right_at_fraction(&self, t: f32) -> (Self, Self) {
613        self.split_left_right_at_x(lerp(self.min.x..=self.max.x, t))
614    }
615
616    /// Split rectangle in left and right halves at the given `x` coordinate.
617    pub fn split_left_right_at_x(&self, split_x: f32) -> (Self, Self) {
618        let left = Self::from_min_max(self.min, Pos2::new(split_x, self.max.y));
619        let right = Self::from_min_max(Pos2::new(split_x, self.min.y), self.max);
620        (left, right)
621    }
622
623    /// Split rectangle in top and bottom halves. `t` is expected to be in the (0,1) range.
624    pub fn split_top_bottom_at_fraction(&self, t: f32) -> (Self, Self) {
625        self.split_top_bottom_at_y(lerp(self.min.y..=self.max.y, t))
626    }
627
628    /// Split rectangle in top and bottom halves at the given `y` coordinate.
629    pub fn split_top_bottom_at_y(&self, split_y: f32) -> (Self, Self) {
630        let top = Self::from_min_max(self.min, Pos2::new(self.max.x, split_y));
631        let bottom = Self::from_min_max(Pos2::new(self.min.x, split_y), self.max);
632        (top, bottom)
633    }
634}
635
636impl Rect {
637    /// Does this Rect intersect the given ray (where `d` is normalized)?
638    ///
639    /// A ray that starts inside the rect will return `true`.
640    pub fn intersects_ray(&self, o: Pos2, d: Vec2) -> bool {
641        let mut tmin = -f32::INFINITY;
642        let mut tmax = f32::INFINITY;
643
644        if d.x != 0.0 {
645            let tx1 = (self.min.x - o.x) / d.x;
646            let tx2 = (self.max.x - o.x) / d.x;
647
648            tmin = tmin.max(tx1.min(tx2));
649            tmax = tmax.min(tx1.max(tx2));
650        }
651
652        if d.y != 0.0 {
653            let ty1 = (self.min.y - o.y) / d.y;
654            let ty2 = (self.max.y - o.y) / d.y;
655
656            tmin = tmin.max(ty1.min(ty2));
657            tmax = tmax.min(ty1.max(ty2));
658        }
659
660        0.0 <= tmax && tmin <= tmax
661    }
662}
663
664impl fmt::Debug for Rect {
665    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
666        write!(f, "[{:?} - {:?}]", self.min, self.max)
667    }
668}
669
670impl fmt::Display for Rect {
671    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
672        f.write_str("[")?;
673        self.min.fmt(f)?;
674        f.write_str(" - ")?;
675        self.max.fmt(f)?;
676        f.write_str("]")?;
677        Ok(())
678    }
679}
680
681/// from (min, max) or (left top, right bottom)
682impl From<[Pos2; 2]> for Rect {
683    #[inline]
684    fn from([min, max]: [Pos2; 2]) -> Self {
685        Self { min, max }
686    }
687}
688
689impl Mul<f32> for Rect {
690    type Output = Self;
691
692    #[inline]
693    fn mul(self, factor: f32) -> Self {
694        Self {
695            min: self.min * factor,
696            max: self.max * factor,
697        }
698    }
699}
700
701impl Mul<Rect> for f32 {
702    type Output = Rect;
703
704    #[inline]
705    fn mul(self, vec: Rect) -> Rect {
706        Rect {
707            min: self * vec.min,
708            max: self * vec.max,
709        }
710    }
711}
712
713impl Div<f32> for Rect {
714    type Output = Self;
715
716    #[inline]
717    fn div(self, factor: f32) -> Self {
718        Self {
719            min: self.min / factor,
720            max: self.max / factor,
721        }
722    }
723}
724
725#[cfg(test)]
726mod tests {
727    use super::*;
728
729    #[test]
730    fn test_rect() {
731        let r = Rect::from_min_max(pos2(10.0, 10.0), pos2(20.0, 20.0));
732        assert_eq!(r.distance_sq_to_pos(pos2(15.0, 15.0)), 0.0);
733        assert_eq!(r.distance_sq_to_pos(pos2(10.0, 15.0)), 0.0);
734        assert_eq!(r.distance_sq_to_pos(pos2(10.0, 10.0)), 0.0);
735
736        assert_eq!(r.distance_sq_to_pos(pos2(5.0, 15.0)), 25.0); // left of
737        assert_eq!(r.distance_sq_to_pos(pos2(25.0, 15.0)), 25.0); // right of
738        assert_eq!(r.distance_sq_to_pos(pos2(15.0, 5.0)), 25.0); // above
739        assert_eq!(r.distance_sq_to_pos(pos2(15.0, 25.0)), 25.0); // below
740        assert_eq!(r.distance_sq_to_pos(pos2(25.0, 5.0)), 50.0); // right and above
741    }
742
743    #[test]
744    fn test_ray_intersection() {
745        let rect = Rect::from_min_max(pos2(1.0, 1.0), pos2(3.0, 3.0));
746
747        eprintln!("Righward ray from left:");
748        assert!(rect.intersects_ray(pos2(0.0, 2.0), Vec2::RIGHT));
749
750        eprintln!("Righward ray from center:");
751        assert!(rect.intersects_ray(pos2(2.0, 2.0), Vec2::RIGHT));
752
753        eprintln!("Righward ray from right:");
754        assert!(!rect.intersects_ray(pos2(4.0, 2.0), Vec2::RIGHT));
755
756        eprintln!("Leftward ray from left:");
757        assert!(!rect.intersects_ray(pos2(0.0, 2.0), Vec2::LEFT));
758
759        eprintln!("Leftward ray from center:");
760        assert!(rect.intersects_ray(pos2(2.0, 2.0), Vec2::LEFT));
761
762        eprintln!("Leftward ray from right:");
763        assert!(rect.intersects_ray(pos2(4.0, 2.0), Vec2::LEFT));
764    }
765}