use crate::math::vect::Vect;
use std::mem;
use crate::math::{Intersection, IntersectionPoints};
use crate::math::circle::Circ;
use crate::Ray;
#[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};
#[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 },
}
}
#[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 },
}
}
#[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 },
}
}
#[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 },
}
}
#[inline]
pub fn from_vec(v: Vect) -> Rect {
Rect {
min: Vect::ZERO,
max: v,
}
}
#[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
}
#[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 }
]
}
#[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) },
}
}
#[inline]
pub fn center(&self) -> Vect {
self.min + (self.max - self.min) / 2f32
}
#[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
}
#[inline]
pub fn to_local(&self) -> Rect {
Self::centered(Vect::ZERO, self.width(), self.height())
}
#[inline]
pub fn width(&self) -> f32 {
self.max.x - self.min.x
}
#[inline]
pub fn height(&self) -> f32 {
self.max.y - self.min.y
}
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
}
#[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
}
#[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
}
#[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
}
#[inline]
pub fn radius(&self) -> f32 {
(self.max - self.min).len() / 2f32
}
#[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 {
#[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 {
#[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 {
#[inline]
fn intersects(&self, o: &Ray) -> bool {
o.intersects(self)
}
}
impl IntersectionPoints<Ray> for Rect {
#[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)));
}
}