use super::Vector;
use core::ops::{Add, AddAssign, Index, Sub, SubAssign};
use eunomia::{NumericElement, RealField};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "serde",
serde(bound(deserialize = "T: serde::Deserialize<'de> + Copy + Default"))
)]
#[repr(C)]
pub struct Point<T, const N: usize> {
pub coords: Vector<T, N>,
}
pub type Point1<T> = Point<T, 1>;
pub type Point2<T> = Point<T, 2>;
pub type Point3<T> = Point<T, 3>;
impl<T, const N: usize> Point<T, N> {
#[inline(always)]
pub const fn from_coords(coords: Vector<T, N>) -> Self {
Self { coords }
}
#[inline(always)]
pub const fn from_array(data: [T; N]) -> Self {
Self {
coords: Vector::from_array(data),
}
}
}
impl<T> Point<T, 1> {
#[inline(always)]
pub const fn new(x: T) -> Self {
Self {
coords: Vector::from_array([x]),
}
}
}
impl<T> Point<T, 2> {
#[inline(always)]
pub const fn new(x: T, y: T) -> Self {
Self {
coords: Vector::from_array([x, y]),
}
}
}
impl<T> Point<T, 3> {
#[inline(always)]
pub const fn new(x: T, y: T, z: T) -> Self {
Self {
coords: Vector::from_array([x, y, z]),
}
}
}
impl<T, const N: usize> From<[T; N]> for Point<T, N> {
#[inline(always)]
fn from(data: [T; N]) -> Self {
Self::from_array(data)
}
}
impl<T, const N: usize> From<Vector<T, N>> for Point<T, N> {
#[inline(always)]
fn from(coords: Vector<T, N>) -> Self {
Self { coords }
}
}
impl<T: RealField, const N: usize> Point<T, N> {
#[inline]
pub fn origin() -> Self {
Self {
coords: Vector::zeros(),
}
}
#[inline]
pub fn distance(self, other: Self) -> T {
(self - other).norm()
}
#[inline]
pub fn distance_squared(self, other: Self) -> T {
(self - other).norm_squared()
}
}
impl<T: Sub<Output = T> + Copy, const N: usize> Sub for Point<T, N> {
type Output = Vector<T, N>;
#[inline(always)]
fn sub(self, rhs: Self) -> Vector<T, N> {
self.coords - rhs.coords
}
}
impl<T: Add<Output = T> + Copy, const N: usize> Add<Vector<T, N>> for Point<T, N> {
type Output = Self;
#[inline(always)]
fn add(self, rhs: Vector<T, N>) -> Self {
Self {
coords: self.coords + rhs,
}
}
}
impl<T: Sub<Output = T> + Copy, const N: usize> Sub<Vector<T, N>> for Point<T, N> {
type Output = Self;
#[inline(always)]
fn sub(self, rhs: Vector<T, N>) -> Self {
Self {
coords: self.coords - rhs,
}
}
}
impl<T: Add<Output = T> + Copy, const N: usize> AddAssign<Vector<T, N>> for Point<T, N> {
#[inline(always)]
fn add_assign(&mut self, rhs: Vector<T, N>) {
self.coords += rhs;
}
}
impl<T: Sub<Output = T> + Copy, const N: usize> SubAssign<Vector<T, N>> for Point<T, N> {
#[inline(always)]
fn sub_assign(&mut self, rhs: Vector<T, N>) {
self.coords -= rhs;
}
}
impl<T: NumericElement, const N: usize> Default for Point<T, N> {
#[inline]
fn default() -> Self {
Self::from_coords(Vector::splat(<T as NumericElement>::ZERO))
}
}
impl<T, const N: usize> Index<usize> for Point<T, N> {
type Output = T;
#[inline(always)]
fn index(&self, i: usize) -> &T {
&self.coords.data[i]
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::geometry::Vector3;
#[test]
fn assign_ops_translate_in_place() {
let mut p = Point3::new(1.0_f64, 2.0, 3.0);
p += Vector3::from_array([10.0, 20.0, 30.0]);
assert_eq!(p.coords.data, [11.0, 22.0, 33.0]);
p -= Vector3::from_array([1.0, 2.0, 3.0]);
assert_eq!(p.coords.data, [10.0, 20.0, 30.0]);
}
#[test]
fn default_is_origin() {
assert_eq!(Point3::<f64>::default().coords.data, [0.0, 0.0, 0.0]);
}
#[test]
fn affine_algebra() {
let a = Point3::from_array([1.0_f64, 2.0, 3.0]);
let b = Point3::from_array([4.0_f64, 6.0, 3.0]);
let d = b - a;
assert_eq!(d.data, [3.0, 4.0, 0.0]);
assert_eq!(a.distance(b), 5.0);
assert_eq!((a + d), b);
assert_eq!((b - d), a);
assert_eq!(Point3::<f64>::origin().coords.data, [0.0, 0.0, 0.0]);
}
}