use std::ops::Sub;
use enum_ordinalize::Ordinalize;
use glam::{IVec2, Vec2};
use crate::GridPoint;
use super::GridSize;
#[derive(Eq, PartialEq, Clone, Copy, Debug, Ordinalize)]
pub enum Pivot {
TopLeft,
TopCenter,
TopRight,
LeftCenter,
RightCenter,
BottomLeft,
BottomCenter,
BottomRight,
Center,
}
impl Pivot {
#[inline]
pub fn axis(&self) -> IVec2 {
match self {
Pivot::TopLeft => IVec2::new(1, -1),
Pivot::TopRight => IVec2::new(-1, -1),
Pivot::Center => IVec2::new(1, 1),
Pivot::BottomLeft => IVec2::new(1, 1),
Pivot::BottomRight => IVec2::new(-1, 1),
Pivot::TopCenter => IVec2::new(1, -1),
Pivot::LeftCenter => IVec2::new(1, 1),
Pivot::RightCenter => IVec2::new(-1, 1),
Pivot::BottomCenter => IVec2::new(1, 1),
}
}
#[inline]
pub fn normalized(&self) -> Vec2 {
match self {
Pivot::TopLeft => Vec2::new(0.0, 1.0),
Pivot::TopRight => Vec2::new(1.0, 1.0),
Pivot::Center => Vec2::new(0.5, 0.5),
Pivot::BottomLeft => Vec2::new(0.0, 0.0),
Pivot::BottomRight => Vec2::new(1.0, 0.0),
Pivot::TopCenter => Vec2::new(0.5, 1.0),
Pivot::LeftCenter => Vec2::new(0.0, 0.5),
Pivot::RightCenter => Vec2::new(1.0, 0.5),
Pivot::BottomCenter => Vec2::new(0.5, 0.0),
}
}
#[inline]
pub fn transform_point(&self, grid_point: impl GridPoint) -> IVec2 {
grid_point.to_ivec2() * self.axis()
}
#[inline]
pub fn pivot_position(&self, grid_size: impl GridSize) -> IVec2 {
(grid_size.to_vec2().sub(1.0) * self.normalized())
.round()
.as_ivec2()
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct PivotedPoint {
pub point: IVec2,
pub pivot: Option<Pivot>,
}
impl PivotedPoint {
pub fn new(xy: impl GridPoint, pivot: Pivot) -> Self {
Self {
point: xy.to_ivec2(),
pivot: Some(pivot),
}
}
pub fn calculate(&self, grid_size: impl GridSize) -> IVec2 {
if let Some(pivot) = self.pivot {
pivot.pivot_position(grid_size) + pivot.transform_point(self.point)
} else {
self.point
}
}
pub fn with_default_pivot(&self, default_pivot: Pivot) -> PivotedPoint {
Self {
point: self.point,
pivot: Some(self.pivot.unwrap_or(default_pivot)),
}
}
}
impl<T: GridPoint> From<T> for PivotedPoint {
fn from(value: T) -> Self {
Self {
point: value.to_ivec2(),
pivot: None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn grid_pivot_size_offset() {
assert_eq!([4, 4], Pivot::TopRight.pivot_position([5, 5]).to_array());
assert_eq!([2, 2], Pivot::Center.pivot_position([5, 5]).to_array());
assert_eq!([3, 3], Pivot::TopRight.pivot_position([4, 4]).to_array());
assert_eq!([2, 2], Pivot::Center.pivot_position([4, 4]).to_array());
}
#[test]
fn pivoted_point() {
let pp = [1, 1].pivot(Pivot::TopLeft);
assert_eq!([1, 3], pp.calculate([5, 5]).to_array());
let pp = [1, 1].pivot(Pivot::TopRight);
assert_eq!([3, 3], pp.calculate([5, 5]).to_array());
let pp = [1, 1].pivot(Pivot::TopRight);
assert_eq!([4, 4], pp.calculate([6, 6]).to_array());
let pp = [1, 1].pivot(Pivot::Center);
assert_eq!([4, 4], pp.calculate([6, 6]).to_array());
let pp = [1, 1].pivot(Pivot::Center);
assert_eq!([3, 3], pp.calculate([5, 5]).to_array());
let pp = [0, 0].pivot(Pivot::BottomRight);
assert_eq!([8, 0], pp.calculate([9, 9]).to_array());
}
#[test]
fn center_negative() {
let p = [-5, -5].pivot(Pivot::Center);
assert_eq!([0, 0], p.calculate([10, 10]).to_array());
}
}