use std::{
cmp::{max, min},
ops::Range,
};
use super::{vec2::Vec2, Padding, Vec2Range};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Rect {
pub pos: Vec2,
pub size: Vec2,
}
impl Rect {
pub fn new(x: usize, y: usize, width: usize, height: usize) -> Self {
Self {
pos: Vec2::new(x, y),
size: Vec2::new(width, height),
}
}
pub fn from_coords<T1, T2>(pos: T1, size: T2) -> Self
where
T1: Into<Vec2>,
T2: Into<Vec2>,
{
Self {
pos: pos.into(),
size: size.into(),
}
}
pub fn inner<T>(self, padding: T) -> Self
where
T: Into<Padding>,
{
let padding: Padding = padding.into();
Self {
pos: Vec2::new(self.x() + padding.left, self.y() + padding.top),
size: Vec2::new(
self.width().saturating_sub(padding.get_horizontal()),
self.height().saturating_sub(padding.get_vertical()),
),
}
}
pub fn union(self, other: &Self) -> Self {
let min_x = min(self.x(), other.x());
let min_y = min(self.y(), other.y());
let max_x = max(self.right() + 1, other.right() + 1);
let max_y = max(self.bottom() + 1, other.bottom() + 1);
Self {
pos: Vec2::new(min_x, min_y),
size: Vec2::new(
max_x.saturating_sub(min_x),
max_y.saturating_sub(min_y),
),
}
}
pub fn intersection(self, other: &Self) -> Self {
let max_x = max(self.x(), other.x());
let max_y = max(self.y(), other.y());
let min_x = min(self.right() + 1, other.right() + 1);
let min_y = min(self.bottom() + 1, other.bottom() + 1);
Self {
pos: Vec2::new(max_x, max_y),
size: Vec2::new(
min_x.saturating_sub(max_x),
min_y.saturating_sub(max_y),
),
}
}
pub fn move_to(&mut self, pos: Vec2) {
self.pos = pos;
}
pub fn pos(&self) -> &Vec2 {
&self.pos
}
pub fn x(&self) -> usize {
self.pos.x
}
pub fn left(&self) -> usize {
self.pos.x
}
pub fn right(&self) -> usize {
(self.pos.x + self.size.x).saturating_sub(1)
}
pub fn y(&self) -> usize {
self.pos.y
}
pub fn top(&self) -> usize {
self.pos.y
}
pub fn bottom(&self) -> usize {
(self.pos.y + self.size.y).saturating_sub(1)
}
pub fn top_left(&self) -> Vec2 {
*self.pos()
}
pub fn top_right(&self) -> Vec2 {
Vec2::new(self.right(), self.top())
}
pub fn bottom_left(&self) -> Vec2 {
Vec2::new(self.x(), self.bottom())
}
pub fn bottom_right(&self) -> Vec2 {
Vec2::new(self.right(), self.bottom())
}
pub fn size(&self) -> &Vec2 {
&self.size
}
pub fn width(&self) -> usize {
self.size.x
}
pub fn height(&self) -> usize {
self.size.y
}
pub fn contains(&self, other: &Self) -> bool {
!other.is_empty()
&& self.x() <= other.x()
&& self.y() <= other.y()
&& self.right() >= other.right()
&& self.bottom() >= other.bottom()
}
pub fn contains_pos(&self, pos: &Vec2) -> bool {
self.x() <= pos.x
&& pos.x <= self.right()
&& self.y() <= pos.y
&& pos.y <= self.bottom()
}
pub fn intersects(&self, other: &Self) -> bool {
(self.x() < other.right() && self.right() > other.x())
|| (self.y() < other.bottom() && self.bottom() > other.x())
}
pub const fn area(&self) -> usize {
self.size.x * self.size.y
}
pub const fn is_empty(&self) -> bool {
self.size.x == 0 || self.size.y == 0
}
}
impl From<(Vec2, Vec2)> for Rect {
fn from((pos, size): (Vec2, Vec2)) -> Self {
Self { pos, size }
}
}
impl From<(usize, usize, Vec2)> for Rect {
fn from((x, y, size): (usize, usize, Vec2)) -> Self {
Self {
pos: Vec2::new(x, y),
size,
}
}
}
impl From<(Vec2, usize, usize)> for Rect {
fn from((pos, width, height): (Vec2, usize, usize)) -> Self {
Self {
pos,
size: Vec2::new(width, height),
}
}
}
impl From<(usize, usize, usize, usize)> for Rect {
fn from((x, y, width, height): (usize, usize, usize, usize)) -> Self {
Self::new(x, y, width, height)
}
}
impl From<(Range<usize>, Range<usize>)> for Rect {
fn from((x, y): (Range<usize>, Range<usize>)) -> Self {
Self::new(x.start, y.start, x.end - x.start, y.end - y.start)
}
}
impl IntoIterator for Rect {
type Item = Vec2;
type IntoIter = Vec2Range;
fn into_iter(self) -> Self::IntoIter {
Vec2Range::new(
self.pos,
Vec2::new(self.x() + self.width(), self.y() + self.height()),
)
}
}