use std::ops::{Add, Neg, Sub};
#[derive(Clone, Copy, Debug)]
pub struct BoundingBox<T: Copy> {
pub x0: T,
pub y0: T,
pub x1: T,
pub y1: T,
}
impl<T> BoundingBox<T>
where
T: Copy + PartialOrd + Neg<Output = T>,
{
pub fn new(x0: T, y0: T, x1: T, y1: T) -> Self {
BoundingBox { x0, y0, x1, y1 }
}
}
pub trait Intersection {
fn intersects(&self, other: &Self) -> bool;
}
#[derive(Debug, Clone, Copy)]
pub struct RotatedRect {
pub x: f32,
pub y: f32,
pub w: f32,
pub h: f32,
pub a: f32,
}
#[derive(Debug, Clone, Copy)]
struct Vector(f32, f32);
impl Vector {
fn rotate(&self, a: f32) -> Self {
let cosa = a.cos();
let sina = a.sin();
Vector(
self.0 * cosa + self.1 * sina,
-self.0 * sina + self.1 * cosa,
)
}
}
impl Add<Vector> for Vector {
type Output = Self;
fn add(self, other: Self) -> Self {
Vector(self.0 + other.0, self.1 + other.1)
}
}
impl Neg for Vector {
type Output = Self;
fn neg(self) -> Self {
Vector(-self.0, -self.1)
}
}
impl Sub<Vector> for Vector {
type Output = Self;
fn sub(self, other: Self) -> Self {
Vector(self.0 - other.0, self.1 - other.1)
}
}
impl From<&RotatedRect> for BoundingBox<f32> {
fn from(rect: &RotatedRect) -> Self {
let c = rect.a.cos().abs();
let s = rect.a.sin().abs();
let ex = rect.w / 2.0;
let ey = rect.h / 2.0;
let x_radius = ex * c + ey * s;
let y_radius = ex * s + ey * c;
BoundingBox::<f32>::new(
rect.x - x_radius,
rect.y - y_radius,
rect.x + x_radius,
rect.y + y_radius,
)
}
}
pub fn intersects<T>(item: &T, other: &T) -> bool
where
T: Intersection,
{
item.intersects(other)
}
impl Intersection for RotatedRect {
fn intersects(&self, other: &Self) -> bool {
let ang = self.a - other.a;
let cosa = ang.cos();
let sina = ang.sin();
let c = Vector(other.x, other.y) - Vector(self.x, self.y);
let c = c.rotate(other.a);
let size = Vector(other.w / 2.0, other.h / 2.0);
let bl = c - size;
let tr = c + size;
let (a, b) = {
let cost = self.w / 2.0 * cosa;
let sint = self.w / 2.0 * sina;
let ax = -self.h / 2.0 * sina;
let ay = self.h / 2.0 * cosa;
(Vector(ax + cost, ay + sint), Vector(ax - cost, ay - sint))
};
let t = sina * cosa;
let (a, b) = {
if t < 0.0 {
(b, a)
} else {
(a, b)
}
};
let b = if sina < 0.0 { -b } else { b };
if b.0 > tr.0 || b.0 > -(bl.0) {
return false;
}
let mut ext1 = a.1;
let mut ext2 = -a.1;
if t != 0.0 {
{
let mut dx1 = bl.0 - a.0;
let dx2 = tr.0 - a.0;
if dx1 * dx2 > 0.0 {
let mut dx = a.0;
if dx1 < 0.0 {
dx -= b.0;
ext1 -= b.1;
dx1 = dx2;
} else {
dx += b.0;
ext1 += b.1;
}
ext1 *= dx1;
ext1 /= dx;
ext1 += a.1;
}
}
{
let mut dx1 = bl.0 + a.0;
let dx2 = tr.0 + a.0;
if dx1 * dx2 > 0.0 {
let mut dx = -a.0;
if dx1 < 0.0 {
dx -= b.0;
ext2 -= b.1;
dx1 = dx2;
} else {
dx += b.0;
ext2 += b.1;
}
ext2 *= dx1;
ext2 /= dx;
ext2 -= a.1;
}
}
}
let (ext1, ext2) = if ext1 > ext2 {
(ext2, ext1)
} else {
(ext1, ext2)
};
!((ext1 < bl.1 && ext2 < bl.1) || (ext1 > tr.1 && ext2 > tr.1))
}
}