use std::{hash::Hash, fmt, ops};
use bytemuck::{Zeroable, Pod};
use num_traits::NumCast;
use crate::{Space, Vector, Scalar, WorldSpace, SphericalDir, Float, SphericalPos, Vec2, Vec3, Point};
#[repr(transparent)]
pub struct Dir<T: Scalar, const N: usize, S: Space>(Vector<T, N, S>);
pub type Dir2<T, S = WorldSpace> = Dir<T, 2, S>;
pub type Dir3<T, S = WorldSpace> = Dir<T, 3, S>;
pub type Dir2f<S = WorldSpace> = Dir2<f32, S>;
pub type Dir3f<S = WorldSpace> = Dir3<f32, S>;
impl<T: Scalar, const N: usize, S: Space> Dir<T, N, S> {
pub fn from_vec(v: Vector<T, N, S>) -> Self
where
T: Float,
{
v.into()
}
pub fn from_unit_vec_unchecked(v: Vector<T, N, S>) -> Self {
Self(v)
}
pub fn to_unit_vec(&self) -> Vector<T, N, S> {
self.0
}
pub fn to_point(&self) -> Point<T, N, S> {
self.0.to_point()
}
pub fn in_space<Target: Space>(self) -> Dir<T, N, Target> {
Dir(self.0.in_space())
}
pub fn to_f32(self) -> Dir<f32, N, S>
where
T: NumCast,
{
Dir(self.0.map(|s| num_traits::cast(s).unwrap()))
}
pub fn to_f64(self) -> Dir<f64, N, S>
where
T: NumCast,
{
Dir(self.0.map(|s| num_traits::cast(s).unwrap()))
}
pub fn as_bytes(&self) -> &[u8] {
bytemuck::bytes_of(self)
}
pub fn to_array(self) -> [T; N] {
self.into()
}
}
impl<T: Scalar, S: Space> Dir2<T, S> {
pub fn new(x: T, y: T) -> Self
where
T: Float,
{
Self::from_vec(Vec2::new(x, y))
}
pub const fn new_unchecked(x: T, y: T) -> Self {
Self(Vec2::new(x, y))
}
pub fn unit_x() -> Self {
Self(Vector::unit_x())
}
pub fn unit_y() -> Self {
Self(Vector::unit_y())
}
pub fn x(&self) -> T {
self.0.x
}
pub fn y(&self) -> T {
self.0.y
}
}
impl<T: Scalar, S: Space> Dir3<T, S> {
pub fn new(x: T, y: T, z: T) -> Self
where
T: Float,
{
Self::from_vec(Vec3::new(x, y, z))
}
pub const fn new_unchecked(x: T, y: T, z: T) -> Self {
Self(Vec3::new(x, y, z))
}
pub fn unit_x() -> Self {
Self(Vector::unit_x())
}
pub fn unit_y() -> Self {
Self(Vector::unit_y())
}
pub fn unit_z() -> Self {
Self(Vector::unit_z())
}
pub fn x(&self) -> T {
self.0.x
}
pub fn y(&self) -> T {
self.0.y
}
pub fn z(&self) -> T {
self.0.z
}
}
impl<T: Float, S: Space> From<SphericalDir<T, S>> for Dir3<T, S> {
fn from(src: SphericalDir<T, S>) -> Self {
Self(src.to_unit_vec())
}
}
impl<T: Float, S: Space> From<Dir3<T, S>> for SphericalDir<T, S> {
fn from(src: Dir3<T, S>) -> Self {
src.0.into()
}
}
impl<T: Float, S: Space> From<SphericalPos<T, S>> for Dir3<T, S> {
fn from(src: SphericalPos<T, S>) -> Self {
Self(src.without_radius().to_unit_vec())
}
}
impl<T: Float, const N: usize, S: Space> From<Vector<T, N, S>> for Dir<T, N, S> {
fn from(v: Vector<T, N, S>) -> Self {
let l = v.length();
assert!(!l.is_zero(), "zero vector passed to `Dir::from`");
Self(v / l)
}
}
impl<T: Float, const N: usize, S: Space> From<Dir<T, N, S>> for Vector<T, N, S> {
fn from(src: Dir<T, N, S>) -> Self {
src.0
}
}
impl<T: Scalar, S: Space> fmt::Debug for Dir3<T, S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Dir")?;
crate::util::debug_list_one_line(&self.0.0, f)
}
}
unsafe impl<T: Scalar + Zeroable, const N: usize, S: Space> Zeroable for Dir<T, N, S> {}
unsafe impl<T: Scalar + Pod, const N: usize, S: Space> Pod for Dir<T, N, S> {}
impl<T: Scalar, const N: usize, S: Space> Clone for Dir<T, N, S> {
fn clone(&self) -> Self {
Self(self.0)
}
}
impl<T: Scalar, const N: usize, S: Space> Copy for Dir<T, N, S> {}
impl<T: Scalar, const N: usize, S: Space> PartialEq for Dir<T, N, S> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T: Scalar + Hash, const N: usize, S: Space> Hash for Dir<T, N, S> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl<T: Scalar, const N: usize, S: Space> ops::Mul<T> for Dir<T, N, S> {
type Output = Vector<T, N, S>;
fn mul(self, rhs: T) -> Self::Output {
self.0 * rhs
}
}
macro_rules! impl_scalar_mul {
($($ty:ident),*) => {
$(
impl<const N: usize, S: Space> ops::Mul<Dir<$ty, N, S>> for $ty {
type Output = Vector<$ty, N, S>;
fn mul(self, rhs: Dir<$ty, N, S>) -> Self::Output {
rhs * self
}
}
)*
};
}
impl_scalar_mul!(f32, f64, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128);
impl<T: Scalar, const N: usize, S: Space> ops::Div<T> for Dir<T, N, S> {
type Output = Vector<T, N, S>;
fn div(self, rhs: T) -> Self::Output {
self.0 / rhs
}
}
impl<T: Scalar + ops::Neg, const N: usize, S: Space> ops::Neg for Dir<T, N, S>
where
<T as ops::Neg>::Output: Scalar,
{
type Output = Dir<<T as ops::Neg>::Output, N, S>;
fn neg(self) -> Self::Output {
Dir(-self.0)
}
}
impl<T: Scalar, const N: usize, S: Space> ops::Index<usize> for Dir<T, N, S> {
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
impl<T: Scalar, const N: usize, S: Space> From<Dir<T, N, S>> for [T; N] {
fn from(src: Dir<T, N, S>) -> Self {
src.0.0
}
}