use std;
use std::sync::LazyLock;
use derive_more::{From, TryInto};
use crate::math::Vector2;
use crate::geometry::integer::Aabb2;
pub use self::position::Position;
pub use self::dimensions::Dimensions;
pub (crate) static TILE_WH : LazyLock <[u32; 2]> = LazyLock::new (||{
let width = std::env::var ("GOOEY_TILE_WIDTH").unwrap().parse().unwrap();
let height = std::env::var ("GOOEY_TILE_HEIGHT").unwrap().parse().unwrap();
[width, height]
});
pub (crate) static SCREEN_WH : LazyLock <std::sync::RwLock <[u32; 2]>> = LazyLock::new (
|| std::sync::RwLock::new ([0, 0]));
#[derive(Clone, Copy, Debug, Eq, PartialEq, From, TryInto)]
pub enum Coordinates {
Tile (position::Tile, dimensions::Tile),
Pixel (position::Pixel, dimensions::Pixel)
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Kind {
Tile, Pixel
}
pub fn pixel_to_tile (x : i32, y : i32) -> [i32; 2] {
let [tile_w, tile_h] = *TILE_WH;
let [_screen_w, screen_h] = *SCREEN_WH.read().unwrap();
let column = x / tile_w as i32;
let row = (screen_h as i32 - y) / tile_h as i32;
[row, column]
}
pub fn pixel_to_tile_aabb (aabb : Aabb2 <i32>) -> Aabb2 <i32> {
let [min_x, min_y] = aabb.min().0.into_array();
let [max_x, max_y] = aabb.max().0.into_array();
let min = pixel_to_tile (min_x, max_y).into();
let max = pixel_to_tile (max_x, min_y).into();
Aabb2::with_minmax (min, max)
}
pub fn tile_to_pixel (row : i32, column : i32) -> [i32; 2] {
let [tile_w, tile_h] = *TILE_WH;
let [_screen_w, screen_h] = *SCREEN_WH.read().unwrap();
let x = column * tile_w as i32;
let y = screen_h as i32 - row * tile_h as i32;
[x, y]
}
pub fn tile_to_pixel_aabb (aabb : Aabb2 <i32>) -> Aabb2 <i32> {
let aabb = Aabb2::with_minmax (
aabb.min(),
aabb.max() + Vector2::new (1, 1)
);
let [min_row, min_col] = aabb.min().0.into_array();
let [max_row, max_col] = aabb.max().0.into_array();
let min = tile_to_pixel (max_row, min_col).into();
let max = tile_to_pixel (min_row, max_col).into();
Aabb2::with_minmax (min, max)
}
impl Coordinates {
#[inline]
pub fn default_tile() -> Self {
(position::Tile::default(), dimensions::Tile::default()).into()
}
#[inline]
pub fn default_pixel() -> Self {
(position::Pixel::default(), dimensions::Pixel::default()).into()
}
pub fn tile_from_aabb (aabb : Aabb2 <i32>) -> Self {
let position = (aabb.min()).into();
let dimensions = aabb.dimensions().numcast().unwrap().into();
Coordinates::Tile (position, dimensions)
}
pub fn pixel_from_aabb (aabb : Aabb2 <i32>) -> Self {
let position = aabb.min().into();
let dimensions = aabb.dimensions().numcast().unwrap().into();
Coordinates::Pixel (position, dimensions)
}
#[inline]
pub const fn kind (&self) -> Kind {
match self {
Coordinates::Tile (_, _) => Kind::Tile,
Coordinates::Pixel (_, _) => Kind::Pixel
}
}
#[inline]
pub fn dimensions (&self) -> Dimensions {
match self {
Coordinates::Tile (_, dimensions) => (*dimensions).into(),
Coordinates::Pixel (_, dimensions) => (*dimensions).into()
}
}
#[inline]
pub fn position (&self) -> Position {
match self {
Coordinates::Tile (position, _) => (*position).into(),
Coordinates::Pixel (position, _) => (*position).into()
}
}
#[inline]
pub const fn dimensions_horizontal (&self) -> u32 {
match self {
Coordinates::Tile (_, dimensions) => dimensions.columns(),
Coordinates::Pixel (_, dimensions) => dimensions.width()
}
}
#[inline]
pub const fn dimensions_vertical (&self) -> u32 {
match self {
Coordinates::Tile (_, dimensions) => dimensions.rows(),
Coordinates::Pixel (_, dimensions) => dimensions.height()
}
}
#[inline]
pub fn position_horizontal (&self) -> i32 {
match self {
Coordinates::Tile (position, _) => position.column(),
Coordinates::Pixel (position, _) => position.0.x
}
}
#[inline]
pub fn position_vertical (&self) -> i32 {
match self {
Coordinates::Tile (position, _) => position.row(),
Coordinates::Pixel (position, _) => position.0.y
}
}
#[inline]
pub fn modify_dimensions_horizontal (&mut self, d : i32) {
match self {
Coordinates::Tile (_, dimensions) =>
*dimensions.columns_mut() =
std::cmp::max (0, dimensions.columns() as i32 + d) as u32,
Coordinates::Pixel (_, dimensions) =>
dimensions.x = std::cmp::max (0, dimensions.x as i32 + d) as u32
}
}
#[inline]
pub fn modify_dimensions_vertical (&mut self, d : i32) {
match self {
Coordinates::Tile (_, dimensions) =>
*dimensions.rows_mut() =
std::cmp::max (0, dimensions.rows() as i32 + d) as u32,
Coordinates::Pixel (_, dimensions) =>
dimensions.y = std::cmp::max (0, dimensions.y as i32 + d) as u32
}
}
#[inline]
pub fn modify_position_horizontal (&mut self, d : i32) {
match self {
Coordinates::Tile (position, _) => *position.column_mut() = position.column() + d,
Coordinates::Pixel (position, _) => position.0.x += d
}
}
#[inline]
pub fn modify_position_vertical (&mut self, d : i32) {
match self {
Coordinates::Tile (position, _) => *position.row_mut() = position.row() + d,
Coordinates::Pixel (position, _) => position.0.y += d
}
}
#[inline]
pub fn set_position (&mut self, position : Position) {
match (self, position) {
(Coordinates::Tile (position, _), Position::Tile (p)) => *position = p,
(Coordinates::Pixel (position, _), Position::Pixel (p)) => *position = p,
_ => unreachable!()
}
}
#[inline]
pub fn set_dimensions (&mut self, dimensions : Dimensions) {
match (self, dimensions) {
(Coordinates::Tile (_, dimensions), Dimensions::Tile (d)) =>
*dimensions = d,
(Coordinates::Pixel (_, dimensions), Dimensions::Pixel (d)) =>
*dimensions = d,
_ => unreachable!()
}
}
}
impl From <Coordinates> for Aabb2 <i32> {
fn from (coordinates : Coordinates) -> Aabb2 <i32> {
log::trace!("aabb2 from coordinates: {coordinates:?}");
let (min, max) = match coordinates {
Coordinates::Tile (position, dimensions) => {
let mut max = *position + dimensions.numcast().unwrap();
if dimensions.x > 0 {
max.0.x -= 1;
}
if dimensions.y > 0 {
max.0.y -= 1;
}
(*position, max)
}
Coordinates::Pixel (position, dimensions) => {
let mut max = *position + dimensions.numcast().unwrap();
if dimensions.x > 0 {
max.0.x -= 1;
}
if dimensions.y > 0 {
max.0.y -= 1;
}
(*position, max)
}
};
log::trace!("aabb2 with min, max: {:?}", (min, max));
Aabb2::with_minmax (min, max)
}
}
impl TryFrom <(Position, Dimensions)> for Coordinates {
type Error = (Position, Dimensions);
fn try_from ((position, dimensions) : (Position, Dimensions))
-> Result <Coordinates, (Position, Dimensions)>
{
let coordinates = match (position, dimensions) {
(Position::Tile (position), Dimensions::Tile (dimensions)) =>
Coordinates::Tile (position, dimensions),
(Position::Pixel (position), Dimensions::Pixel (dimensions)) =>
Coordinates::Pixel (position, dimensions),
_ => return Err ((position, dimensions))
};
Ok (coordinates)
}
}
pub mod position {
use derive_more::{From, TryInto};
use crate::math::Point2;
#[derive(Clone, Copy, Debug, Eq, PartialEq, From, TryInto)]
pub enum Position {
Tile (Tile),
Pixel (Pixel)
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Tile {
pub position_rc : Point2 <i32>
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Pixel {
pub position_xy : Point2 <i32>
}
impl Tile {
#[inline]
pub const fn origin() -> Self {
Tile {
position_rc: Point2::new (0, 0)
}
}
#[inline]
pub fn new_rc (row : i32, column : i32) -> Self {
Point2::new (row, column).into()
}
#[inline]
pub const fn row (&self) -> i32 {
self.position_rc.0.x
}
#[inline]
pub const fn column (&self) -> i32 {
self.position_rc.0.y
}
#[inline]
pub const fn row_mut (&mut self) -> &mut i32 {
&mut self.position_rc.0.x
}
#[inline]
pub const fn column_mut (&mut self) -> &mut i32 {
&mut self.position_rc.0.y
}
}
impl Default for Tile {
fn default() -> Self {
Tile { position_rc: [0,0].into() }
}
}
impl From <Point2 <i32>> for Tile {
fn from (position_rc : Point2 <i32>) -> Self {
Tile { position_rc }
}
}
impl std::ops::Deref for Tile {
type Target = Point2 <i32>;
fn deref (&self) -> &Point2 <i32> {
&self.position_rc
}
}
impl std::ops::DerefMut for Tile {
fn deref_mut (&mut self) -> &mut Point2 <i32> {
&mut self.position_rc
}
}
impl Pixel {
#[inline]
pub const fn origin() -> Self {
Pixel {
position_xy: Point2::new (0, 0)
}
}
#[inline]
pub fn new_xy (x : i32, y : i32) -> Self {
Point2::new (x, y).into()
}
}
impl Default for Pixel {
fn default() -> Self {
Pixel { position_xy: [0,0].into() }
}
}
impl From <Point2 <i32>> for Pixel {
fn from (position_xy : Point2 <i32>) -> Self {
Pixel { position_xy }
}
}
impl std::ops::Deref for Pixel {
type Target = Point2 <i32>;
fn deref (&self) -> &Point2 <i32> {
&self.position_xy
}
}
impl std::ops::DerefMut for Pixel {
fn deref_mut (&mut self) -> &mut Point2 <i32> {
&mut self.position_xy
}
}
}
pub mod dimensions {
use derive_more::{From, TryInto};
use crate::math::Vector2;
#[derive(Clone, Copy, Debug, Eq, PartialEq, From, TryInto)]
pub enum Dimensions {
Tile (Tile),
Pixel (Pixel)
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Tile {
pub dimensions_rc : Vector2 <u32>
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Pixel {
pub dimensions_xy : Vector2 <u32>
}
impl Dimensions {
pub const fn horizontal (&self) -> u32 {
match self {
Dimensions::Tile (tile) => tile.columns(),
Dimensions::Pixel (pixel) => pixel.width()
}
}
pub const fn vertical (&self) -> u32 {
match self {
Dimensions::Tile (tile) => tile.rows(),
Dimensions::Pixel (pixel) => pixel.height()
}
}
pub fn vec (&self) -> Vector2 <u32> {
match self {
Dimensions::Tile (tile) => **tile,
Dimensions::Pixel (pixel) => **pixel
}
}
}
impl Tile {
#[inline]
pub fn new_rc (rows : u32, columns : u32) -> Self {
Vector2::new (rows, columns).into()
}
#[inline]
pub const fn columns (&self) -> u32 {
self.dimensions_rc.y
}
#[inline]
pub const fn rows (&self) -> u32 {
self.dimensions_rc.x
}
#[inline]
pub const fn rows_mut (&mut self) -> &mut u32 {
&mut self.dimensions_rc.x
}
#[inline]
pub const fn columns_mut (&mut self) -> &mut u32 {
&mut self.dimensions_rc.y
}
#[inline]
pub fn to_pixel (self) -> Pixel {
Vector2::new (self.columns(), self.rows()).into()
}
}
impl Pixel {
#[inline]
pub fn new_wh (width : u32, height : u32) -> Self {
Vector2::new (width, height).into()
}
#[inline]
pub const fn width (&self) -> u32 {
self.dimensions_xy.x
}
#[inline]
pub const fn height (&self) -> u32 {
self.dimensions_xy.y
}
#[inline]
pub fn to_tile (self) -> Tile {
Vector2::new (self.height(), self.width()).into()
}
}
impl Default for Tile {
fn default() -> Self {
Tile { dimensions_rc: [0,0].into() }
}
}
impl From <Vector2 <u32>> for Tile {
fn from (dimensions_rc : Vector2 <u32>) -> Self {
Tile { dimensions_rc }
}
}
impl std::ops::Deref for Tile {
type Target = Vector2 <u32>;
fn deref (&self) -> &Self::Target {
&self.dimensions_rc
}
}
impl std::ops::DerefMut for Tile {
fn deref_mut (&mut self) -> &mut Self::Target {
&mut self.dimensions_rc
}
}
impl Default for Pixel {
fn default() -> Self {
Pixel { dimensions_xy: [0,0].into() }
}
}
impl From <Vector2 <u32>> for Pixel {
fn from (dimensions_xy : Vector2 <u32>) -> Self {
Pixel { dimensions_xy }
}
}
impl std::ops::Deref for Pixel {
type Target = Vector2 <u32>;
fn deref (&self) -> &Self::Target {
&self.dimensions_xy
}
}
impl std::ops::DerefMut for Pixel {
fn deref_mut (&mut self) -> &mut Self::Target {
&mut self.dimensions_xy
}
}
}
impl std::ops::Add <dimensions::Tile> for position::Tile {
type Output = Self;
fn add (self, rhs : dimensions::Tile) -> Self {
(self.position_rc + rhs.dimensions_rc.numcast().unwrap()).into()
}
}
impl std::ops::Add <dimensions::Pixel> for position::Pixel {
type Output = Self;
fn add (self, rhs : dimensions::Pixel) -> Self {
(self.position_xy + rhs.dimensions_xy.numcast().unwrap()).into()
}
}
impl std::ops::Sub <dimensions::Tile> for position::Tile {
type Output = Self;
fn sub (self, rhs : dimensions::Tile) -> Self {
(self.position_rc - rhs.dimensions_rc.numcast().unwrap()).into()
}
}
impl std::ops::Sub <dimensions::Pixel> for position::Pixel {
type Output = Self;
fn sub (self, rhs : dimensions::Pixel) -> Self {
(self.position_xy - rhs.dimensions_xy.numcast().unwrap()).into()
}
}
#[cfg(test)]
mod tests {
use crate::geometry::integer::Aabb2;
use super::*;
#[test]
fn tile_to_pixel_aabb() {
use super::tile_to_pixel_aabb;
unsafe {
std::env::set_var ("GOOEY_TILE_WIDTH", "8");
std::env::set_var ("GOOEY_TILE_HEIGHT", "8");
}
*SCREEN_WH.write().unwrap() = [100, 100];
let coordinates = Coordinates::Tile (
position::Tile::new_rc (0, 0),
dimensions::Tile::new_rc (1, 1));
let aabb = Aabb2::from (coordinates);
assert!(aabb.contains ([0, 0].into()));
assert!(!aabb.contains ([1, 1].into()));
let aabb_pixel = tile_to_pixel_aabb (aabb);
assert_eq!(aabb_pixel.min(), [0, 92].into());
assert_eq!(aabb_pixel.max(), [8, 100].into());
}
}