1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
use crate::math::vect::Vect;
use std::mem;
use crate::math::{Intersection, IntersectionPoints};
use crate::math::circle::Circ;
use crate::Ray;


/// Rect is Rectangular shape or AABB for detecting collisions in 2D space

#[derive(Copy, Clone, Debug)]
pub struct Rect {
    pub min: Vect,
    pub max: Vect,
}

pub enum Sides {
    Right, Left, Top, Bottom, In
}

impl Rect {
    pub const ZERO: Rect = Rect{min: Vect::ZERO, max: Vect::ZERO};
    pub const INVERTED_MAX_RECT: Rect = Rect{min: Vect::MAX, max: Vect::MIN};
    pub const MAX_RECT: Rect = Rect{min: Vect::MIN, max: Vect::MAX};

    /// new is rect constructor

    #[inline]
    pub fn new(x0: f32, y0: f32, x1: f32, y1: f32) -> Rect {
        Rect {
            min: Vect { x: x0, y: y0 },
            max: Vect { x: x1, y: y1 },
        }
    }

    /// wn lets you specify width and height of rectangle rather then two points

    #[inline]
    pub fn wh(x: f32, y: f32, w: f32, h: f32) -> Rect {
        Rect {
            min: Vect { x, y },
            max: Vect { x: x + w, y: y + h },
        }
    }

    /// centered returns rect that has center in c

    #[inline]
    pub fn centered(c: Vect, mut w: f32, mut h: f32) -> Rect {
        w /= 2f32;
        h /= 2f32;
        Rect {
            min: Vect { x: c.x - w, y: c.y - h },
            max: Vect { x: c.x + w, y: c.y + h },
        }
    }

    /// cube is similar to centered but you specify just radius of cube

    /// with means that if rad is 5 then width and height are 10

    #[inline]
    pub fn cube(c: Vect, rad: f32) -> Rect {
        Rect {
            min: Vect { x: c.x - rad, y: c.y - rad },
            max: Vect { x: c.x + rad, y: c.y + rad },
        }
    }

    /// from_vec turns vector to rectangle. This works properly only for purely positive otherwise

    /// normalization is required

    #[inline]
    pub fn from_vec(v: Vect) -> Rect {
        Rect {
            min: Vect::ZERO,
            max: v,
        }
    }

    /// bounds for returns rectangle where all points from slice fits in

    #[inline]
    pub fn bounds_for(points: &[Vect]) -> Rect {
        if points.is_empty() {
            return Self::ZERO;
        }

        let mut bounds = Self::INVERTED_MAX_RECT;

        for p in points {
            if p.x > bounds.max.x {
                bounds.max.x = p.x;
            }
            if p.x < bounds.min.x {
                bounds.min.x = p.x;
            }
            if p.y > bounds.max.y {
                bounds.max.y = p.y;
            }
            if p.y < bounds.min.y {
                bounds.min.y = p.y;
            }
        }

        bounds
    }

    /// verts returns corner points of rectangle

    #[inline]
    pub fn verts(&self) -> [Vect; 4] {
        [
            self.min,
            Vect { x: self.min.x, y: self.max.y },
            self.max,
            Vect { x: self.max.x, y: self.min.y }
        ]
    }

    /// union returns smallest rectangle in witch both self and o fits in

    #[inline]
    pub fn union(&self, o: &Rect) -> Rect {
        Rect {
            min: Vect { x: self.min.x.min(o.min.x), y: self.min.y.min(o.min.y) },
            max: Vect { x: self.max.x.max(o.max.x), y: self.max.y.max(o.max.y) },
        }
    }

    /// center returns center of rectangle

    #[inline]
    pub fn center(&self) -> Vect {
        self.min + (self.max - self.min) / 2f32
    }

    /// loc_verts returns corners of rectangle relative to its center

    #[inline]
    pub fn loc_verts(&self) -> [Vect; 4] {
        let c = self.center();
        let mut verts = self.verts();
        for i in 0..4 {
            verts[i] -= c;
        }
        verts
    }

    /// to_local returns rect centered around coordinate origin (0, 0)

    #[inline]
    pub fn to_local(&self) -> Rect {
        Self::centered(Vect::ZERO, self.width(), self.height())
    }

    /// width returns rectangles width

    #[inline]
    pub fn width(&self) -> f32 {
        self.max.x - self.min.x
    }

    /// height returns rectangles height

    #[inline]
    pub fn height(&self) -> f32 {
        self.max.y - self.min.y
    }

    /// respective returns where the point is respective to rectangle

    pub fn respective(&self, pos: Vect) -> Sides {
        if pos.x < self.min.x {
            return Sides::Left;
        }
        if pos.x > self.max.x {
            return Sides::Right;
        }
        if pos.y < self.min.y {
            return Sides::Bottom;
        }
        if pos.y > self.max.y {
            return Sides::Top;
        }
        Sides::In
    }

    /// normalized normalizes rectangle so the min is lower them max

    #[inline]
    pub fn normalized(mut self) -> Rect {
        if self.min.x > self.max.x {
            mem::swap(&mut self.min.x , &mut self.max.x)
        }

        if self.min.y > self.max.y {
            mem::swap(&mut self.min.y , &mut self.max.y)
        }

        self
    }

    /// contains returns whether rect contains the points

    #[inline]
    pub fn contains(&self, pos: Vect) -> bool {
        self.max.x > pos.x && self.min.x < pos.x && self.max.y > pos.y && self.min.y < pos.y
    }

    /// fits_in returns whether self fits in o

    #[inline]
    pub fn fits_in(&self, o: &Rect) -> bool {
        self.max.x <= o.max.x && self.max.y <= o.max.y && o.min.x <= self.min.x && o.min.y <= self.min.y
    }

    /// radius returns distance from rect center to max

    #[inline]
    pub fn radius(&self) -> f32 {
        (self.max - self.min).len() / 2f32
    }

    /// moved returns rectangle moved by delta

    #[inline]
    pub fn moved(&self, delta: Vect) -> Self {
        Rect {
            min: self.min + delta,
            max: self.max + delta,
        }
    }

    #[inline]
    pub fn centered_to(&self, pos: Vect) -> Self {
        Self::centered(pos, self.width(), self.height())
    }

    #[inline]
    pub fn area(&self) -> f32 {
        self.width() * self.height()
    }

    pub fn to_rays(&self) -> [Ray; 4] {
        let verts = self.verts();
        [
            Ray::from_points(verts[0], verts[1]),
            Ray::from_points(verts[1], verts[2]),
            Ray::from_points(verts[2], verts[3]),
            Ray::from_points(verts[3], verts[0]),
        ]
    }
}

impl Intersection<Rect> for Rect {
    /// returns whether rectangle intersects another rectangle

    #[inline]
    fn intersects(&self, o: &Rect) -> bool {
        !(self.max.x < o.min.x || self.max.y < o.min.y || o.max.x < self.min.x || o.max.y < self.min.y)
    }
}

impl Intersection<Circ> for Rect {
    /// returns whether rectangle intersects circle

    #[inline]
    fn intersects(&self, o: &Circ) -> bool {
        let left =      self.min.x > o.c.x;
        let right =     self.max.x < o.c.x;
        let bottom =    self.min.y > o.c.y;
        let top =       self.max.y < o.c.y;

        if !left && !right {
            return !(self.min.y > o.c.y + o.r || self.max.y < o.c.y - o.r)
        } else if !top && !bottom {
            return !(self.min.x > o.c.x + o.r || self.max.x < o.c.x - o.r)
        } else if left && bottom {
            return o.contains(self.min)
        } else if right && top {
            return o.contains(self.max)
        } else if right && bottom {
            return o.contains(Vect::new(self.max.x, self.min.y))
        } else if left && top {
            return o.contains(Vect::new(self.min.x, self.max.y))
        }

        false
    }
}

impl Intersection<Ray> for Rect {
    /// returns whether rectangle intersects ray

    #[inline]
    fn intersects(&self, o: &Ray) -> bool {
        o.intersects(self)
    }
}

impl IntersectionPoints<Ray> for Rect {
    /// returns intersection points of rectangle and ray

    #[inline]
    fn intersects_points(&self, o: &Ray) -> [Option<Vect>; 2] {
        o.intersects_points(self)
    }
}

impl Default for Rect {
    fn default() -> Self {
        Rect::ZERO
    }
}

#[cfg(test)]
mod tests {
    use crate::math::rect::Rect;
    use crate::math::Intersection;
    use crate::Vect;
    use crate::math::circle::Circ;

    #[test]
    fn intersects_test() {
        let base = Rect::new(0f32, 0f32, 10f32, 10f32);
        assert!(base.intersects(&base));
        assert!(base.intersects(&Rect::new(10f32, 10f32, 100f32, 100f32)));
        assert!(!base.intersects(&Rect::new(100f32, 100f32, 1000f32, 1000f32)));
    }

    #[test]
    fn intersects_circle_test() {
        let base = rect!(0, 0, 100, 100);
        assert!(base.intersects(&circ!(0, 0; 0)));
        assert!(base.intersects(&circ!(10, 10; 10)));
        assert!(base.intersects(&circ!(-10, -10; 10f32.hypot(10.0))));
        assert!(base.intersects(&circ!(110, 50; 10)));
        assert!(!base.intersects(&circ!(-10, -10; 10.0)));

    }
}