use std::{array, marker::PhantomData};
use bytemuck::{Pod, Zeroable};
use crate::{Vector, Scalar, Float, WorldSpace, Space, HcPoint};
#[repr(transparent)]
pub struct Point<T: Scalar, const N: usize, S: Space = WorldSpace>(
pub(crate) [T; N],
PhantomData<S>,
);
pub type Point2<T, S = WorldSpace> = Point<T, 2, S>;
pub type Point3<T, S = WorldSpace> = Point<T, 3, S>;
pub type Point2f<S = WorldSpace> = Point2<f32, S>;
pub type Point3f<S = WorldSpace> = Point3<f32, S>;
impl<T: Scalar, const N: usize, S: Space> Point<T, N, S> {
pub fn origin() -> Self {
std::array::from_fn(|_| T::zero()).into()
}
pub fn distance2_from(self, other: Self) -> T {
(self - other).length2()
}
pub fn distance_from(self, other: Self) -> T
where
T: Float,
{
(self - other).length()
}
pub fn vec_to(self, other: Self) -> Vector<T, N, S> {
other - self
}
pub fn to_hc_point(self) -> HcPoint<T, N, S> {
self.into()
}
pub fn to_vec(self) -> Vector<T, N, S> {
self.0.into()
}
pub fn centroid(points: impl IntoIterator<Item = Self>) -> Option<Self> {
let mut it = points.into_iter();
let mut total_displacement = it.next()?.to_vec();
let mut count = T::one();
for p in it {
total_displacement += p.to_vec();
count += T::one();
}
Some((total_displacement / count).to_point())
}
shared_methods!(Point, "point", "point2", "point3");
}
impl<T: Scalar, S: Space> Point<T, 2, S> {
shared_methods2!(Point, "point");
}
impl<T: Scalar, S: Space> Point<T, 3, S> {
shared_methods3!(Point, "point");
}
shared_impls!(Point, "point", "Point");
pub const fn point2<T: Scalar>(x: T, y: T) -> Point2<T> {
Point2::new(x, y)
}
pub const fn point3<T: Scalar>(x: T, y: T, z: T) -> Point3<T> {
Point3::new(x, y, z)
}
impl<T: Scalar + std::hash::Hash, const N: usize, S: Space> std::hash::Hash for Point<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> PartialEq for Point<T, N, S> {
fn eq(&self, other: &Self) -> bool {
self.0.eq(&other.0)
}
}
impl<T: Scalar + Eq, const N: usize, S: Space> Eq for Point<T, N, S> {}
impl<T: Scalar, const N: usize, S: Space> Clone for Point<T, N, S> {
fn clone(&self) -> Self {
Self(self.0, self.1)
}
}
impl<T: Scalar, const N: usize, S: Space> Copy for Point<T, N, S> {}
unsafe impl<T: Scalar + Zeroable, const N: usize, S: Space> Zeroable for Point<T, N, S> {}
unsafe impl<T: Scalar + Pod, const N: usize, S: Space> Pod for Point<T, N, S> {}
impl<T: Scalar, const N: usize, S: Space> ops::Add<Vector<T, N, S>> for Point<T, N, S> {
type Output = Self;
fn add(self, rhs: Vector<T, N, S>) -> Self::Output {
array::from_fn(|i| self[i] + rhs[i]).into()
}
}
impl<T: Scalar, const N: usize, S: Space> ops::AddAssign<Vector<T, N, S>> for Point<T, N, S> {
fn add_assign(&mut self, rhs: Vector<T, N, S>) {
for (lhs, rhs) in IntoIterator::into_iter(&mut self.0).zip(rhs.0) {
*lhs += rhs;
}
}
}
impl<T: Scalar, const N: usize, S: Space> ops::Sub<Vector<T, N, S>> for Point<T, N, S> {
type Output = Self;
fn sub(self, rhs: Vector<T, N, S>) -> Self::Output {
array::from_fn(|i| self[i] - rhs[i]).into()
}
}
impl<T: Scalar, const N: usize, S: Space> ops::SubAssign<Vector<T, N, S>> for Point<T, N, S> {
fn sub_assign(&mut self, rhs: Vector<T, N, S>) {
for (lhs, rhs) in IntoIterator::into_iter(&mut self.0).zip(rhs.0) {
*lhs -= rhs;
}
}
}
impl<T: Scalar, const N: usize, S: Space> ops::Sub<Self> for Point<T, N, S> {
type Output = Vector<T, N, S>;
fn sub(self, rhs: Self) -> Self::Output {
array::from_fn(|i| self[i] - rhs[i]).into()
}
}