use crate::helpers::square_grid::SquarePos;
use crate::helpers::square_grid::neighbors::{SQUARE_OFFSETS, SquareDirection};
use crate::helpers::square_grid::staggered::StaggeredPos;
use crate::tiles::TilePos;
use crate::{TilemapGridSize, TilemapSize};
use bevy::math::{Mat2, Vec2};
use std::ops::{Add, Mul, Sub};
#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DiamondPos {
pub x: i32,
pub y: i32,
}
impl Add<DiamondPos> for DiamondPos {
type Output = DiamondPos;
fn add(self, rhs: DiamondPos) -> Self::Output {
DiamondPos {
x: self.x + rhs.x,
y: self.y + rhs.y,
}
}
}
impl Sub<DiamondPos> for DiamondPos {
type Output = DiamondPos;
fn sub(self, rhs: DiamondPos) -> Self::Output {
DiamondPos {
x: self.x - rhs.x,
y: self.y - rhs.y,
}
}
}
impl Mul<DiamondPos> for i32 {
type Output = DiamondPos;
fn mul(self, rhs: DiamondPos) -> Self::Output {
DiamondPos {
x: self * rhs.x,
y: self * rhs.y,
}
}
}
pub const DIAMOND_BASIS: Mat2 = Mat2::from_cols(Vec2::new(0.5, -0.5), Vec2::new(0.5, 0.5));
pub const INV_DIAMOND_BASIS: Mat2 = Mat2::from_cols(Vec2::new(1.0, 1.0), Vec2::new(-1.0, 1.0));
impl From<TilePos> for DiamondPos {
#[inline]
fn from(tile_pos: TilePos) -> Self {
let TilePos { x, y } = tile_pos;
DiamondPos {
x: x as i32,
y: y as i32,
}
}
}
impl From<&TilePos> for DiamondPos {
#[inline]
fn from(tile_pos: &TilePos) -> Self {
DiamondPos::from(*tile_pos)
}
}
impl From<StaggeredPos> for DiamondPos {
#[inline]
fn from(staggered_pos: StaggeredPos) -> Self {
let StaggeredPos { x, y } = staggered_pos;
DiamondPos { x, y: y + x }
}
}
impl From<&StaggeredPos> for DiamondPos {
#[inline]
fn from(staggered_pos: &StaggeredPos) -> Self {
DiamondPos::from(*staggered_pos)
}
}
impl From<SquarePos> for DiamondPos {
#[inline]
fn from(square_pos: SquarePos) -> Self {
let SquarePos { x, y } = square_pos;
DiamondPos { x, y }
}
}
impl From<&SquarePos> for DiamondPos {
#[inline]
fn from(square_pos: &SquarePos) -> Self {
DiamondPos::from(*square_pos)
}
}
impl DiamondPos {
pub const fn new(x: i32, y: i32) -> Self {
Self { x, y }
}
#[inline]
pub fn project(pos: Vec2, grid_size: &TilemapGridSize) -> Vec2 {
let unscaled_pos = DIAMOND_BASIS * pos;
Vec2::new(grid_size.x * unscaled_pos.x, grid_size.y * unscaled_pos.y)
}
#[inline]
pub fn center_in_world(&self, grid_size: &TilemapGridSize) -> Vec2 {
Self::project(Vec2::new(self.x as f32, self.y as f32), grid_size)
}
#[inline]
pub fn corner_offset_in_world(
corner_direction: SquareDirection,
grid_size: &TilemapGridSize,
) -> Vec2 {
let corner_offset = DiamondPos::from(SquarePos::from(corner_direction));
let corner_pos = 0.5 * Vec2::new(corner_offset.x as f32, corner_offset.y as f32);
Self::project(corner_pos, grid_size)
}
#[inline]
pub fn corner_in_world(
&self,
corner_direction: SquareDirection,
grid_size: &TilemapGridSize,
) -> Vec2 {
let center = Vec2::new(self.x as f32, self.y as f32);
let corner_offset = DiamondPos::from(SquarePos::from(corner_direction));
let corner_pos = 0.5 * Vec2::new(corner_offset.x as f32, corner_offset.y as f32);
Self::project(center + corner_pos, grid_size)
}
#[inline]
pub fn from_world_pos(world_pos: &Vec2, grid_size: &TilemapGridSize) -> DiamondPos {
let normalized_world_pos = Vec2::new(world_pos.x / grid_size.x, world_pos.y / grid_size.y);
let Vec2 { x, y } = INV_DIAMOND_BASIS * normalized_world_pos;
DiamondPos {
x: (x + 0.5).floor() as i32,
y: (y + 0.5).floor() as i32,
}
}
#[inline]
pub fn as_tile_pos(&self, map_size: &TilemapSize) -> Option<TilePos> {
TilePos::from_i32_pair(self.x, self.y, map_size)
}
#[inline]
pub fn offset(&self, direction: &SquareDirection) -> DiamondPos {
DiamondPos::from(SquarePos::from(self) + SQUARE_OFFSETS[*direction as usize])
}
}
impl TilePos {
#[inline]
pub fn diamond_offset(
&self,
direction: &SquareDirection,
map_size: &TilemapSize,
) -> Option<TilePos> {
DiamondPos::from(self)
.offset(direction)
.as_tile_pos(map_size)
}
}