use super::{Abs, NonNeg, Point, Size, Sq, Two, Vector, Zero};
use crate::range::Range;
use core::ops::{Add, Div, Mul, Sub};
#[derive(Debug, Clone, Copy)]
pub struct Rect<T> {
x: T,
y: T,
w: T,
h: T,
}
impl<T> Rect<T> {
pub fn x(&self) -> &T {
&self.x
}
pub fn y(&self) -> &T {
&self.y
}
pub fn w(&self) -> &T {
&self.w
}
pub fn h(&self) -> &T {
&self.h
}
pub fn left(&self) -> T
where
T: Clone,
{
self.x.clone()
}
pub fn right(&self) -> T
where
T: Add<Output = T> + Clone,
{
self.x.clone() + self.w.clone()
}
pub fn top(&self) -> T
where
T: Clone,
{
self.y.clone()
}
pub fn bottom(&self) -> T
where
T: Add<Output = T> + Clone,
{
self.y.clone() + self.h.clone()
}
pub fn left_top(&self) -> Point<T>
where
T: Clone,
{
(self.left(), self.top()).into()
}
pub fn right_top(&self) -> Point<T>
where
T: Add<Output = T> + Clone,
{
(self.right(), self.top()).into()
}
pub fn right_bottom(&self) -> Point<T>
where
T: Add<Output = T> + Clone,
{
(self.right(), self.bottom()).into()
}
pub fn left_bottom(&self) -> Point<T>
where
T: Add<Output = T> + Clone,
{
(self.left(), self.bottom()).into()
}
pub fn from_lrtb(left: T, right: T, top: T, bottom: T) -> Self
where
T: Sub<Output = T> + Clone + Ord,
{
let x = if left < right {
(left, right)
} else {
(right, left)
};
let y = if top < bottom {
(top, bottom)
} else {
(bottom, top)
};
Self {
x: x.0.clone(),
y: y.0.clone(),
w: x.1 - x.0,
h: y.1 - y.0,
}
}
pub fn from_lrtb_unchecked(left: T, right: T, top: T, bottom: T) -> Self
where
T: Sub<Output = T> + Clone,
{
Self {
x: left.clone(),
y: top.clone(),
w: right - left,
h: bottom - top,
}
}
pub fn aabb<I>(iter: I) -> Option<Rect<T>>
where
T: Add<Output = T> + Sub<Output = T> + Clone + PartialOrd,
I: Iterator<Item = Rect<T>>,
{
let mut result: Option<(T, T, T, T)> = None;
for rect in iter {
let current = (rect.left(), rect.right(), rect.top(), rect.bottom());
let result = result.get_or_insert(current.clone());
if current.0 < result.0 {
result.0 = current.0
}
if current.1 > result.1 {
result.1 = current.1
}
if current.2 < result.2 {
result.2 = current.2
}
if current.3 > result.3 {
result.3 = current.3
}
}
result.map(|a| Rect::from_lrtb_unchecked(a.0, a.1, a.2, a.3))
}
pub fn aabb_from_points<I>(iter: I) -> Option<Rect<T>>
where
T: Add<Output = T> + Sub<Output = T> + Clone + PartialOrd,
I: Iterator<Item = Point<T>>,
{
let mut result: Option<(T, T, T, T)> = None;
for rect in iter {
let current = (
rect.x().clone(),
rect.x().clone(),
rect.y().clone(),
rect.y().clone(),
);
let result = result.get_or_insert(current.clone());
if current.0 < result.0 {
result.0 = current.0
}
if current.1 > result.1 {
result.1 = current.1
}
if current.2 < result.2 {
result.2 = current.2
}
if current.3 > result.3 {
result.3 = current.3
}
}
result.map(|a| Rect::from_lrtb_unchecked(a.0, a.1, a.2, a.3))
}
pub fn from_center(center: Point<T>, size: Size<T>) -> Self
where
T: Clone + Two + Sub<Output = T> + Div<Output = T>,
{
Self {
x: center.x().clone() - size.w().clone() / T::two(),
y: center.y().clone() - size.h().clone() / T::two(),
w: size.w().clone(),
h: size.h().clone(),
}
}
pub fn center(&self) -> Point<T>
where
T: Two + Add<Output = T> + Div<Output = T> + Clone,
{
(
self.x.clone() + self.w.clone() / T::two(),
self.y.clone() + self.h.clone() / T::two(),
)
.into()
}
pub fn x_range(&self) -> Range<T>
where
T: Clone + Add<Output = T>,
{
Range {
start: self.x.clone(),
end: self.w.clone() + self.x.clone(),
}
}
pub fn y_range(&self) -> Range<T>
where
T: Clone + Add<Output = T>,
{
Range {
start: self.y.clone(),
end: self.h.clone() + self.y.clone(),
}
}
pub fn contains(&self, other: &Rect<T>) -> bool
where
T: PartialOrd + Add<Output = T> + Clone,
{
other.left() >= self.left()
&& other.right() <= self.right()
&& other.top() >= self.top()
&& other.bottom() <= self.bottom()
}
pub fn contains_point(&self, _other: &Point<T>) -> bool {
todo!()
}
pub fn intersects(&self, other: &Rect<T>) -> bool
where
T: PartialOrd + Add<Output = T> + Clone,
{
let max = |x, y| if x > y { x } else { y };
let min = |x, y| if x < y { x } else { y };
let l = max(self.left(), other.left());
let r = min(self.right(), other.right());
let t = max(self.top(), other.top());
let b = min(self.bottom(), other.bottom());
l < r && t < b
}
pub fn intersects_circle(&self, center: Point<T>, radius: NonNeg<T>) -> bool
where
T: Add<Output = T> + Sub<Output = T> + Clone + Sq<Output = T> + PartialOrd,
{
let cx = center.x();
let cy = center.y();
let rx = self.x.clone();
let ry = self.y.clone();
let rw = self.w.clone();
let rh = self.h.clone();
let mut test_x = cx.clone();
let mut test_y = cy.clone();
if *cx < rx {
test_x = rx.clone();
}
else if *cx > rx.clone() + rw.clone() {
test_x = rx.clone() + rw.clone();
} if *cy < ry {
test_y = ry.clone();
}
else if *cy > ry.clone() + rh.clone() {
test_y = ry.clone() + rh.clone();
}
let dist_x = cx.clone() - test_x;
let dist_y = cy.clone() - test_y;
let distance_sqr = dist_x.sq() + dist_y.sq();
distance_sqr <= radius.into_inner().sq()
}
pub fn extended(self, vec: Vector<T>) -> Rect<T>
where
T: Two + Sub<Output = T> + Add<Output = T> + Mul<Output = T> + Clone,
{
let (x, y) = vec.into();
(
self.x - x.clone(),
self.y - y.clone(),
self.w + x * T::two(),
self.h + y * T::two(),
)
.into()
}
pub fn homogeneous_mul(self, rhs: T) -> (Self, T)
where
T: Two
+ Add<Output = T>
+ Div<Output = T>
+ Mul<Output = T>
+ Sub<Output = T>
+ Clone
+ Abs<Output = T>
+ PartialOrd,
{
let c = self.center();
let s = self.size();
let new_s = s.clone() * rhs;
let (w, h) = s.into();
let (new_w, new_h) = new_s.into();
let delta_w = new_w - w.clone();
let delta_h = new_h - h.clone();
let min_delta = if delta_w.clone().abs() < delta_h.clone().abs() {
delta_w
} else {
delta_h
};
(
Self::from_center(c, (w + min_delta.clone(), h + min_delta.clone()).into()),
min_delta,
)
}
pub fn homogeneous_div(self, _rhs: T) -> Self
where
T: Two + Add<Output = T> + Div<Output = T> + Sub<Output = T> + Clone,
{
todo!()
}
pub fn size(self) -> Size<T> {
(self.w, self.h).into()
}
}
impl<T> From<(Point<T>, Size<T>)> for Rect<T> {
fn from(value: (Point<T>, Size<T>)) -> Self {
let ((x, y), (w, h)) = (value.0.into(), value.1.into());
Rect { x, y, w, h }
}
}
impl<T: Zero> From<Size<T>> for Rect<T> {
fn from(value: Size<T>) -> Self {
let (w, h) = value.into();
Rect {
x: Zero::zero(),
y: Zero::zero(),
w,
h,
}
}
}
impl<T> From<(T, T, T, T)> for Rect<T> {
fn from(value: (T, T, T, T)) -> Self {
Self {
x: value.0,
y: value.1,
w: value.2,
h: value.3,
}
}
}
impl<T> Mul<T> for Rect<T>
where
T: Two + Add<Output = T> + Div<Output = T> + Mul<Output = T> + Sub<Output = T> + Clone,
{
type Output = Rect<T>;
fn mul(self, rhs: T) -> Self::Output {
let c = self.center();
let s = self.size();
Self::Output::from_center(c, s * rhs)
}
}
impl<T> Div<T> for Rect<T>
where
T: Two + Add<Output = T> + Div<Output = T> + Sub<Output = T> + Clone,
{
type Output = Rect<T>;
fn div(self, rhs: T) -> Self::Output {
let c = self.center();
let s = self.size();
Self::Output::from_center(c, s / rhs)
}
}