use std::ops::{Add, AddAssign, Neg, Sub, SubAssign};
use crate::Float;
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Point<T> {
pub x: T,
pub y: T,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Size<T> {
pub width: T,
pub height: T,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Rect<T> {
pub point: Point<T>,
pub size: Size<T>,
}
pub type FPoint = Point<Float>;
pub type FSize = Size<Float>;
pub type FRect = Rect<Float>;
pub type IPoint = Point<i32>;
pub type ISize = Size<i32>;
pub type IRect = Rect<i32>;
impl<T> Point<T> {
pub fn new(x: T, y: T) -> Self {
Self {
x,
y,
}
}
}
impl<T> Size<T> {
pub fn new(width: T, height: T) -> Self {
Self {
width,
height,
}
}
}
impl<T> Rect<T>
where
T: Add<Output = T> + AddAssign + Sub<Output = T> + SubAssign + Neg<Output = T> + Copy + Default + PartialOrd + PartialEq,
{
pub fn new(x: T, y: T, width: T, height: T) -> Self {
Self {
point: Point {
x,
y,
},
size: Size {
width,
height,
},
}
}
pub fn from_bounds(left: T, top: T, right: T, bottom: T) -> Self {
Self {
point: Point {
x: left,
y: top,
},
size: Size {
width: right - left,
height: bottom - top,
},
}
}
pub fn standardize(&self) {
let mut rect = *self;
if rect.size.width < T::default() {
rect.point.x += rect.size.width;
rect.size.width = -rect.size.width;
}
if rect.size.height < T::default() {
rect.point.y += rect.size.height;
rect.size.height = -rect.size.height;
}
}
pub fn is_empty(&self) -> bool {
self.size.width <= T::default() || self.size.height <= T::default()
}
pub fn is_null(&self) -> bool {
self.point.x == T::default() && self.point.y == T::default() && self.size.width == T::default() && self.size.height == T::default()
}
pub fn offset(&self, dx: T, dy: T) -> Rect<T> {
let mut rect = *self;
rect.point.x += dx;
rect.point.y += dy;
rect
}
pub fn inflate(&self, dx: T, dy: T) -> Rect<T> {
let mut rect = *self;
rect.point.x -= dx;
rect.point.y -= dy;
rect.size.width += dx + dx;
rect.size.height += dy + dy;
rect
}
pub fn deflate(&self, dx: T, dy: T) -> Rect<T> {
let mut rect = *self;
rect.point.x += dx;
rect.point.y += dy;
rect.size.width -= dx + dx;
rect.size.height -= dy + dy;
rect
}
pub fn contains_point(&self, point: Point<T>) -> bool {
point.x >= self.point.x && point.x < self.point.x + self.size.width && point.y >= self.point.y && point.y < self.point.y + self.size.height
}
pub fn contains_rect(&self, rect: Rect<T>) -> bool {
self.point.x <= rect.point.x &&
self.point.y <= rect.point.y &&
self.point.x + self.size.width >= rect.point.x + rect.size.width &&
self.point.y + self.size.height >= rect.point.y + rect.size.height
}
pub fn intersects(&self, rect: Rect<T>) -> bool {
self.point.x < rect.point.x + rect.size.width &&
rect.point.x < self.point.x + self.size.width &&
self.point.y < rect.point.y + rect.size.height &&
rect.point.y < self.point.y + self.size.height
}
pub fn union(&self, rect: Rect<T>) -> Option<Rect<T>> {
let left = if self.point.x < rect.point.x {
self.point.x
} else {
rect.point.x
};
let top = if self.point.y < rect.point.y {
self.point.y
} else {
rect.point.y
};
let right = if self.point.x + self.size.width > rect.point.x + rect.size.width {
self.point.x + self.size.width
} else {
rect.point.x + rect.size.width
};
let bottom = if self.point.y + self.size.height > rect.point.y + rect.size.height {
self.point.y + self.size.height
} else {
rect.point.y + rect.size.height
};
if left > right || top > bottom {
None
} else {
Some(Rect::from_bounds(left, top, right, bottom))
}
}
pub fn intersection(&self, rect: Rect<T>) -> Option<Rect<T>> {
let left = if self.point.x > rect.point.x {
self.point.x
} else {
rect.point.x
};
let top = if self.point.y > rect.point.y {
self.point.y
} else {
rect.point.y
};
let right = if self.point.x + self.size.width < rect.point.x + rect.size.width {
self.point.x + self.size.width
} else {
rect.point.x + rect.size.width
};
let bottom = if self.point.y + self.size.height < rect.point.y + rect.size.height {
self.point.y + self.size.height
} else {
rect.point.y + rect.size.height
};
if left > right || top > bottom {
None
} else {
Some(Rect::from_bounds(left, top, right, bottom))
}
}
}
impl From<IPoint> for (i32, i32) {
fn from(point: IPoint) -> Self {
(point.x, point.y)
}
}
impl From<FPoint> for (Float, Float) {
fn from(point: FPoint) -> Self {
(point.x, point.y)
}
}
impl From<IPoint> for FPoint {
fn from(point: IPoint) -> Self {
Self {
x: point.x as Float,
y: point.y as Float,
}
}
}
impl From<FPoint> for IPoint {
fn from(point: FPoint) -> Self {
Self {
x: point.x as i32,
y: point.y as i32,
}
}
}
impl From<ISize> for (i32, i32) {
fn from(size: ISize) -> Self {
(size.width, size.height)
}
}
impl From<FSize> for (Float, Float) {
fn from(size: FSize) -> Self {
(size.width, size.height)
}
}
impl From<ISize> for FSize {
fn from(size: ISize) -> Self {
Self {
width: size.width as Float,
height: size.height as Float,
}
}
}
impl From<FSize> for ISize {
fn from(size: FSize) -> Self {
Self {
width: size.width as i32,
height: size.height as i32,
}
}
}
impl From<IRect> for (i32, i32, i32, i32) {
fn from(rect: IRect) -> Self {
(rect.point.x, rect.point.y, rect.size.width, rect.size.height)
}
}
impl From<FRect> for (Float, Float, Float, Float) {
fn from(rect: FRect) -> Self {
(rect.point.x, rect.point.y, rect.size.width, rect.size.height)
}
}
impl From<IRect> for FRect {
fn from(rect: IRect) -> Self {
Self {
point: FPoint {
x: rect.point.x as Float,
y: rect.point.y as Float,
},
size: FSize {
width: rect.size.width as Float,
height: rect.size.height as Float,
},
}
}
}
impl From<FRect> for IRect {
fn from(rect: FRect) -> Self {
Self {
point: IPoint {
x: rect.point.x as i32,
y: rect.point.y as i32,
},
size: ISize {
width: rect.size.width as i32,
height: rect.size.height as i32,
},
}
}
}