rustbatch/math/
rect.rs

1use crate::math::vect::Vect;
2use std::mem;
3use crate::math::{Intersection, IntersectionPoints};
4use crate::math::circle::Circ;
5use crate::Ray;
6
7
8/// Rect is Rectangular shape or AABB for detecting collisions in 2D space
9#[derive(Copy, Clone, Debug)]
10pub struct Rect {
11    pub min: Vect,
12    pub max: Vect,
13}
14
15pub enum Sides {
16    Right, Left, Top, Bottom, In
17}
18
19impl Rect {
20    pub const ZERO: Rect = Rect{min: Vect::ZERO, max: Vect::ZERO};
21    pub const INVERTED_MAX_RECT: Rect = Rect{min: Vect::MAX, max: Vect::MIN};
22    pub const MAX_RECT: Rect = Rect{min: Vect::MIN, max: Vect::MAX};
23
24    /// new is rect constructor
25    #[inline]
26    pub fn new(x0: f32, y0: f32, x1: f32, y1: f32) -> Rect {
27        Rect {
28            min: Vect { x: x0, y: y0 },
29            max: Vect { x: x1, y: y1 },
30        }
31    }
32
33    /// wn lets you specify width and height of rectangle rather then two points
34    #[inline]
35    pub fn wh(x: f32, y: f32, w: f32, h: f32) -> Rect {
36        Rect {
37            min: Vect { x, y },
38            max: Vect { x: x + w, y: y + h },
39        }
40    }
41
42    /// centered returns rect that has center in c
43    #[inline]
44    pub fn centered(c: Vect, mut w: f32, mut h: f32) -> Rect {
45        w /= 2f32;
46        h /= 2f32;
47        Rect {
48            min: Vect { x: c.x - w, y: c.y - h },
49            max: Vect { x: c.x + w, y: c.y + h },
50        }
51    }
52
53    /// cube is similar to centered but you specify just radius of cube
54    /// with means that if rad is 5 then width and height are 10
55    #[inline]
56    pub fn cube(c: Vect, rad: f32) -> Rect {
57        Rect {
58            min: Vect { x: c.x - rad, y: c.y - rad },
59            max: Vect { x: c.x + rad, y: c.y + rad },
60        }
61    }
62
63    /// from_vec turns vector to rectangle. This works properly only for purely positive otherwise
64    /// normalization is required
65    #[inline]
66    pub fn from_vec(v: Vect) -> Rect {
67        Rect {
68            min: Vect::ZERO,
69            max: v,
70        }
71    }
72
73    /// bounds for returns rectangle where all points from slice fits in
74    #[inline]
75    pub fn bounds_for(points: &[Vect]) -> Rect {
76        if points.is_empty() {
77            return Self::ZERO;
78        }
79
80        let mut bounds = Self::INVERTED_MAX_RECT;
81
82        for p in points {
83            if p.x > bounds.max.x {
84                bounds.max.x = p.x;
85            }
86            if p.x < bounds.min.x {
87                bounds.min.x = p.x;
88            }
89            if p.y > bounds.max.y {
90                bounds.max.y = p.y;
91            }
92            if p.y < bounds.min.y {
93                bounds.min.y = p.y;
94            }
95        }
96
97        bounds
98    }
99
100    /// verts returns corner points of rectangle
101    #[inline]
102    pub fn verts(&self) -> [Vect; 4] {
103        [
104            self.min,
105            Vect { x: self.min.x, y: self.max.y },
106            self.max,
107            Vect { x: self.max.x, y: self.min.y }
108        ]
109    }
110
111    /// union returns smallest rectangle in witch both self and o fits in
112    #[inline]
113    pub fn union(&self, o: &Rect) -> Rect {
114        Rect {
115            min: Vect { x: self.min.x.min(o.min.x), y: self.min.y.min(o.min.y) },
116            max: Vect { x: self.max.x.max(o.max.x), y: self.max.y.max(o.max.y) },
117        }
118    }
119
120    /// center returns center of rectangle
121    #[inline]
122    pub fn center(&self) -> Vect {
123        self.min + (self.max - self.min) / 2f32
124    }
125
126    /// loc_verts returns corners of rectangle relative to its center
127    #[inline]
128    pub fn loc_verts(&self) -> [Vect; 4] {
129        let c = self.center();
130        let mut verts = self.verts();
131        for i in 0..4 {
132            verts[i] -= c;
133        }
134        verts
135    }
136
137    /// to_local returns rect centered around coordinate origin (0, 0)
138    #[inline]
139    pub fn to_local(&self) -> Rect {
140        Self::centered(Vect::ZERO, self.width(), self.height())
141    }
142
143    /// width returns rectangles width
144    #[inline]
145    pub fn width(&self) -> f32 {
146        self.max.x - self.min.x
147    }
148
149    /// height returns rectangles height
150    #[inline]
151    pub fn height(&self) -> f32 {
152        self.max.y - self.min.y
153    }
154
155    /// respective returns where the point is respective to rectangle
156    pub fn respective(&self, pos: Vect) -> Sides {
157        if pos.x < self.min.x {
158            return Sides::Left;
159        }
160        if pos.x > self.max.x {
161            return Sides::Right;
162        }
163        if pos.y < self.min.y {
164            return Sides::Bottom;
165        }
166        if pos.y > self.max.y {
167            return Sides::Top;
168        }
169        Sides::In
170    }
171
172    /// normalized normalizes rectangle so the min is lower them max
173    #[inline]
174    pub fn normalized(mut self) -> Rect {
175        if self.min.x > self.max.x {
176            mem::swap(&mut self.min.x , &mut self.max.x)
177        }
178
179        if self.min.y > self.max.y {
180            mem::swap(&mut self.min.y , &mut self.max.y)
181        }
182
183        self
184    }
185
186    /// contains returns whether rect contains the points
187    #[inline]
188    pub fn contains(&self, pos: Vect) -> bool {
189        self.max.x > pos.x && self.min.x < pos.x && self.max.y > pos.y && self.min.y < pos.y
190    }
191
192    /// fits_in returns whether self fits in o
193    #[inline]
194    pub fn fits_in(&self, o: &Rect) -> bool {
195        self.max.x <= o.max.x && self.max.y <= o.max.y && o.min.x <= self.min.x && o.min.y <= self.min.y
196    }
197
198    /// radius returns distance from rect center to max
199    #[inline]
200    pub fn radius(&self) -> f32 {
201        (self.max - self.min).len() / 2f32
202    }
203
204    /// moved returns rectangle moved by delta
205    #[inline]
206    pub fn moved(&self, delta: Vect) -> Self {
207        Rect {
208            min: self.min + delta,
209            max: self.max + delta,
210        }
211    }
212
213    #[inline]
214    pub fn centered_to(&self, pos: Vect) -> Self {
215        Self::centered(pos, self.width(), self.height())
216    }
217
218    #[inline]
219    pub fn area(&self) -> f32 {
220        self.width() * self.height()
221    }
222
223    pub fn to_rays(&self) -> [Ray; 4] {
224        let verts = self.verts();
225        [
226            Ray::from_points(verts[0], verts[1]),
227            Ray::from_points(verts[1], verts[2]),
228            Ray::from_points(verts[2], verts[3]),
229            Ray::from_points(verts[3], verts[0]),
230        ]
231    }
232}
233
234impl Intersection<Rect> for Rect {
235    /// returns whether rectangle intersects another rectangle
236    #[inline]
237    fn intersects(&self, o: &Rect) -> bool {
238        !(self.max.x < o.min.x || self.max.y < o.min.y || o.max.x < self.min.x || o.max.y < self.min.y)
239    }
240}
241
242impl Intersection<Circ> for Rect {
243    /// returns whether rectangle intersects circle
244    #[inline]
245    fn intersects(&self, o: &Circ) -> bool {
246        let left =      self.min.x > o.c.x;
247        let right =     self.max.x < o.c.x;
248        let bottom =    self.min.y > o.c.y;
249        let top =       self.max.y < o.c.y;
250
251        if !left && !right {
252            return !(self.min.y > o.c.y + o.r || self.max.y < o.c.y - o.r)
253        } else if !top && !bottom {
254            return !(self.min.x > o.c.x + o.r || self.max.x < o.c.x - o.r)
255        } else if left && bottom {
256            return o.contains(self.min)
257        } else if right && top {
258            return o.contains(self.max)
259        } else if right && bottom {
260            return o.contains(Vect::new(self.max.x, self.min.y))
261        } else if left && top {
262            return o.contains(Vect::new(self.min.x, self.max.y))
263        }
264
265        false
266    }
267}
268
269impl Intersection<Ray> for Rect {
270    /// returns whether rectangle intersects ray
271    #[inline]
272    fn intersects(&self, o: &Ray) -> bool {
273        o.intersects(self)
274    }
275}
276
277impl IntersectionPoints<Ray> for Rect {
278    /// returns intersection points of rectangle and ray
279    #[inline]
280    fn intersects_points(&self, o: &Ray) -> [Option<Vect>; 2] {
281        o.intersects_points(self)
282    }
283}
284
285impl Default for Rect {
286    fn default() -> Self {
287        Rect::ZERO
288    }
289}
290
291#[cfg(test)]
292mod tests {
293    use crate::math::rect::Rect;
294    use crate::math::Intersection;
295    use crate::Vect;
296    use crate::math::circle::Circ;
297
298    #[test]
299    fn intersects_test() {
300        let base = Rect::new(0f32, 0f32, 10f32, 10f32);
301        assert!(base.intersects(&base));
302        assert!(base.intersects(&Rect::new(10f32, 10f32, 100f32, 100f32)));
303        assert!(!base.intersects(&Rect::new(100f32, 100f32, 1000f32, 1000f32)));
304    }
305
306    #[test]
307    fn intersects_circle_test() {
308        let base = rect!(0, 0, 100, 100);
309        assert!(base.intersects(&circ!(0, 0; 0)));
310        assert!(base.intersects(&circ!(10, 10; 10)));
311        assert!(base.intersects(&circ!(-10, -10; 10f32.hypot(10.0))));
312        assert!(base.intersects(&circ!(110, 50; 10)));
313        assert!(!base.intersects(&circ!(-10, -10; 10.0)));
314
315    }
316}