use std::ops::{
Bound, Range, RangeBounds, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
};
use mint::Vector2;
use crate::util::*;
const MIN: Vector2<i32> = Vector2 {
x: i32::MIN,
y: i32::MIN,
};
const MAX: Vector2<i32> = Vector2 {
x: i32::MAX - 1,
y: i32::MAX - 1,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Boundary {
pub min: Vector2<i32>,
pub dimension: Vector2<u32>,
}
impl Default for Boundary {
fn default() -> Self {
Self {
min: Vector2 { x: 0, y: 0 },
dimension: Vector2 { x: 0, y: 0 },
}
}
}
impl<T: Into<Vector2<i32>>> FromIterator<T> for Boundary {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let mut min = Vector2 {
x: i32::MAX - 1,
y: i32::MAX - 1,
};
let mut max = Vector2 {
x: i32::MIN,
y: i32::MIN,
};
let mut encountered = false;
for point in iter {
encountered = true;
let point: Vector2<i32> = point.into();
min = vec_min(min, point);
max = vec_max(max, point);
}
if encountered {
Boundary::min_max(min, max)
} else {
Boundary::EMPTY
}
}
}
impl Boundary {
pub const EMPTY: Self = Self {
min: Vector2 { x: 0, y: 0 },
dimension: Vector2 { x: 0, y: 0 },
};
pub const ALL: Self = Self {
min: Vector2 {
x: i32::MIN,
y: i32::MIN,
},
dimension: Vector2 {
x: u32::MAX,
y: u32::MAX,
},
};
pub fn is_empty(&self) -> bool {
self.dimension.x == 0 || self.dimension.y == 0
}
pub fn len(&self) -> usize {
(self.dimension.x * self.dimension.y) as usize
}
pub(crate) fn pitch(&self) -> usize {
self.dimension.x as usize
}
pub fn max(&self) -> Vector2<i32> {
sub(addu(self.min, self.dimension), [1, 1].into())
}
pub fn max_non_inclusive(&self) -> Vector2<i32> {
addu(self.min, self.dimension)
}
pub fn intersection(&self, other: Boundary) -> Option<Boundary> {
let min = vec_max(self.min, other.min);
let u1 = self.max_non_inclusive();
let u2 = other.max_non_inclusive();
let max = vec_min(u1, u2);
if max.x < min.x || max.y < min.y {
None
} else {
Some(Boundary::min_max_non_inclusive(min, max))
}
}
pub fn from_point(point: impl Into<Vector2<i32>>) -> Self {
Boundary {
min: point.into(),
dimension: Vector2 { x: 1, y: 1 },
}
}
pub fn from_dimension(dimension: impl Into<Vector2<i32>>) -> Self {
let dimension = i2u(dimension.into());
Boundary {
min: Vector2 { x: 0, y: 0 },
dimension,
}
}
pub fn min_max(min: impl Into<Vector2<i32>>, max: impl Into<Vector2<i32>>) -> Self {
let min = min.into();
let max = max.into();
if min == MIN && max == MAX {
Boundary::ALL
} else {
let dimension = Vector2 {
x: max.x.wrapping_sub(min.x).wrapping_add(1) as u32,
y: max.y.wrapping_sub(min.y).wrapping_add(1) as u32,
};
Boundary { min, dimension }
}
}
pub(crate) fn min_max_non_inclusive(
min: impl Into<Vector2<i32>>,
max: impl Into<Vector2<i32>>,
) -> Self {
let min = min.into();
let max = max.into();
let dimension = Vector2 {
x: max.x.wrapping_sub(min.x) as u32,
y: max.y.wrapping_sub(min.y) as u32,
};
Boundary { min, dimension }
}
pub fn min_dim(min: impl Into<Vector2<i32>>, dimension: impl Into<Vector2<i32>>) -> Self {
let min = min.into();
let dimension = i2u(abs(dimension.into()));
Boundary { min, dimension }
}
pub fn center_hdim(center: impl Into<Vector2<i32>>, half_dim: impl Into<Vector2<i32>>) -> Self {
let center = center.into();
let half_dim = abs(half_dim.into());
let min = sub(center, half_dim);
let dimension = i2u(add(add(half_dim, half_dim), [1, 1].into()));
Boundary { min, dimension }
}
pub fn xy(x: impl RangeBounds<i32>, y: impl RangeBounds<i32>) -> Self {
let min_x = match x.start_bound() {
Bound::Included(v) => *v,
Bound::Excluded(v) => *v + 1,
Bound::Unbounded => i32::MIN,
};
let min_y = match y.start_bound() {
Bound::Included(v) => *v,
Bound::Excluded(v) => *v + 1,
Bound::Unbounded => i32::MIN,
};
let max_x = match x.end_bound() {
Bound::Included(v) => *v,
Bound::Excluded(v) => *v - 1,
Bound::Unbounded => i32::MAX - 1,
};
let max_y = match y.end_bound() {
Bound::Included(v) => *v,
Bound::Excluded(v) => *v - 1,
Bound::Unbounded => i32::MAX - 1,
};
Boundary::min_max([min_x, min_y], [max_x, max_y])
}
pub fn displace(&mut self, by: impl Into<Vector2<i32>>) {
self.min = add(self.min, by.into())
}
pub fn displace_by(&self, by: impl Into<Vector2<i32>>) -> Boundary {
let mut result = *self;
result.displace(by);
result
}
pub fn expand(&mut self, by: impl Into<Vector2<i32>>) {
let by = by.into();
self.min = sub(self.min, by);
self.dimension = i2u(add(add(u2i(self.dimension), by), by));
}
pub fn expand_by(&self, by: impl Into<Vector2<i32>>) -> Boundary {
let mut result = *self;
result.expand(by);
result
}
pub fn contains(&self, position: impl Into<Vector2<i32>>) -> bool {
let position = position.into();
position.x >= self.min.x
&& position.y >= self.min.y
&& position.x < self.min.x.wrapping_add(self.dimension.x as i32)
&& position.y < self.min.y.wrapping_add(self.dimension.y as i32)
}
pub fn iter<T: From<Vector2<i32>>>(&self) -> impl Iterator<Item = T> + 'static + use<T> {
let min = self.min;
DimensionIter::new(self.dimension).map(move |x| add(x, min).into())
}
}
pub trait IntoBoundary {
fn into_boundary(self) -> Boundary;
}
impl IntoBoundary for Boundary {
fn into_boundary(self) -> Boundary {
self
}
}
impl<T: IntoBoundary + Copy> IntoBoundary for &T {
fn into_boundary(self) -> Boundary {
(*self).into_boundary()
}
}
impl<U: Into<Vector2<i32>>> IntoBoundary for Range<U> {
fn into_boundary(self) -> Boundary {
Boundary::min_max_non_inclusive(self.start, self.end)
}
}
impl<U: Into<Vector2<i32>>> IntoBoundary for RangeInclusive<U> {
fn into_boundary(self) -> Boundary {
let (min, max) = self.into_inner();
Boundary::min_max(min, max)
}
}
impl<U: Into<Vector2<i32>>> IntoBoundary for RangeFrom<U> {
fn into_boundary(self) -> Boundary {
let min = self.start.into();
Boundary::min_max(min, MAX)
}
}
impl<U: Into<Vector2<i32>>> IntoBoundary for RangeTo<U> {
fn into_boundary(self) -> Boundary {
let max: Vector2<i32> = self.end.into();
Boundary::min_max_non_inclusive(MIN, max)
}
}
impl<U: Into<Vector2<i32>>> IntoBoundary for RangeToInclusive<U> {
fn into_boundary(self) -> Boundary {
let max: Vector2<i32> = self.end.into();
Boundary::min_max(MIN, max)
}
}
impl IntoBoundary for RangeFull {
fn into_boundary(self) -> Boundary {
Boundary::ALL
}
}
impl<A: RangeBounds<i32>, B: RangeBounds<i32>> IntoBoundary for (A, B) {
fn into_boundary(self) -> Boundary {
Boundary::xy(self.0, self.1)
}
}
impl IntoBoundary for [u32; 2] {
fn into_boundary(self) -> Boundary {
Boundary {
min: Vector2 { x: 0, y: 0 },
dimension: self.into(),
}
}
}