use std::{marker::PhantomData, fmt, ops};
use bytemuck::{Zeroable, Pod};
use crate::{Scalar, Space, WorldSpace, Point};
#[repr(C)]
pub struct HcPoint<T: Scalar, const N: usize, S: Space = WorldSpace> {
pub(crate) coords: [T; N],
pub weight: T,
_dummy: PhantomData<S>,
}
pub type HcPoint2<T, S = WorldSpace> = HcPoint<T, 2, S>;
pub type HcPoint3<T, S = WorldSpace> = HcPoint<T, 3, S>;
pub type HcPoint2f<S = WorldSpace> = HcPoint2<f32, S>;
pub type HcPoint3f<S = WorldSpace> = HcPoint3<f32, S>;
impl<T: Scalar, const N: usize, S: Space> HcPoint<T, N, S> {
pub fn origin() -> Self {
Self::new(std::array::from_fn(|_| T::zero()), T::one())
}
pub fn new(coords: impl Into<[T; N]>, weight: T) -> Self {
Self {
coords: coords.into(),
weight,
_dummy: PhantomData,
}
}
pub fn to_point(self) -> Point<T, N, S> {
self.coords.map(|s| s / self.weight).into()
}
pub const fn in_space<Target: Space>(self) -> HcPoint<T, N, Target> {
HcPoint {
coords: self.coords,
weight: self.weight,
_dummy: PhantomData,
}
}
pub fn to_f32(self) -> HcPoint<f32, N, S>
where
T: num_traits::NumCast,
{
HcPoint {
coords: self.coords.map(|s| num_traits::cast(s).unwrap()),
weight: num_traits::cast(self.weight).unwrap(),
_dummy: PhantomData,
}
}
pub fn to_f64(self) -> HcPoint<f64, N, S>
where
T: num_traits::NumCast,
{
HcPoint {
coords: self.coords.map(|s| num_traits::cast(s).unwrap()),
weight: num_traits::cast(self.weight).unwrap(),
_dummy: PhantomData,
}
}
pub fn as_bytes(&self) -> &[u8] {
bytemuck::bytes_of(self)
}
}
impl<T: Scalar + std::hash::Hash, const N: usize, S: Space> std::hash::Hash for HcPoint<T, N, S> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.coords.hash(state);
self.weight.hash(state);
}
}
impl<T: Scalar, const N: usize, S: Space> PartialEq for HcPoint<T, N, S> {
fn eq(&self, other: &Self) -> bool {
self.weight.eq(&other.weight) && self.coords.eq(&other.coords)
}
}
impl<T: Scalar + Eq, const N: usize, S: Space> Eq for HcPoint<T, N, S> {}
impl<T: Scalar, const N: usize, S: Space> Clone for HcPoint<T, N, S> {
fn clone(&self) -> Self {
Self {
coords: self.coords,
weight: self.weight,
_dummy: PhantomData,
}
}
}
impl<T: Scalar, const N: usize, S: Space> Copy for HcPoint<T, N, S> {}
unsafe impl<T: Scalar + Zeroable, const N: usize, S: Space> Zeroable for HcPoint<T, N, S> {}
unsafe impl<T: Scalar + Pod, const N: usize, S: Space> Pod for HcPoint<T, N, S> {}
impl<T: Scalar, const N: usize, S: Space> ops::Index<usize> for HcPoint<T, N, S> {
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
match () {
() if index < N => &self.coords[index],
() if index == N => &self.weight,
_ => panic!("index ({index}) out of bounds ({})", N + 1),
}
}
}
impl<T: Scalar, const N: usize, S: Space> ops::IndexMut<usize> for HcPoint<T, N, S> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
match () {
() if index < N => &mut self.coords[index],
() if index == N => &mut self.weight,
_ => panic!("index ({index}) out of bounds ({})", N + 1),
}
}
}
impl<T: Scalar, const N: usize, S: Space> From<Point<T, N, S>> for HcPoint<T, N, S> {
fn from(src: Point<T, N, S>) -> Self {
Self::new(src, T::one())
}
}
impl<T: Scalar, const N: usize, S: Space> From<HcPoint<T, N, S>> for Point<T, N, S> {
fn from(src: HcPoint<T, N, S>) -> Self {
src.to_point()
}
}
macro_rules! impl_np1 {
($n:expr, $np1:expr) => {
impl<T: Scalar, S: Space> HcPoint<T, $n, S> {
pub fn to_array(self) -> [T; $np1] {
self.into()
}
}
impl<T: Scalar, S: Space> From<[T; $np1]> for HcPoint<T, $n, S> {
fn from(src: [T; $np1]) -> Self {
let coords = std::array::from_fn(|i| src[i]);
Self::new(coords, src[$n])
}
}
impl<T: Scalar, S: Space> From<HcPoint<T, $n, S>> for [T; $np1] {
fn from(src: HcPoint<T, $n, S>) -> Self {
std::array::from_fn(|i| if i == $n { src.weight } else { src.coords[i] })
}
}
impl<T: Scalar, S: Space> AsRef<[T; $np1]> for HcPoint<T, $n, S> {
fn as_ref(&self) -> &[T; $np1] {
bytemuck::cast_ref(self)
}
}
impl<T: Scalar, S: Space> AsMut<[T; $np1]> for HcPoint<T, $n, S> {
fn as_mut(&mut self) -> &mut [T; $np1] {
bytemuck::cast_mut(self)
}
}
};
}
impl_np1!(2, 3);
impl<T: Scalar, const N: usize, S: Space> fmt::Debug for HcPoint<T, N, S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "HcPoint[")?;
for (i, e) in self.coords.iter().enumerate() {
if i != 0 {
write!(f, ", ")?;
}
e.fmt(f)?;
}
write!(f, "; ")?;
self.weight.fmt(f)?;
write!(f, "]")
}
}