use std::ops::{Add, Sub};
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Point<T> {
pub x: T,
pub y: T,
}
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Dimension {
pub x: i32,
pub y: i32,
}
#[derive(Debug, Default, Copy, Clone, PartialEq)]
pub struct Size {
pub width: f32,
pub height: f32,
}
pub type Location = Point<i32>;
pub type Offset = Point<i32>;
pub type Coordinate = Point<f32>;
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct Scope(usize);
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Distance {
Euclidean,
Manhattan,
}
impl Default for Distance {
fn default() -> Self {
Self::Euclidean
}
}
impl Coordinate {
pub const fn origin() -> Self {
Self { x: 0.0, y: 0.0 }
}
}
impl Location {
pub const fn origin() -> Self {
Self { x: 0, y: 0 }
}
pub fn distance(self, other: Self, representation: Distance) -> usize {
match representation {
Distance::Euclidean => {
let x2 = self.x.saturating_sub(other.x).pow(2) as f64;
let y2 = self.y.saturating_sub(other.y).pow(2) as f64;
(x2 + y2).sqrt() as usize
}
Distance::Manhattan => {
let x = self.x.saturating_sub(other.x).abs();
let y = self.y.saturating_sub(other.y).abs();
(x + y) as usize
}
}
}
pub fn to_pixel_coords(self, side: f32) -> Coordinate {
Coordinate {
x: self.x as f32 * side,
y: self.y as f32 * side,
}
}
pub fn one_dimensional(self, dimension: impl Into<Dimension>) -> usize {
debug_assert!(!self.x.is_negative());
debug_assert!(!self.y.is_negative());
let dimension = dimension.into();
let pos = self.y.saturating_mul(dimension.x).saturating_add(self.x);
debug_assert!(!pos.is_negative());
debug_assert!(pos < dimension.x.saturating_mul(dimension.y));
pos as usize
}
pub fn from_one_dimensional(
index: usize,
dimension: impl Into<Dimension>,
) -> Self {
let dimension = dimension.into();
debug_assert!(dimension.x.is_positive());
Self {
x: index as i32 % dimension.x,
y: index as i32 / dimension.x,
}
}
pub fn translate(
&mut self,
offset: impl Into<Offset>,
dimension: impl Into<Dimension>,
) -> &mut Self {
let offset = offset.into();
let dimension = dimension.into();
self.x = self.x.saturating_add(offset.x).rem_euclid(dimension.x);
self.y = self.y.saturating_add(offset.y).rem_euclid(dimension.y);
self
}
pub fn translate_towards(
&mut self,
destination: impl Into<Self>,
dimension: impl Into<Dimension>,
) -> &mut Self {
let dimension = dimension.into();
let destination = destination.into();
let x = destination
.x
.rem_euclid(dimension.x)
.saturating_sub(self.x)
.signum();
let y = destination
.y
.rem_euclid(dimension.y)
.saturating_sub(self.y)
.signum();
self.translate(Offset { x, y }, dimension)
}
}
impl From<(i32, i32)> for Location {
fn from((x, y): (i32, i32)) -> Self {
Self { x, y }
}
}
impl From<Location> for (i32, i32) {
fn from(location: Location) -> Self {
(location.x, location.y)
}
}
impl Offset {
pub fn border(scope: impl Into<Scope>) -> Vec<Offset> {
let scope = scope.into();
let delta = scope.magnitude() as i32;
if delta == 0 {
return vec![Offset::origin()];
}
let mut offsets =
Vec::with_capacity(Dimension::perimeter_with_scope(scope));
for &y in &[-delta, delta] {
for x in -delta..=delta {
offsets.push(Offset { x, y });
}
}
for y in 1i32.saturating_sub(delta)..=delta.saturating_sub(1) {
for &x in &[-delta, delta] {
offsets.push(Offset { x, y });
}
}
debug_assert!(!offsets.is_empty());
offsets
}
pub fn corners(scope: impl Into<Scope>) -> [Offset; 4] {
let delta = scope.into().magnitude() as i32;
[
(-delta, -delta).into(),
(-delta, delta).into(),
(delta, -delta).into(),
(delta, delta).into(),
]
}
}
impl Size {
pub fn center(self) -> Coordinate {
Coordinate {
x: self.width / 2.0,
y: self.height / 2.0,
}
}
pub fn to_dimension(self, side: f32) -> Dimension {
Dimension {
x: (self.width / side) as i32,
y: (self.height / side) as i32,
}
}
}
impl From<(f32, f32)> for Size {
fn from((width, height): (f32, f32)) -> Self {
Self { width, height }
}
}
impl From<Size> for (f32, f32) {
fn from(size: Size) -> Self {
(size.width, size.height)
}
}
impl Dimension {
pub fn with_eq_rectangles(count: usize) -> Self {
debug_assert_ne!(count, 0);
let count = count as f64;
let x = count.sqrt().ceil();
let y = (count / x).ceil();
Self {
x: x as i32,
y: y as i32,
}
}
pub fn len(self) -> usize {
debug_assert!(!self.x.is_negative());
debug_assert!(!self.y.is_negative());
self.x.saturating_mul(self.y) as usize
}
pub fn is_empty(self) -> bool {
self.len() == 0
}
pub fn is_square(self) -> bool {
self.x == self.y
}
pub fn center(self) -> Location {
Location {
x: self.x / 2,
y: self.y / 2,
}
}
pub fn contains(self, location: impl Into<Location>) -> bool {
debug_assert!(self.x >= 0 && self.y >= 0);
let location = location.into();
!(location.x < 0
|| location.x >= self.x
|| location.y < 0
|| location.y >= self.y)
}
pub fn aspect_ratio(self) -> f32 {
self.x as f32 / self.y as f32
}
pub fn scale(self, other: impl Into<Self>) -> Self {
let other = other.into();
Self {
x: other.x / self.x,
y: other.y / self.y,
}
}
pub(crate) fn side_with_scope(scope: impl Into<Scope>) -> usize {
1 + scope.into().magnitude().saturating_sub(1) * 2
}
pub(crate) fn perimeter_with_scope(scope: impl Into<Scope>) -> usize {
let scope = scope.into();
match scope.magnitude() {
0 => 1,
_ => Self::side_with_scope(scope) * 4 + 4,
}
}
pub(crate) fn len_with_scope(scope: impl Into<Scope>) -> usize {
let scope = scope.into();
match scope.magnitude() {
0 => 1,
_ => {
Self::len_with_scope(scope.magnitude() - 1)
+ Self::perimeter_with_scope(scope)
}
}
}
}
impl From<(i32, i32)> for Dimension {
fn from((x, y): (i32, i32)) -> Self {
Self { x, y }
}
}
impl From<Dimension> for (i32, i32) {
fn from(dimension: Dimension) -> Self {
(dimension.x, dimension.y)
}
}
impl From<usize> for Scope {
fn from(magnitude: usize) -> Self {
Self(magnitude)
}
}
impl From<Scope> for usize {
fn from(scope: Scope) -> Self {
scope.0
}
}
impl Scope {
pub fn with_magnitude(magnitude: usize) -> Self {
Self(magnitude)
}
pub fn empty() -> Self {
Self::with_magnitude(0)
}
pub fn magnitude(self) -> usize {
self.0
}
pub(crate) fn overflows(self, dimension: impl Into<Dimension>) -> bool {
let side = Dimension::side_with_scope(self) as i32;
let dimension = dimension.into();
side > dimension.x || side > dimension.y
}
}
impl Add for Point<i32> {
type Output = Self;
fn add(self, other: Self) -> Self {
Self {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
impl Sub for Point<i32> {
type Output = Self;
fn sub(self, other: Self) -> Self {
Self {
x: self.x - other.x,
y: self.y - other.y,
}
}
}