You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

212 lines
4.6 KiB

use std::{
marker::PhantomData,
ops::{Add, AddAssign, Index, IndexMut, Sub, SubAssign},
};
use bevy::{
ecs::component::Component,
math::{Dir3, IVec3, InvalidDirectionError},
};
use super::Region;
pub struct Pos<T> {
pub x: i32,
pub y: i32,
pub z: i32,
_marker: PhantomData<T>,
}
impl<T> Pos<T> {
pub const ORIGIN: Self = Self::new(0, 0, 0);
pub const fn new(x: i32, y: i32, z: i32) -> Self {
Self {
x,
y,
z,
_marker: PhantomData,
}
}
pub fn region(self) -> Region<T> {
Region::new_unchecked(self.into(), self.into())
}
pub fn distance_to(self, other: Self) -> f32 {
(self.distance_to_squared(other) as f32).sqrt()
}
pub fn distance_to_squared(self, other: Self) -> i32 {
IVec3::distance_squared(self.into(), other.into())
}
pub fn direction_to(self, other: Self) -> Result<Dir3, InvalidDirectionError> {
Dir3::new((other - self).as_vec3())
}
pub fn min(self, rhs: Self) -> Self {
Self::new(self.x.min(rhs.x), self.y.min(rhs.y), self.z.min(rhs.z))
}
pub fn max(self, rhs: Self) -> Self {
Self::new(self.x.max(rhs.x), self.y.max(rhs.y), self.z.max(rhs.z))
}
}
impl<T> std::fmt::Display for Pos<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[{}, {}, {}]", self.x, self.y, self.z)
}
}
impl<T> std::fmt::Debug for Pos<T> {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fmt.debug_tuple(stringify!(Pos<T>))
.field(&self.x)
.field(&self.y)
.field(&self.z)
.finish()
}
}
// Trait implementations that SHOULD be handled by #[derive]
use bevy::ecs::component::StorageType;
impl<T: Send + Sync + 'static> Component for Pos<T> {
const STORAGE_TYPE: StorageType = StorageType::Table;
}
impl<T> Default for Pos<T> {
fn default() -> Self {
Self {
x: 0,
y: 0,
z: 0,
_marker: PhantomData,
}
}
}
impl<T> Clone for Pos<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for Pos<T> {}
impl<T> PartialEq for Pos<T> {
fn eq(&self, other: &Self) -> bool {
(self.x, self.y, self.z).eq(&(other.x, other.y, other.z))
}
}
impl<T> Eq for Pos<T> {}
impl<T> std::hash::Hash for Pos<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
(self.x, self.y, self.z).hash(state)
}
}
// Conversion
impl<T> From<(i32, i32, i32)> for Pos<T> {
fn from((x, y, z): (i32, i32, i32)) -> Self {
Self::new(x, y, z)
}
}
impl<T> From<Pos<T>> for (i32, i32, i32) {
fn from(Pos { x, y, z, .. }: Pos<T>) -> Self {
(x, y, z)
}
}
impl<T> From<[i32; 3]> for Pos<T> {
fn from([x, y, z]: [i32; 3]) -> Self {
Self::new(x, y, z)
}
}
impl<T> From<Pos<T>> for [i32; 3] {
fn from(Pos { x, y, z, .. }: Pos<T>) -> Self {
[x, y, z]
}
}
impl<T> From<IVec3> for Pos<T> {
fn from(IVec3 { x, y, z }: IVec3) -> Self {
Self::new(x, y, z)
}
}
impl<T> From<Pos<T>> for IVec3 {
fn from(Pos { x, y, z, .. }: Pos<T>) -> Self {
Self::new(x, y, z)
}
}
// Offsetting
impl<T> Add<IVec3> for Pos<T> {
type Output = Pos<T>;
fn add(self, rhs: IVec3) -> Self::Output {
Self::new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z)
}
}
impl<T> Sub<IVec3> for Pos<T> {
type Output = Pos<T>;
fn sub(self, rhs: IVec3) -> Self::Output {
Self::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
}
}
impl<T> AddAssign<IVec3> for Pos<T> {
fn add_assign(&mut self, rhs: IVec3) {
self.x += rhs.x;
self.y += rhs.y;
self.z += rhs.z;
}
}
impl<T> SubAssign<IVec3> for Pos<T> {
fn sub_assign(&mut self, rhs: IVec3) {
self.x -= rhs.x;
self.y -= rhs.y;
self.z -= rhs.z;
}
}
// Difference
impl<T> Sub<Pos<T>> for Pos<T> {
type Output = IVec3;
fn sub(self, rhs: Self) -> IVec3 {
IVec3::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
}
}
// Indexing
impl<T> Index<usize> for Pos<T> {
type Output = i32;
fn index(&self, index: usize) -> &Self::Output {
match index {
0 => &self.x,
1 => &self.y,
2 => &self.z,
_ => panic!("index out of bounds"),
}
}
}
impl<T> IndexMut<usize> for Pos<T> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
match index {
0 => &mut self.x,
1 => &mut self.y,
2 => &mut self.z,
_ => panic!("index out of bounds"),
}
}
}